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 >>> BitString("'0A3B5F291CD'H")
1835 BIT STRING 44 bits 0a3b5f291cd0
1836 >>> b = BitString("'010110000000'B")
1837 BIT STRING 12 bits 5800
1840 >>> b[0], b[1], b[2], b[3]
1841 (False, True, False, True)
1845 [False, True, False, True, True, False, False, False, False, False, False, False]
1849 class KeyUsage(BitString):
1851 ("digitalSignature", 0),
1852 ("nonRepudiation", 1),
1853 ("keyEncipherment", 2),
1856 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1857 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1859 ['nonRepudiation', 'keyEncipherment']
1861 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1863 __slots__ = ("tag_constructed", "specs", "defined")
1864 tag_default = tag_encode(3)
1865 asn1_type_name = "BIT STRING"
1878 :param value: set the value. Either binary type, tuple of named
1879 values (if ``schema`` is specified in the class),
1880 string in ``'XXX...'B`` form, or
1881 :py:class:`pyderasn.BitString` object
1882 :param bytes impl: override default tag with ``IMPLICIT`` one
1883 :param bytes expl: override default tag with ``EXPLICIT`` one
1884 :param default: set default value. Type same as in ``value``
1885 :param bool optional: is object ``OPTIONAL`` in sequence
1887 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1888 specs = getattr(self, "schema", {}) if _specs is None else _specs
1889 self.specs = specs if isinstance(specs, dict) else dict(specs)
1890 self._value = None if value is None else self._value_sanitize(value)
1891 if default is not None:
1892 default = self._value_sanitize(default)
1893 self.default = self.__class__(
1899 self._value = default
1901 tag_klass, _, tag_num = tag_decode(self.tag)
1902 self.tag_constructed = tag_encode(
1904 form=TagFormConstructed,
1908 def _bits2octets(self, bits):
1909 if len(self.specs) > 0:
1910 bits = bits.rstrip("0")
1912 bits += "0" * ((8 - (bit_len % 8)) % 8)
1913 octets = bytearray(len(bits) // 8)
1914 for i in six_xrange(len(octets)):
1915 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1916 return bit_len, bytes(octets)
1918 def _value_sanitize(self, value):
1919 if issubclass(value.__class__, BitString):
1921 if isinstance(value, (string_types, binary_type)):
1923 isinstance(value, string_types) and
1924 value.startswith("'")
1926 if value.endswith("'B"):
1928 if not set(value) <= set(("0", "1")):
1929 raise ValueError("B's coding contains unacceptable chars")
1930 return self._bits2octets(value)
1931 elif value.endswith("'H"):
1935 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
1938 raise InvalidValueType((self.__class__, string_types, binary_type))
1939 elif isinstance(value, binary_type):
1940 return (len(value) * 8, value)
1942 raise InvalidValueType((self.__class__, string_types, binary_type))
1943 if isinstance(value, tuple):
1946 isinstance(value[0], integer_types) and
1947 isinstance(value[1], binary_type)
1952 bit = self.specs.get(name)
1954 raise ObjUnknown("BitString value: %s" % name)
1957 return self._bits2octets("")
1959 return self._bits2octets("".join(
1960 ("1" if bit in bits else "0")
1961 for bit in six_xrange(max(bits) + 1)
1963 raise InvalidValueType((self.__class__, binary_type, string_types))
1967 return self._value is not None
1970 obj = self.__class__(_specs=self.specs)
1972 if value is not None:
1973 value = (value[0], value[1])
1976 obj._expl = self._expl
1977 obj.default = self.default
1978 obj.optional = self.optional
1979 obj.offset = self.offset
1980 obj.llen = self.llen
1981 obj.vlen = self.vlen
1985 self._assert_ready()
1986 for i in six_xrange(self._value[0]):
1991 self._assert_ready()
1992 return self._value[0]
1994 def __bytes__(self):
1995 self._assert_ready()
1996 return self._value[1]
1998 def __eq__(self, their):
1999 if isinstance(their, bytes):
2000 return self._value[1] == their
2001 if not issubclass(their.__class__, BitString):
2004 self._value == their._value and
2005 self.tag == their.tag and
2006 self._expl == their._expl
2011 return [name for name, bit in self.specs.items() if self[bit]]
2021 return self.__class__(
2023 impl=self.tag if impl is None else impl,
2024 expl=self._expl if expl is None else expl,
2025 default=self.default if default is None else default,
2026 optional=self.optional if optional is None else optional,
2030 def __getitem__(self, key):
2031 if isinstance(key, int):
2032 bit_len, octets = self._value
2036 byte2int(memoryview(octets)[key // 8:]) >>
2039 if isinstance(key, string_types):
2040 value = self.specs.get(key)
2042 raise ObjUnknown("BitString value: %s" % key)
2044 raise InvalidValueType((int, str))
2047 self._assert_ready()
2048 bit_len, octets = self._value
2051 len_encode(len(octets) + 1),
2052 int2byte((8 - bit_len % 8) % 8),
2056 def _decode_chunk(self, lv, offset, decode_path, ctx):
2058 l, llen, v = len_decode(lv)
2059 except DecodeError as err:
2060 raise err.__class__(
2062 klass=self.__class__,
2063 decode_path=decode_path,
2067 raise NotEnoughData(
2068 "encoded length is longer than data",
2069 klass=self.__class__,
2070 decode_path=decode_path,
2074 raise NotEnoughData(
2076 klass=self.__class__,
2077 decode_path=decode_path,
2080 pad_size = byte2int(v)
2081 if l == 1 and pad_size != 0:
2083 "invalid empty value",
2084 klass=self.__class__,
2085 decode_path=decode_path,
2091 klass=self.__class__,
2092 decode_path=decode_path,
2095 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2098 klass=self.__class__,
2099 decode_path=decode_path,
2102 v, tail = v[:l], v[l:]
2103 obj = self.__class__(
2104 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2107 default=self.default,
2108 optional=self.optional,
2110 _decoded=(offset, llen, l),
2114 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2116 t, tlen, lv = tag_strip(tlv)
2117 except DecodeError as err:
2118 raise err.__class__(
2120 klass=self.__class__,
2121 decode_path=decode_path,
2127 return self._decode_chunk(lv, offset, decode_path, ctx)
2128 if t == self.tag_constructed:
2129 if not ctx.get("bered", False):
2131 msg="unallowed BER constructed encoding",
2132 decode_path=decode_path,
2139 l, llen, v = len_decode(lv)
2140 except LenIndefForm:
2141 llen, l, v = 1, 0, lv[1:]
2143 except DecodeError as err:
2144 raise err.__class__(
2146 klass=self.__class__,
2147 decode_path=decode_path,
2150 if l > 0 and l > len(v):
2151 raise NotEnoughData(
2152 "encoded length is longer than data",
2153 klass=self.__class__,
2154 decode_path=decode_path,
2157 if not lenindef and l == 0:
2158 raise NotEnoughData(
2160 klass=self.__class__,
2161 decode_path=decode_path,
2165 sub_offset = offset + tlen + llen
2169 if v[:EOC_LEN].tobytes() == EOC:
2176 msg="chunk out of bounds",
2177 decode_path=len(chunks) - 1,
2178 offset=chunks[-1].offset,
2180 sub_decode_path = decode_path + (str(len(chunks)),)
2182 chunk, v_tail = BitString().decode(
2185 decode_path=sub_decode_path,
2191 msg="expected BitString encoded chunk",
2192 decode_path=sub_decode_path,
2195 chunks.append(chunk)
2196 sub_offset += chunk.tlvlen
2197 vlen += chunk.tlvlen
2199 if len(chunks) == 0:
2202 decode_path=decode_path,
2207 for chunk_i, chunk in enumerate(chunks[:-1]):
2208 if chunk.bit_len % 8 != 0:
2210 msg="BitString chunk is not multiple of 8 bit",
2211 decode_path=decode_path + (str(chunk_i),),
2212 offset=chunk.offset,
2214 values.append(bytes(chunk))
2215 bit_len += chunk.bit_len
2216 chunk_last = chunks[-1]
2217 values.append(bytes(chunk_last))
2218 bit_len += chunk_last.bit_len
2219 obj = self.__class__(
2220 value=(bit_len, b"".join(values)),
2223 default=self.default,
2224 optional=self.optional,
2226 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2228 obj.lenindef = lenindef
2230 return obj, (v[EOC_LEN:] if lenindef else v)
2232 klass=self.__class__,
2233 decode_path=decode_path,
2238 return pp_console_row(next(self.pps()))
2240 def pps(self, decode_path=()):
2244 bit_len, blob = self._value
2245 value = "%d bits" % bit_len
2246 if len(self.specs) > 0:
2247 blob = tuple(self.named)
2249 asn1_type_name=self.asn1_type_name,
2250 obj_name=self.__class__.__name__,
2251 decode_path=decode_path,
2254 optional=self.optional,
2255 default=self == self.default,
2256 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2257 expl=None if self._expl is None else tag_decode(self._expl),
2262 expl_offset=self.expl_offset if self.expled else None,
2263 expl_tlen=self.expl_tlen if self.expled else None,
2264 expl_llen=self.expl_llen if self.expled else None,
2265 expl_vlen=self.expl_vlen if self.expled else None,
2267 defined_by, defined = self.defined or (None, None)
2268 if defined_by is not None:
2270 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2274 class OctetString(Obj):
2275 """``OCTET STRING`` binary string type
2277 >>> s = OctetString(b"hello world")
2278 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2279 >>> s == OctetString(b"hello world")
2284 >>> OctetString(b"hello", bounds=(4, 4))
2285 Traceback (most recent call last):
2286 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2287 >>> OctetString(b"hell", bounds=(4, 4))
2288 OCTET STRING 4 bytes 68656c6c
2290 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2291 tag_default = tag_encode(4)
2292 asn1_type_name = "OCTET STRING"
2305 :param value: set the value. Either binary type, or
2306 :py:class:`pyderasn.OctetString` object
2307 :param bounds: set ``(MIN, MAX)`` value size constraint.
2308 (-inf, +inf) by default
2309 :param bytes impl: override default tag with ``IMPLICIT`` one
2310 :param bytes expl: override default tag with ``EXPLICIT`` one
2311 :param default: set default value. Type same as in ``value``
2312 :param bool optional: is object ``OPTIONAL`` in sequence
2314 super(OctetString, self).__init__(
2322 self._bound_min, self._bound_max = getattr(
2326 ) if bounds is None else bounds
2327 if value is not None:
2328 self._value = self._value_sanitize(value)
2329 if default is not None:
2330 default = self._value_sanitize(default)
2331 self.default = self.__class__(
2336 if self._value is None:
2337 self._value = default
2339 tag_klass, _, tag_num = tag_decode(self.tag)
2340 self.tag_constructed = tag_encode(
2342 form=TagFormConstructed,
2346 def _value_sanitize(self, value):
2347 if issubclass(value.__class__, OctetString):
2348 value = value._value
2349 elif isinstance(value, binary_type):
2352 raise InvalidValueType((self.__class__, bytes))
2353 if not self._bound_min <= len(value) <= self._bound_max:
2354 raise BoundsError(self._bound_min, len(value), self._bound_max)
2359 return self._value is not None
2362 obj = self.__class__()
2363 obj._value = self._value
2364 obj._bound_min = self._bound_min
2365 obj._bound_max = self._bound_max
2367 obj._expl = self._expl
2368 obj.default = self.default
2369 obj.optional = self.optional
2370 obj.offset = self.offset
2371 obj.llen = self.llen
2372 obj.vlen = self.vlen
2375 def __bytes__(self):
2376 self._assert_ready()
2379 def __eq__(self, their):
2380 if isinstance(their, binary_type):
2381 return self._value == their
2382 if not issubclass(their.__class__, OctetString):
2385 self._value == their._value and
2386 self.tag == their.tag and
2387 self._expl == their._expl
2390 def __lt__(self, their):
2391 return self._value < their._value
2402 return self.__class__(
2405 (self._bound_min, self._bound_max)
2406 if bounds is None else bounds
2408 impl=self.tag if impl is None else impl,
2409 expl=self._expl if expl is None else expl,
2410 default=self.default if default is None else default,
2411 optional=self.optional if optional is None else optional,
2415 self._assert_ready()
2418 len_encode(len(self._value)),
2422 def _decode_chunk(self, lv, offset, decode_path, ctx):
2424 l, llen, v = len_decode(lv)
2425 except DecodeError as err:
2426 raise err.__class__(
2428 klass=self.__class__,
2429 decode_path=decode_path,
2433 raise NotEnoughData(
2434 "encoded length is longer than data",
2435 klass=self.__class__,
2436 decode_path=decode_path,
2439 v, tail = v[:l], v[l:]
2441 obj = self.__class__(
2443 bounds=(self._bound_min, self._bound_max),
2446 default=self.default,
2447 optional=self.optional,
2448 _decoded=(offset, llen, l),
2450 except DecodeError as err:
2453 klass=self.__class__,
2454 decode_path=decode_path,
2457 except BoundsError as err:
2460 klass=self.__class__,
2461 decode_path=decode_path,
2466 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2468 t, tlen, lv = tag_strip(tlv)
2469 except DecodeError as err:
2470 raise err.__class__(
2472 klass=self.__class__,
2473 decode_path=decode_path,
2479 return self._decode_chunk(lv, offset, decode_path, ctx)
2480 if t == self.tag_constructed:
2481 if not ctx.get("bered", False):
2483 msg="unallowed BER constructed encoding",
2484 decode_path=decode_path,
2491 l, llen, v = len_decode(lv)
2492 except LenIndefForm:
2493 llen, l, v = 1, 0, lv[1:]
2495 except DecodeError as err:
2496 raise err.__class__(
2498 klass=self.__class__,
2499 decode_path=decode_path,
2502 if l > 0 and l > len(v):
2503 raise NotEnoughData(
2504 "encoded length is longer than data",
2505 klass=self.__class__,
2506 decode_path=decode_path,
2509 if not lenindef and l == 0:
2510 raise NotEnoughData(
2512 klass=self.__class__,
2513 decode_path=decode_path,
2517 sub_offset = offset + tlen + llen
2521 if v[:EOC_LEN].tobytes() == EOC:
2528 msg="chunk out of bounds",
2529 decode_path=len(chunks) - 1,
2530 offset=chunks[-1].offset,
2532 sub_decode_path = decode_path + (str(len(chunks)),)
2534 chunk, v_tail = OctetString().decode(
2537 decode_path=sub_decode_path,
2543 msg="expected OctetString encoded chunk",
2544 decode_path=sub_decode_path,
2547 chunks.append(chunk)
2548 sub_offset += chunk.tlvlen
2549 vlen += chunk.tlvlen
2551 if len(chunks) == 0:
2554 decode_path=decode_path,
2558 obj = self.__class__(
2559 value=b"".join(bytes(chunk) for chunk in chunks),
2560 bounds=(self._bound_min, self._bound_max),
2563 default=self.default,
2564 optional=self.optional,
2565 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2567 except DecodeError as err:
2570 klass=self.__class__,
2571 decode_path=decode_path,
2574 except BoundsError as err:
2577 klass=self.__class__,
2578 decode_path=decode_path,
2581 obj.lenindef = lenindef
2583 return obj, (v[EOC_LEN:] if lenindef else v)
2585 klass=self.__class__,
2586 decode_path=decode_path,
2591 return pp_console_row(next(self.pps()))
2593 def pps(self, decode_path=()):
2595 asn1_type_name=self.asn1_type_name,
2596 obj_name=self.__class__.__name__,
2597 decode_path=decode_path,
2598 value=("%d bytes" % len(self._value)) if self.ready else None,
2599 blob=self._value if self.ready else None,
2600 optional=self.optional,
2601 default=self == self.default,
2602 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2603 expl=None if self._expl is None else tag_decode(self._expl),
2608 expl_offset=self.expl_offset if self.expled else None,
2609 expl_tlen=self.expl_tlen if self.expled else None,
2610 expl_llen=self.expl_llen if self.expled else None,
2611 expl_vlen=self.expl_vlen if self.expled else None,
2613 defined_by, defined = self.defined or (None, None)
2614 if defined_by is not None:
2616 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2621 """``NULL`` null object
2629 tag_default = tag_encode(5)
2630 asn1_type_name = "NULL"
2634 value=None, # unused, but Sequence passes it
2641 :param bytes impl: override default tag with ``IMPLICIT`` one
2642 :param bytes expl: override default tag with ``EXPLICIT`` one
2643 :param bool optional: is object ``OPTIONAL`` in sequence
2645 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2653 obj = self.__class__()
2655 obj._expl = self._expl
2656 obj.default = self.default
2657 obj.optional = self.optional
2658 obj.offset = self.offset
2659 obj.llen = self.llen
2660 obj.vlen = self.vlen
2663 def __eq__(self, their):
2664 if not issubclass(their.__class__, Null):
2667 self.tag == their.tag and
2668 self._expl == their._expl
2678 return self.__class__(
2679 impl=self.tag if impl is None else impl,
2680 expl=self._expl if expl is None else expl,
2681 optional=self.optional if optional is None else optional,
2685 return self.tag + len_encode(0)
2687 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2689 t, _, lv = tag_strip(tlv)
2690 except DecodeError as err:
2691 raise err.__class__(
2693 klass=self.__class__,
2694 decode_path=decode_path,
2699 klass=self.__class__,
2700 decode_path=decode_path,
2706 l, _, v = len_decode(lv)
2707 except DecodeError as err:
2708 raise err.__class__(
2710 klass=self.__class__,
2711 decode_path=decode_path,
2715 raise InvalidLength(
2716 "Null must have zero length",
2717 klass=self.__class__,
2718 decode_path=decode_path,
2721 obj = self.__class__(
2724 optional=self.optional,
2725 _decoded=(offset, 1, 0),
2730 return pp_console_row(next(self.pps()))
2732 def pps(self, decode_path=()):
2734 asn1_type_name=self.asn1_type_name,
2735 obj_name=self.__class__.__name__,
2736 decode_path=decode_path,
2737 optional=self.optional,
2738 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2739 expl=None if self._expl is None else tag_decode(self._expl),
2744 expl_offset=self.expl_offset if self.expled else None,
2745 expl_tlen=self.expl_tlen if self.expled else None,
2746 expl_llen=self.expl_llen if self.expled else None,
2747 expl_vlen=self.expl_vlen if self.expled else None,
2751 class ObjectIdentifier(Obj):
2752 """``OBJECT IDENTIFIER`` OID type
2754 >>> oid = ObjectIdentifier((1, 2, 3))
2755 OBJECT IDENTIFIER 1.2.3
2756 >>> oid == ObjectIdentifier("1.2.3")
2762 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2763 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2765 >>> str(ObjectIdentifier((3, 1)))
2766 Traceback (most recent call last):
2767 pyderasn.InvalidOID: unacceptable first arc value
2769 __slots__ = ("defines",)
2770 tag_default = tag_encode(6)
2771 asn1_type_name = "OBJECT IDENTIFIER"
2784 :param value: set the value. Either tuples of integers,
2785 string of "."-concatenated integers, or
2786 :py:class:`pyderasn.ObjectIdentifier` object
2787 :param defines: sequence of tuples. Each tuple has two elements.
2788 First one is relative to current one decode
2789 path, aiming to the field defined by that OID.
2790 Read about relative path in
2791 :py:func:`pyderasn.abs_decode_path`. Second
2792 tuple element is ``{OID: pyderasn.Obj()}``
2793 dictionary, mapping between current OID value
2794 and structure applied to defined field.
2795 :ref:`Read about DEFINED BY <definedby>`
2796 :param bytes impl: override default tag with ``IMPLICIT`` one
2797 :param bytes expl: override default tag with ``EXPLICIT`` one
2798 :param default: set default value. Type same as in ``value``
2799 :param bool optional: is object ``OPTIONAL`` in sequence
2801 super(ObjectIdentifier, self).__init__(
2809 if value is not None:
2810 self._value = self._value_sanitize(value)
2811 if default is not None:
2812 default = self._value_sanitize(default)
2813 self.default = self.__class__(
2818 if self._value is None:
2819 self._value = default
2820 self.defines = defines
2822 def __add__(self, their):
2823 if isinstance(their, self.__class__):
2824 return self.__class__(self._value + their._value)
2825 if isinstance(their, tuple):
2826 return self.__class__(self._value + their)
2827 raise InvalidValueType((self.__class__, tuple))
2829 def _value_sanitize(self, value):
2830 if issubclass(value.__class__, ObjectIdentifier):
2832 if isinstance(value, string_types):
2834 value = tuple(int(arc) for arc in value.split("."))
2836 raise InvalidOID("unacceptable arcs values")
2837 if isinstance(value, tuple):
2839 raise InvalidOID("less than 2 arcs")
2840 first_arc = value[0]
2841 if first_arc in (0, 1):
2842 if not (0 <= value[1] <= 39):
2843 raise InvalidOID("second arc is too wide")
2844 elif first_arc == 2:
2847 raise InvalidOID("unacceptable first arc value")
2849 raise InvalidValueType((self.__class__, str, tuple))
2853 return self._value is not None
2856 obj = self.__class__()
2857 obj._value = self._value
2858 obj.defines = self.defines
2860 obj._expl = self._expl
2861 obj.default = self.default
2862 obj.optional = self.optional
2863 obj.offset = self.offset
2864 obj.llen = self.llen
2865 obj.vlen = self.vlen
2869 self._assert_ready()
2870 return iter(self._value)
2873 return ".".join(str(arc) for arc in self._value or ())
2876 self._assert_ready()
2879 bytes(self._expl or b"") +
2880 str(self._value).encode("ascii"),
2883 def __eq__(self, their):
2884 if isinstance(their, tuple):
2885 return self._value == their
2886 if not issubclass(their.__class__, ObjectIdentifier):
2889 self.tag == their.tag and
2890 self._expl == their._expl and
2891 self._value == their._value
2894 def __lt__(self, their):
2895 return self._value < their._value
2906 return self.__class__(
2908 defines=self.defines if defines is None else defines,
2909 impl=self.tag if impl is None else impl,
2910 expl=self._expl if expl is None else expl,
2911 default=self.default if default is None else default,
2912 optional=self.optional if optional is None else optional,
2916 self._assert_ready()
2918 first_value = value[1]
2919 first_arc = value[0]
2922 elif first_arc == 1:
2924 elif first_arc == 2:
2926 else: # pragma: no cover
2927 raise RuntimeError("invalid arc is stored")
2928 octets = [zero_ended_encode(first_value)]
2929 for arc in value[2:]:
2930 octets.append(zero_ended_encode(arc))
2931 v = b"".join(octets)
2932 return b"".join((self.tag, len_encode(len(v)), v))
2934 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2936 t, _, lv = tag_strip(tlv)
2937 except DecodeError as err:
2938 raise err.__class__(
2940 klass=self.__class__,
2941 decode_path=decode_path,
2946 klass=self.__class__,
2947 decode_path=decode_path,
2953 l, llen, v = len_decode(lv)
2954 except DecodeError as err:
2955 raise err.__class__(
2957 klass=self.__class__,
2958 decode_path=decode_path,
2962 raise NotEnoughData(
2963 "encoded length is longer than data",
2964 klass=self.__class__,
2965 decode_path=decode_path,
2969 raise NotEnoughData(
2971 klass=self.__class__,
2972 decode_path=decode_path,
2975 v, tail = v[:l], v[l:]
2981 octet = indexbytes(v, i)
2982 arc = (arc << 7) | (octet & 0x7F)
2983 if octet & 0x80 == 0:
2991 klass=self.__class__,
2992 decode_path=decode_path,
2996 second_arc = arcs[0]
2997 if 0 <= second_arc <= 39:
2999 elif 40 <= second_arc <= 79:
3005 obj = self.__class__(
3006 value=tuple([first_arc, second_arc] + arcs[1:]),
3009 default=self.default,
3010 optional=self.optional,
3011 _decoded=(offset, llen, l),
3016 return pp_console_row(next(self.pps()))
3018 def pps(self, decode_path=()):
3020 asn1_type_name=self.asn1_type_name,
3021 obj_name=self.__class__.__name__,
3022 decode_path=decode_path,
3023 value=str(self) if self.ready else None,
3024 optional=self.optional,
3025 default=self == self.default,
3026 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3027 expl=None if self._expl is None else tag_decode(self._expl),
3032 expl_offset=self.expl_offset if self.expled else None,
3033 expl_tlen=self.expl_tlen if self.expled else None,
3034 expl_llen=self.expl_llen if self.expled else None,
3035 expl_vlen=self.expl_vlen if self.expled else None,
3039 class Enumerated(Integer):
3040 """``ENUMERATED`` integer type
3042 This type is identical to :py:class:`pyderasn.Integer`, but requires
3043 schema to be specified and does not accept values missing from it.
3046 tag_default = tag_encode(10)
3047 asn1_type_name = "ENUMERATED"
3058 bounds=None, # dummy argument, workability for Integer.decode
3060 super(Enumerated, self).__init__(
3069 if len(self.specs) == 0:
3070 raise ValueError("schema must be specified")
3072 def _value_sanitize(self, value):
3073 if isinstance(value, self.__class__):
3074 value = value._value
3075 elif isinstance(value, integer_types):
3076 if value not in list(self.specs.values()):
3078 "unknown integer value: %s" % value,
3079 klass=self.__class__,
3081 elif isinstance(value, string_types):
3082 value = self.specs.get(value)
3084 raise ObjUnknown("integer value: %s" % value)
3086 raise InvalidValueType((self.__class__, int, str))
3090 obj = self.__class__(_specs=self.specs)
3091 obj._value = self._value
3092 obj._bound_min = self._bound_min
3093 obj._bound_max = self._bound_max
3095 obj._expl = self._expl
3096 obj.default = self.default
3097 obj.optional = self.optional
3098 obj.offset = self.offset
3099 obj.llen = self.llen
3100 obj.vlen = self.vlen
3112 return self.__class__(
3114 impl=self.tag if impl is None else impl,
3115 expl=self._expl if expl is None else expl,
3116 default=self.default if default is None else default,
3117 optional=self.optional if optional is None else optional,
3122 class CommonString(OctetString):
3123 """Common class for all strings
3125 Everything resembles :py:class:`pyderasn.OctetString`, except
3126 ability to deal with unicode text strings.
3128 >>> hexenc("привет мир".encode("utf-8"))
3129 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3130 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3132 >>> s = UTF8String("привет мир")
3133 UTF8String UTF8String привет мир
3135 'привет мир'
3136 >>> hexenc(bytes(s))
3137 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3139 >>> PrintableString("привет мир")
3140 Traceback (most recent call last):
3141 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3143 >>> BMPString("ада", bounds=(2, 2))
3144 Traceback (most recent call last):
3145 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3146 >>> s = BMPString("ад", bounds=(2, 2))
3149 >>> hexenc(bytes(s))
3157 * - :py:class:`pyderasn.UTF8String`
3159 * - :py:class:`pyderasn.NumericString`
3161 * - :py:class:`pyderasn.PrintableString`
3163 * - :py:class:`pyderasn.TeletexString`
3165 * - :py:class:`pyderasn.T61String`
3167 * - :py:class:`pyderasn.VideotexString`
3169 * - :py:class:`pyderasn.IA5String`
3171 * - :py:class:`pyderasn.GraphicString`
3173 * - :py:class:`pyderasn.VisibleString`
3175 * - :py:class:`pyderasn.ISO646String`
3177 * - :py:class:`pyderasn.GeneralString`
3179 * - :py:class:`pyderasn.UniversalString`
3181 * - :py:class:`pyderasn.BMPString`
3184 __slots__ = ("encoding",)
3186 def _value_sanitize(self, value):
3188 value_decoded = None
3189 if isinstance(value, self.__class__):
3190 value_raw = value._value
3191 elif isinstance(value, text_type):
3192 value_decoded = value
3193 elif isinstance(value, binary_type):
3196 raise InvalidValueType((self.__class__, text_type, binary_type))
3199 value_decoded.encode(self.encoding)
3200 if value_raw is None else value_raw
3203 value_raw.decode(self.encoding)
3204 if value_decoded is None else value_decoded
3206 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3207 raise DecodeError(str(err))
3208 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3216 def __eq__(self, their):
3217 if isinstance(their, binary_type):
3218 return self._value == their
3219 if isinstance(their, text_type):
3220 return self._value == their.encode(self.encoding)
3221 if not isinstance(their, self.__class__):
3224 self._value == their._value and
3225 self.tag == their.tag and
3226 self._expl == their._expl
3229 def __unicode__(self):
3231 return self._value.decode(self.encoding)
3232 return text_type(self._value)
3235 return pp_console_row(next(self.pps(no_unicode=PY2)))
3237 def pps(self, decode_path=(), no_unicode=False):
3240 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3242 asn1_type_name=self.asn1_type_name,
3243 obj_name=self.__class__.__name__,
3244 decode_path=decode_path,
3246 optional=self.optional,
3247 default=self == self.default,
3248 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3249 expl=None if self._expl is None else tag_decode(self._expl),
3254 expl_offset=self.expl_offset if self.expled else None,
3255 expl_tlen=self.expl_tlen if self.expled else None,
3256 expl_llen=self.expl_llen if self.expled else None,
3257 expl_vlen=self.expl_vlen if self.expled else None,
3261 class UTF8String(CommonString):
3263 tag_default = tag_encode(12)
3265 asn1_type_name = "UTF8String"
3268 class NumericString(CommonString):
3270 tag_default = tag_encode(18)
3272 asn1_type_name = "NumericString"
3273 allowable_chars = set(digits.encode("ascii"))
3275 def _value_sanitize(self, value):
3276 value = super(NumericString, self)._value_sanitize(value)
3277 if not set(value) <= self.allowable_chars:
3278 raise DecodeError("non-numeric value")
3282 class PrintableString(CommonString):
3284 tag_default = tag_encode(19)
3286 asn1_type_name = "PrintableString"
3289 class TeletexString(CommonString):
3291 tag_default = tag_encode(20)
3293 asn1_type_name = "TeletexString"
3296 class T61String(TeletexString):
3298 asn1_type_name = "T61String"
3301 class VideotexString(CommonString):
3303 tag_default = tag_encode(21)
3304 encoding = "iso-8859-1"
3305 asn1_type_name = "VideotexString"
3308 class IA5String(CommonString):
3310 tag_default = tag_encode(22)
3312 asn1_type_name = "IA5"
3315 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3316 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3317 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3320 class UTCTime(CommonString):
3321 """``UTCTime`` datetime type
3323 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3324 UTCTime UTCTime 2017-09-30T22:07:50
3330 datetime.datetime(2017, 9, 30, 22, 7, 50)
3331 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3332 datetime.datetime(1957, 9, 30, 22, 7, 50)
3335 tag_default = tag_encode(23)
3337 asn1_type_name = "UTCTime"
3339 fmt = "%y%m%d%H%M%SZ"
3349 bounds=None, # dummy argument, workability for OctetString.decode
3352 :param value: set the value. Either datetime type, or
3353 :py:class:`pyderasn.UTCTime` object
3354 :param bytes impl: override default tag with ``IMPLICIT`` one
3355 :param bytes expl: override default tag with ``EXPLICIT`` one
3356 :param default: set default value. Type same as in ``value``
3357 :param bool optional: is object ``OPTIONAL`` in sequence
3359 super(UTCTime, self).__init__(
3367 if value is not None:
3368 self._value = self._value_sanitize(value)
3369 if default is not None:
3370 default = self._value_sanitize(default)
3371 self.default = self.__class__(
3376 if self._value is None:
3377 self._value = default
3379 def _value_sanitize(self, value):
3380 if isinstance(value, self.__class__):
3382 if isinstance(value, datetime):
3383 return value.strftime(self.fmt).encode("ascii")
3384 if isinstance(value, binary_type):
3385 value_decoded = value.decode("ascii")
3386 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3388 datetime.strptime(value_decoded, self.fmt)
3390 raise DecodeError("invalid UTCTime format")
3393 raise DecodeError("invalid UTCTime length")
3394 raise InvalidValueType((self.__class__, datetime))
3396 def __eq__(self, their):
3397 if isinstance(their, binary_type):
3398 return self._value == their
3399 if isinstance(their, datetime):
3400 return self.todatetime() == their
3401 if not isinstance(their, self.__class__):
3404 self._value == their._value and
3405 self.tag == their.tag and
3406 self._expl == their._expl
3409 def todatetime(self):
3410 """Convert to datetime
3414 Pay attention that UTCTime can not hold full year, so all years
3415 having < 50 years are treated as 20xx, 19xx otherwise, according
3416 to X.509 recomendation.
3418 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3419 year = value.year % 100
3421 year=(2000 + year) if year < 50 else (1900 + year),
3425 minute=value.minute,
3426 second=value.second,
3430 return pp_console_row(next(self.pps()))
3432 def pps(self, decode_path=()):
3434 asn1_type_name=self.asn1_type_name,
3435 obj_name=self.__class__.__name__,
3436 decode_path=decode_path,
3437 value=self.todatetime().isoformat() if self.ready else None,
3438 optional=self.optional,
3439 default=self == self.default,
3440 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3441 expl=None if self._expl is None else tag_decode(self._expl),
3446 expl_offset=self.expl_offset if self.expled else None,
3447 expl_tlen=self.expl_tlen if self.expled else None,
3448 expl_llen=self.expl_llen if self.expled else None,
3449 expl_vlen=self.expl_vlen if self.expled else None,
3453 class GeneralizedTime(UTCTime):
3454 """``GeneralizedTime`` datetime type
3456 This type is similar to :py:class:`pyderasn.UTCTime`.
3458 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3459 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3461 '20170930220750.000123Z'
3462 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3463 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3466 tag_default = tag_encode(24)
3467 asn1_type_name = "GeneralizedTime"
3469 fmt = "%Y%m%d%H%M%SZ"
3470 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3472 def _value_sanitize(self, value):
3473 if isinstance(value, self.__class__):
3475 if isinstance(value, datetime):
3476 return value.strftime(
3477 self.fmt_ms if value.microsecond > 0 else self.fmt
3479 if isinstance(value, binary_type):
3480 value_decoded = value.decode("ascii")
3481 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3483 datetime.strptime(value_decoded, self.fmt)
3486 "invalid GeneralizedTime (without ms) format",
3489 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3491 datetime.strptime(value_decoded, self.fmt_ms)
3494 "invalid GeneralizedTime (with ms) format",
3499 "invalid GeneralizedTime length",
3500 klass=self.__class__,
3502 raise InvalidValueType((self.__class__, datetime))
3504 def todatetime(self):
3505 value = self._value.decode("ascii")
3506 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3507 return datetime.strptime(value, self.fmt)
3508 return datetime.strptime(value, self.fmt_ms)
3511 class GraphicString(CommonString):
3513 tag_default = tag_encode(25)
3514 encoding = "iso-8859-1"
3515 asn1_type_name = "GraphicString"
3518 class VisibleString(CommonString):
3520 tag_default = tag_encode(26)
3522 asn1_type_name = "VisibleString"
3525 class ISO646String(VisibleString):
3527 asn1_type_name = "ISO646String"
3530 class GeneralString(CommonString):
3532 tag_default = tag_encode(27)
3533 encoding = "iso-8859-1"
3534 asn1_type_name = "GeneralString"
3537 class UniversalString(CommonString):
3539 tag_default = tag_encode(28)
3540 encoding = "utf-32-be"
3541 asn1_type_name = "UniversalString"
3544 class BMPString(CommonString):
3546 tag_default = tag_encode(30)
3547 encoding = "utf-16-be"
3548 asn1_type_name = "BMPString"
3552 """``CHOICE`` special type
3556 class GeneralName(Choice):
3558 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3559 ("dNSName", IA5String(impl=tag_ctxp(2))),
3562 >>> gn = GeneralName()
3564 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3565 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3566 >>> gn["dNSName"] = IA5String("bar.baz")
3567 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3568 >>> gn["rfc822Name"]
3571 [2] IA5String IA5 bar.baz
3574 >>> gn.value == gn["dNSName"]
3577 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3579 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3580 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3582 __slots__ = ("specs",)
3584 asn1_type_name = "CHOICE"
3597 :param value: set the value. Either ``(choice, value)`` tuple, or
3598 :py:class:`pyderasn.Choice` object
3599 :param bytes impl: can not be set, do **not** use it
3600 :param bytes expl: override default tag with ``EXPLICIT`` one
3601 :param default: set default value. Type same as in ``value``
3602 :param bool optional: is object ``OPTIONAL`` in sequence
3604 if impl is not None:
3605 raise ValueError("no implicit tag allowed for CHOICE")
3606 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3608 schema = getattr(self, "schema", ())
3609 if len(schema) == 0:
3610 raise ValueError("schema must be specified")
3612 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3615 if value is not None:
3616 self._value = self._value_sanitize(value)
3617 if default is not None:
3618 default_value = self._value_sanitize(default)
3619 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3620 default_obj.specs = self.specs
3621 default_obj._value = default_value
3622 self.default = default_obj
3624 self._value = default_obj.copy()._value
3626 def _value_sanitize(self, value):
3627 if isinstance(value, self.__class__):
3629 if isinstance(value, tuple) and len(value) == 2:
3631 spec = self.specs.get(choice)
3633 raise ObjUnknown(choice)
3634 if not isinstance(obj, spec.__class__):
3635 raise InvalidValueType((spec,))
3636 return (choice, spec(obj))
3637 raise InvalidValueType((self.__class__, tuple))
3641 return self._value is not None and self._value[1].ready
3644 obj = self.__class__(schema=self.specs)
3645 obj._expl = self._expl
3646 obj.default = self.default
3647 obj.optional = self.optional
3648 obj.offset = self.offset
3649 obj.llen = self.llen
3650 obj.vlen = self.vlen
3652 if value is not None:
3653 obj._value = (value[0], value[1].copy())
3656 def __eq__(self, their):
3657 if isinstance(their, tuple) and len(their) == 2:
3658 return self._value == their
3659 if not isinstance(their, self.__class__):
3662 self.specs == their.specs and
3663 self._value == their._value
3673 return self.__class__(
3676 expl=self._expl if expl is None else expl,
3677 default=self.default if default is None else default,
3678 optional=self.optional if optional is None else optional,
3683 self._assert_ready()
3684 return self._value[0]
3688 self._assert_ready()
3689 return self._value[1]
3691 def __getitem__(self, key):
3692 if key not in self.specs:
3693 raise ObjUnknown(key)
3694 if self._value is None:
3696 choice, value = self._value
3701 def __setitem__(self, key, value):
3702 spec = self.specs.get(key)
3704 raise ObjUnknown(key)
3705 if not isinstance(value, spec.__class__):
3706 raise InvalidValueType((spec.__class__,))
3707 self._value = (key, spec(value))
3715 return self._value[1].decoded if self.ready else False
3718 self._assert_ready()
3719 return self._value[1].encode()
3721 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3722 for choice, spec in self.specs.items():
3723 sub_decode_path = decode_path + (choice,)
3729 decode_path=sub_decode_path,
3738 klass=self.__class__,
3739 decode_path=decode_path,
3744 value, tail = spec.decode(
3748 decode_path=sub_decode_path,
3751 obj = self.__class__(
3754 default=self.default,
3755 optional=self.optional,
3756 _decoded=(offset, 0, value.tlvlen),
3758 obj._value = (choice, value)
3762 value = pp_console_row(next(self.pps()))
3764 value = "%s[%r]" % (value, self.value)
3767 def pps(self, decode_path=()):
3769 asn1_type_name=self.asn1_type_name,
3770 obj_name=self.__class__.__name__,
3771 decode_path=decode_path,
3772 value=self.choice if self.ready else None,
3773 optional=self.optional,
3774 default=self == self.default,
3775 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3776 expl=None if self._expl is None else tag_decode(self._expl),
3783 yield self.value.pps(decode_path=decode_path + (self.choice,))
3786 class PrimitiveTypes(Choice):
3787 """Predefined ``CHOICE`` for all generic primitive types
3789 It could be useful for general decoding of some unspecified values:
3791 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3792 OCTET STRING 3 bytes 666f6f
3793 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3797 schema = tuple((klass.__name__, klass()) for klass in (
3822 """``ANY`` special type
3824 >>> Any(Integer(-123))
3826 >>> a = Any(OctetString(b"hello world").encode())
3827 ANY 040b68656c6c6f20776f726c64
3828 >>> hexenc(bytes(a))
3829 b'0x040x0bhello world'
3831 __slots__ = ("defined",)
3832 tag_default = tag_encode(0)
3833 asn1_type_name = "ANY"
3843 :param value: set the value. Either any kind of pyderasn's
3844 **ready** object, or bytes. Pay attention that
3845 **no** validation is performed is raw binary value
3847 :param bytes expl: override default tag with ``EXPLICIT`` one
3848 :param bool optional: is object ``OPTIONAL`` in sequence
3850 super(Any, self).__init__(None, expl, None, optional, _decoded)
3851 self._value = None if value is None else self._value_sanitize(value)
3854 def _value_sanitize(self, value):
3855 if isinstance(value, self.__class__):
3857 if isinstance(value, Obj):
3858 return value.encode()
3859 if isinstance(value, binary_type):
3861 raise InvalidValueType((self.__class__, Obj, binary_type))
3865 return self._value is not None
3868 obj = self.__class__()
3869 obj._value = self._value
3871 obj._expl = self._expl
3872 obj.optional = self.optional
3873 obj.offset = self.offset
3874 obj.llen = self.llen
3875 obj.vlen = self.vlen
3878 def __eq__(self, their):
3879 if isinstance(their, binary_type):
3880 return self._value == their
3881 if issubclass(their.__class__, Any):
3882 return self._value == their._value
3891 return self.__class__(
3893 expl=self._expl if expl is None else expl,
3894 optional=self.optional if optional is None else optional,
3897 def __bytes__(self):
3898 self._assert_ready()
3906 self._assert_ready()
3909 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3911 t, tlen, lv = tag_strip(tlv)
3912 except DecodeError as err:
3913 raise err.__class__(
3915 klass=self.__class__,
3916 decode_path=decode_path,
3920 l, llen, v = len_decode(lv)
3921 except LenIndefForm as err:
3922 if not ctx.get("bered", False):
3923 raise err.__class__(
3925 klass=self.__class__,
3926 decode_path=decode_path,
3929 llen, vlen, v = 1, 0, lv[1:]
3930 sub_offset = offset + tlen + llen
3933 if v[:EOC_LEN].tobytes() == EOC:
3934 tlvlen = tlen + llen + vlen + EOC_LEN
3935 obj = self.__class__(
3936 value=tlv[:tlvlen].tobytes(),
3938 optional=self.optional,
3939 _decoded=(offset, 0, tlvlen),
3943 return obj, v[EOC_LEN:]
3945 chunk, v = Any().decode(
3948 decode_path=decode_path + (str(chunk_i),),
3952 vlen += chunk.tlvlen
3953 sub_offset += chunk.tlvlen
3955 except DecodeError as err:
3956 raise err.__class__(
3958 klass=self.__class__,
3959 decode_path=decode_path,
3963 raise NotEnoughData(
3964 "encoded length is longer than data",
3965 klass=self.__class__,
3966 decode_path=decode_path,
3969 tlvlen = tlen + llen + l
3970 v, tail = tlv[:tlvlen], v[l:]
3971 obj = self.__class__(
3974 optional=self.optional,
3975 _decoded=(offset, 0, tlvlen),
3981 return pp_console_row(next(self.pps()))
3983 def pps(self, decode_path=()):
3985 asn1_type_name=self.asn1_type_name,
3986 obj_name=self.__class__.__name__,
3987 decode_path=decode_path,
3988 blob=self._value if self.ready else None,
3989 optional=self.optional,
3990 default=self == self.default,
3991 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3992 expl=None if self._expl is None else tag_decode(self._expl),
3997 expl_offset=self.expl_offset if self.expled else None,
3998 expl_tlen=self.expl_tlen if self.expled else None,
3999 expl_llen=self.expl_llen if self.expled else None,
4000 expl_vlen=self.expl_vlen if self.expled else None,
4002 defined_by, defined = self.defined or (None, None)
4003 if defined_by is not None:
4005 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4009 ########################################################################
4010 # ASN.1 constructed types
4011 ########################################################################
4013 def get_def_by_path(defines_by_path, sub_decode_path):
4014 """Get define by decode path
4016 for path, define in defines_by_path:
4017 if len(path) != len(sub_decode_path):
4019 for p1, p2 in zip(path, sub_decode_path):
4020 if (p1 != any) and (p1 != p2):
4026 def abs_decode_path(decode_path, rel_path):
4027 """Create an absolute decode path from current and relative ones
4029 :param decode_path: current decode path, starting point.
4031 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4032 If first tuple's element is "/", then treat it as
4033 an absolute path, ignoring ``decode_path`` as
4034 starting point. Also this tuple can contain ".."
4035 elements, stripping the leading element from
4038 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4039 ("foo", "bar", "baz", "whatever")
4040 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4042 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4045 if rel_path[0] == "/":
4047 if rel_path[0] == "..":
4048 return abs_decode_path(decode_path[:-1], rel_path[1:])
4049 return decode_path + rel_path
4052 class Sequence(Obj):
4053 """``SEQUENCE`` structure type
4055 You have to make specification of sequence::
4057 class Extension(Sequence):
4059 ("extnID", ObjectIdentifier()),
4060 ("critical", Boolean(default=False)),
4061 ("extnValue", OctetString()),
4064 Then, you can work with it as with dictionary.
4066 >>> ext = Extension()
4067 >>> Extension().specs
4069 ('extnID', OBJECT IDENTIFIER),
4070 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4071 ('extnValue', OCTET STRING),
4073 >>> ext["extnID"] = "1.2.3"
4074 Traceback (most recent call last):
4075 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4076 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4078 You can determine if sequence is ready to be encoded:
4083 Traceback (most recent call last):
4084 pyderasn.ObjNotReady: object is not ready: extnValue
4085 >>> ext["extnValue"] = OctetString(b"foobar")
4089 Value you want to assign, must have the same **type** as in
4090 corresponding specification, but it can have different tags,
4091 optional/default attributes -- they will be taken from specification
4094 class TBSCertificate(Sequence):
4096 ("version", Version(expl=tag_ctxc(0), default="v1")),
4099 >>> tbs = TBSCertificate()
4100 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4102 Assign ``None`` to remove value from sequence.
4104 You can set values in Sequence during its initialization:
4106 >>> AlgorithmIdentifier((
4107 ("algorithm", ObjectIdentifier("1.2.3")),
4108 ("parameters", Any(Null()))
4110 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4112 You can determine if value exists/set in the sequence and take its value:
4114 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4117 OBJECT IDENTIFIER 1.2.3
4119 But pay attention that if value has default, then it won't be (not
4120 in) in the sequence (because ``DEFAULT`` must not be encoded in
4121 DER), but you can read its value:
4123 >>> "critical" in ext, ext["critical"]
4124 (False, BOOLEAN False)
4125 >>> ext["critical"] = Boolean(True)
4126 >>> "critical" in ext, ext["critical"]
4127 (True, BOOLEAN True)
4129 All defaulted values are always optional.
4131 .. _strict_default_existence_ctx:
4135 When decoded DER contains defaulted value inside, then
4136 technically this is not valid DER encoding. But we allow and pass
4137 it **by default**. Of course reencoding of that kind of DER will
4138 result in different binary representation (validly without
4139 defaulted value inside). You can enable strict defaulted values
4140 existence validation by setting ``"strict_default_existence":
4141 True`` :ref:`context <ctx>` option -- decoding process will raise
4142 an exception if defaulted value is met.
4144 Two sequences are equal if they have equal specification (schema),
4145 implicit/explicit tagging and the same values.
4147 __slots__ = ("specs",)
4148 tag_default = tag_encode(form=TagFormConstructed, num=16)
4149 asn1_type_name = "SEQUENCE"
4161 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4163 schema = getattr(self, "schema", ())
4165 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4168 if value is not None:
4169 if issubclass(value.__class__, Sequence):
4170 self._value = value._value
4171 elif hasattr(value, "__iter__"):
4172 for seq_key, seq_value in value:
4173 self[seq_key] = seq_value
4175 raise InvalidValueType((Sequence,))
4176 if default is not None:
4177 if not issubclass(default.__class__, Sequence):
4178 raise InvalidValueType((Sequence,))
4179 default_value = default._value
4180 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4181 default_obj.specs = self.specs
4182 default_obj._value = default_value
4183 self.default = default_obj
4185 self._value = default_obj.copy()._value
4189 for name, spec in self.specs.items():
4190 value = self._value.get(name)
4201 obj = self.__class__(schema=self.specs)
4203 obj._expl = self._expl
4204 obj.default = self.default
4205 obj.optional = self.optional
4206 obj.offset = self.offset
4207 obj.llen = self.llen
4208 obj.vlen = self.vlen
4209 obj._value = {k: v.copy() for k, v in self._value.items()}
4212 def __eq__(self, their):
4213 if not isinstance(their, self.__class__):
4216 self.specs == their.specs and
4217 self.tag == their.tag and
4218 self._expl == their._expl and
4219 self._value == their._value
4230 return self.__class__(
4233 impl=self.tag if impl is None else impl,
4234 expl=self._expl if expl is None else expl,
4235 default=self.default if default is None else default,
4236 optional=self.optional if optional is None else optional,
4239 def __contains__(self, key):
4240 return key in self._value
4242 def __setitem__(self, key, value):
4243 spec = self.specs.get(key)
4245 raise ObjUnknown(key)
4247 self._value.pop(key, None)
4249 if not isinstance(value, spec.__class__):
4250 raise InvalidValueType((spec.__class__,))
4251 value = spec(value=value)
4252 if spec.default is not None and value == spec.default:
4253 self._value.pop(key, None)
4255 self._value[key] = value
4257 def __getitem__(self, key):
4258 value = self._value.get(key)
4259 if value is not None:
4261 spec = self.specs.get(key)
4263 raise ObjUnknown(key)
4264 if spec.default is not None:
4268 def _encoded_values(self):
4270 for name, spec in self.specs.items():
4271 value = self._value.get(name)
4275 raise ObjNotReady(name)
4276 raws.append(value.encode())
4280 v = b"".join(self._encoded_values())
4281 return b"".join((self.tag, len_encode(len(v)), v))
4283 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4285 t, tlen, lv = tag_strip(tlv)
4286 except DecodeError as err:
4287 raise err.__class__(
4289 klass=self.__class__,
4290 decode_path=decode_path,
4295 klass=self.__class__,
4296 decode_path=decode_path,
4303 l, llen, v = len_decode(lv)
4304 except LenIndefForm as err:
4305 if not ctx.get("bered", False):
4306 raise err.__class__(
4308 klass=self.__class__,
4309 decode_path=decode_path,
4312 l, llen, v = 0, 1, lv[1:]
4314 except DecodeError as err:
4315 raise err.__class__(
4317 klass=self.__class__,
4318 decode_path=decode_path,
4322 raise NotEnoughData(
4323 "encoded length is longer than data",
4324 klass=self.__class__,
4325 decode_path=decode_path,
4329 v, tail = v[:l], v[l:]
4331 sub_offset = offset + tlen + llen
4333 for name, spec in self.specs.items():
4334 if spec.optional and (
4335 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4339 sub_decode_path = decode_path + (name,)
4341 value, v_tail = spec.decode(
4345 decode_path=sub_decode_path,
4353 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4354 if defined is not None:
4355 defined_by, defined_spec = defined
4356 if issubclass(value.__class__, SequenceOf):
4357 for i, _value in enumerate(value):
4358 sub_sub_decode_path = sub_decode_path + (
4360 DecodePathDefBy(defined_by),
4362 defined_value, defined_tail = defined_spec.decode(
4363 memoryview(bytes(_value)),
4365 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4366 if value.expled else (value.tlen + value.llen)
4369 decode_path=sub_sub_decode_path,
4372 if len(defined_tail) > 0:
4375 klass=self.__class__,
4376 decode_path=sub_sub_decode_path,
4379 _value.defined = (defined_by, defined_value)
4381 defined_value, defined_tail = defined_spec.decode(
4382 memoryview(bytes(value)),
4384 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4385 if value.expled else (value.tlen + value.llen)
4388 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4391 if len(defined_tail) > 0:
4394 klass=self.__class__,
4395 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4398 value.defined = (defined_by, defined_value)
4400 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4402 sub_offset += value_len
4404 if spec.default is not None and value == spec.default:
4405 if ctx.get("strict_default_existence", False):
4407 "DEFAULT value met",
4408 klass=self.__class__,
4409 decode_path=sub_decode_path,
4414 values[name] = value
4416 spec_defines = getattr(spec, "defines", ())
4417 if len(spec_defines) == 0:
4418 defines_by_path = ctx.get("defines_by_path", ())
4419 if len(defines_by_path) > 0:
4420 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4421 if spec_defines is not None and len(spec_defines) > 0:
4422 for rel_path, schema in spec_defines:
4423 defined = schema.get(value, None)
4424 if defined is not None:
4425 ctx.setdefault("defines", []).append((
4426 abs_decode_path(sub_decode_path[:-1], rel_path),
4430 if v[:EOC_LEN].tobytes() != EOC:
4433 klass=self.__class__,
4434 decode_path=decode_path,
4442 klass=self.__class__,
4443 decode_path=decode_path,
4446 obj = self.__class__(
4450 default=self.default,
4451 optional=self.optional,
4452 _decoded=(offset, llen, vlen),
4455 obj.lenindef = lenindef
4459 value = pp_console_row(next(self.pps()))
4461 for name in self.specs:
4462 _value = self._value.get(name)
4465 cols.append(repr(_value))
4466 return "%s[%s]" % (value, ", ".join(cols))
4468 def pps(self, decode_path=()):
4470 asn1_type_name=self.asn1_type_name,
4471 obj_name=self.__class__.__name__,
4472 decode_path=decode_path,
4473 optional=self.optional,
4474 default=self == self.default,
4475 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4476 expl=None if self._expl is None else tag_decode(self._expl),
4481 expl_offset=self.expl_offset if self.expled else None,
4482 expl_tlen=self.expl_tlen if self.expled else None,
4483 expl_llen=self.expl_llen if self.expled else None,
4484 expl_vlen=self.expl_vlen if self.expled else None,
4486 for name in self.specs:
4487 value = self._value.get(name)
4490 yield value.pps(decode_path=decode_path + (name,))
4493 class Set(Sequence):
4494 """``SET`` structure type
4496 Its usage is identical to :py:class:`pyderasn.Sequence`.
4499 tag_default = tag_encode(form=TagFormConstructed, num=17)
4500 asn1_type_name = "SET"
4503 raws = self._encoded_values()
4506 return b"".join((self.tag, len_encode(len(v)), v))
4508 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4510 t, tlen, lv = tag_strip(tlv)
4511 except DecodeError as err:
4512 raise err.__class__(
4514 klass=self.__class__,
4515 decode_path=decode_path,
4520 klass=self.__class__,
4521 decode_path=decode_path,
4528 l, llen, v = len_decode(lv)
4529 except LenIndefForm as err:
4530 if not ctx.get("bered", False):
4531 raise err.__class__(
4533 klass=self.__class__,
4534 decode_path=decode_path,
4537 l, llen, v = 0, 1, lv[1:]
4539 except DecodeError as err:
4540 raise err.__class__(
4542 klass=self.__class__,
4543 decode_path=decode_path,
4547 raise NotEnoughData(
4548 "encoded length is longer than data",
4549 klass=self.__class__,
4553 v, tail = v[:l], v[l:]
4555 sub_offset = offset + tlen + llen
4557 specs_items = self.specs.items
4559 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4561 for name, spec in specs_items():
4562 sub_decode_path = decode_path + (name,)
4568 decode_path=sub_decode_path,
4577 klass=self.__class__,
4578 decode_path=decode_path,
4581 value, v_tail = spec.decode(
4585 decode_path=sub_decode_path,
4588 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4589 sub_offset += value_len
4592 if spec.default is None or value != spec.default: # pragma: no cover
4593 # SeqMixing.test_encoded_default_accepted covers that place
4594 values[name] = value
4595 obj = self.__class__(
4599 default=self.default,
4600 optional=self.optional,
4601 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4606 msg="not all values are ready",
4607 klass=self.__class__,
4608 decode_path=decode_path,
4611 obj.lenindef = lenindef
4612 return obj, (v[EOC_LEN:] if lenindef else tail)
4615 class SequenceOf(Obj):
4616 """``SEQUENCE OF`` sequence type
4618 For that kind of type you must specify the object it will carry on
4619 (bounds are for example here, not required)::
4621 class Ints(SequenceOf):
4626 >>> ints.append(Integer(123))
4627 >>> ints.append(Integer(234))
4629 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4630 >>> [int(i) for i in ints]
4632 >>> ints.append(Integer(345))
4633 Traceback (most recent call last):
4634 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4637 >>> ints[1] = Integer(345)
4639 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4641 Also you can initialize sequence with preinitialized values:
4643 >>> ints = Ints([Integer(123), Integer(234)])
4645 __slots__ = ("spec", "_bound_min", "_bound_max")
4646 tag_default = tag_encode(form=TagFormConstructed, num=16)
4647 asn1_type_name = "SEQUENCE OF"
4660 super(SequenceOf, self).__init__(
4668 schema = getattr(self, "schema", None)
4670 raise ValueError("schema must be specified")
4672 self._bound_min, self._bound_max = getattr(
4676 ) if bounds is None else bounds
4678 if value is not None:
4679 self._value = self._value_sanitize(value)
4680 if default is not None:
4681 default_value = self._value_sanitize(default)
4682 default_obj = self.__class__(
4687 default_obj._value = default_value
4688 self.default = default_obj
4690 self._value = default_obj.copy()._value
4692 def _value_sanitize(self, value):
4693 if issubclass(value.__class__, SequenceOf):
4694 value = value._value
4695 elif hasattr(value, "__iter__"):
4698 raise InvalidValueType((self.__class__, iter))
4699 if not self._bound_min <= len(value) <= self._bound_max:
4700 raise BoundsError(self._bound_min, len(value), self._bound_max)
4702 if not isinstance(v, self.spec.__class__):
4703 raise InvalidValueType((self.spec.__class__,))
4708 return all(v.ready for v in self._value)
4711 obj = self.__class__(schema=self.spec)
4712 obj._bound_min = self._bound_min
4713 obj._bound_max = self._bound_max
4715 obj._expl = self._expl
4716 obj.default = self.default
4717 obj.optional = self.optional
4718 obj.offset = self.offset
4719 obj.llen = self.llen
4720 obj.vlen = self.vlen
4721 obj._value = [v.copy() for v in self._value]
4724 def __eq__(self, their):
4725 if isinstance(their, self.__class__):
4727 self.spec == their.spec and
4728 self.tag == their.tag and
4729 self._expl == their._expl and
4730 self._value == their._value
4732 if hasattr(their, "__iter__"):
4733 return self._value == list(their)
4745 return self.__class__(
4749 (self._bound_min, self._bound_max)
4750 if bounds is None else bounds
4752 impl=self.tag if impl is None else impl,
4753 expl=self._expl if expl is None else expl,
4754 default=self.default if default is None else default,
4755 optional=self.optional if optional is None else optional,
4758 def __contains__(self, key):
4759 return key in self._value
4761 def append(self, value):
4762 if not isinstance(value, self.spec.__class__):
4763 raise InvalidValueType((self.spec.__class__,))
4764 if len(self._value) + 1 > self._bound_max:
4767 len(self._value) + 1,
4770 self._value.append(value)
4773 self._assert_ready()
4774 return iter(self._value)
4777 self._assert_ready()
4778 return len(self._value)
4780 def __setitem__(self, key, value):
4781 if not isinstance(value, self.spec.__class__):
4782 raise InvalidValueType((self.spec.__class__,))
4783 self._value[key] = self.spec(value=value)
4785 def __getitem__(self, key):
4786 return self._value[key]
4788 def _encoded_values(self):
4789 return [v.encode() for v in self._value]
4792 v = b"".join(self._encoded_values())
4793 return b"".join((self.tag, len_encode(len(v)), v))
4795 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4797 t, tlen, lv = tag_strip(tlv)
4798 except DecodeError as err:
4799 raise err.__class__(
4801 klass=self.__class__,
4802 decode_path=decode_path,
4807 klass=self.__class__,
4808 decode_path=decode_path,
4815 l, llen, v = len_decode(lv)
4816 except LenIndefForm as err:
4817 if not ctx.get("bered", False):
4818 raise err.__class__(
4820 klass=self.__class__,
4821 decode_path=decode_path,
4824 l, llen, v = 0, 1, lv[1:]
4826 except DecodeError as err:
4827 raise err.__class__(
4829 klass=self.__class__,
4830 decode_path=decode_path,
4834 raise NotEnoughData(
4835 "encoded length is longer than data",
4836 klass=self.__class__,
4837 decode_path=decode_path,
4841 v, tail = v[:l], v[l:]
4843 sub_offset = offset + tlen + llen
4847 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4849 value, v_tail = spec.decode(
4853 decode_path=decode_path + (str(len(_value)),),
4856 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4857 sub_offset += value_len
4860 _value.append(value)
4861 obj = self.__class__(
4864 bounds=(self._bound_min, self._bound_max),
4867 default=self.default,
4868 optional=self.optional,
4869 _decoded=(offset, llen, vlen),
4871 obj.lenindef = lenindef
4872 return obj, (v[EOC_LEN:] if lenindef else tail)
4876 pp_console_row(next(self.pps())),
4877 ", ".join(repr(v) for v in self._value),
4880 def pps(self, decode_path=()):
4882 asn1_type_name=self.asn1_type_name,
4883 obj_name=self.__class__.__name__,
4884 decode_path=decode_path,
4885 optional=self.optional,
4886 default=self == self.default,
4887 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4888 expl=None if self._expl is None else tag_decode(self._expl),
4893 expl_offset=self.expl_offset if self.expled else None,
4894 expl_tlen=self.expl_tlen if self.expled else None,
4895 expl_llen=self.expl_llen if self.expled else None,
4896 expl_vlen=self.expl_vlen if self.expled else None,
4898 for i, value in enumerate(self._value):
4899 yield value.pps(decode_path=decode_path + (str(i),))
4902 class SetOf(SequenceOf):
4903 """``SET OF`` sequence type
4905 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4908 tag_default = tag_encode(form=TagFormConstructed, num=17)
4909 asn1_type_name = "SET OF"
4912 raws = self._encoded_values()
4915 return b"".join((self.tag, len_encode(len(v)), v))
4918 def obj_by_path(pypath): # pragma: no cover
4919 """Import object specified as string Python path
4921 Modules must be separated from classes/functions with ``:``.
4923 >>> obj_by_path("foo.bar:Baz")
4924 <class 'foo.bar.Baz'>
4925 >>> obj_by_path("foo.bar:Baz.boo")
4926 <classmethod 'foo.bar.Baz.boo'>
4928 mod, objs = pypath.rsplit(":", 1)
4929 from importlib import import_module
4930 obj = import_module(mod)
4931 for obj_name in objs.split("."):
4932 obj = getattr(obj, obj_name)
4936 def generic_decoder(): # pragma: no cover
4937 # All of this below is a big hack with self references
4938 choice = PrimitiveTypes()
4939 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4940 choice.specs["SetOf"] = SetOf(schema=choice)
4942 choice.specs["SequenceOf%d" % i] = SequenceOf(
4946 choice.specs["Any"] = Any()
4948 # Class name equals to type name, to omit it from output
4949 class SEQUENCEOF(SequenceOf):
4953 def pprint_any(obj, oids=None, with_colours=False):
4954 def _pprint_pps(pps):
4956 if hasattr(pp, "_fields"):
4957 if pp.asn1_type_name == Choice.asn1_type_name:
4959 pp_kwargs = pp._asdict()
4960 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4961 pp = _pp(**pp_kwargs)
4962 yield pp_console_row(
4967 with_colours=with_colours,
4969 for row in pp_console_blob(pp):
4972 for row in _pprint_pps(pp):
4974 return "\n".join(_pprint_pps(obj.pps()))
4975 return SEQUENCEOF(), pprint_any
4978 def main(): # pragma: no cover
4980 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4981 parser.add_argument(
4985 help="Skip that number of bytes from the beginning",
4987 parser.add_argument(
4989 help="Python path to dictionary with OIDs",
4991 parser.add_argument(
4993 help="Python path to schema definition to use",
4995 parser.add_argument(
4996 "--defines-by-path",
4997 help="Python path to decoder's defines_by_path",
4999 parser.add_argument(
5001 type=argparse.FileType("rb"),
5002 help="Path to DER file you want to decode",
5004 args = parser.parse_args()
5005 args.DERFile.seek(args.skip)
5006 der = memoryview(args.DERFile.read())
5007 args.DERFile.close()
5008 oids = obj_by_path(args.oids) if args.oids else {}
5010 schema = obj_by_path(args.schema)
5011 from functools import partial
5012 pprinter = partial(pprint, big_blobs=True)
5014 schema, pprinter = generic_decoder()
5015 ctx = {"bered": True}
5016 if args.defines_by_path is not None:
5017 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5018 obj, tail = schema().decode(der, ctx=ctx)
5022 with_colours=True if environ.get("NO_COLOR") is None else False,
5025 print("\nTrailing data: %s" % hexenc(tail))
5028 if __name__ == "__main__":