3 # PyDERASN -- Python ASN.1 DER/BER 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/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
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:
193 * ``expled`` -- to know if explicit tag is set
194 * ``expl_offset`` (it is lesser than ``offset``)
197 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
198 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
200 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
203 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
210 You can specify so called context keyword argument during ``decode()``
211 invocation. It is dictionary containing various options governing
214 Currently available context options:
216 * :ref:`bered <bered_ctx>`
217 * :ref:`defines_by_path <defines_by_path_ctx>`
218 * :ref:`strict_default_existence <strict_default_existence_ctx>`
225 All objects have ``pps()`` method, that is a generator of
226 :py:class:`pyderasn.PP` namedtuple, holding various raw information
227 about the object. If ``pps`` is called on sequences, then all underlying
228 ``PP`` will be yielded.
230 You can use :py:func:`pyderasn.pp_console_row` function, converting
231 those ``PP`` to human readable string. Actually exactly it is used for
232 all object ``repr``. But it is easy to write custom formatters.
234 >>> from pyderasn import pprint
235 >>> encoded = Integer(-12345).encode()
236 >>> obj, tail = Integer().decode(encoded)
237 >>> print(pprint(obj))
238 0 [1,1, 2] INTEGER -12345
245 ASN.1 structures often have ANY and OCTET STRING fields, that are
246 DEFINED BY some previously met ObjectIdentifier. This library provides
247 ability to specify mapping between some OID and field that must be
248 decoded with specific specification.
253 :py:class:`pyderasn.ObjectIdentifier` field inside
254 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
255 necessary for decoding structures. For example, CMS (:rfc:`5652`)
258 class ContentInfo(Sequence):
260 ("contentType", ContentType(defines=((("content",), {
261 id_digestedData: DigestedData(),
262 id_signedData: SignedData(),
264 ("content", Any(expl=tag_ctxc(0))),
267 ``contentType`` field tells that it defines that ``content`` must be
268 decoded with ``SignedData`` specification, if ``contentType`` equals to
269 ``id-signedData``. The same applies to ``DigestedData``. If
270 ``contentType`` contains unknown OID, then no automatic decoding is
273 You can specify multiple fields, that will be autodecoded -- that is why
274 ``defines`` kwarg is a sequence. You can specify defined field
275 relatively or absolutely to current decode path. For example ``defines``
276 for AlgorithmIdentifier of X.509's
277 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
281 id_ecPublicKey: ECParameters(),
282 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
284 (("..", "subjectPublicKey"), {
285 id_rsaEncryption: RSAPublicKey(),
286 id_GostR3410_2001: OctetString(),
290 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
291 autodecode its parameters inside SPKI's algorithm and its public key
294 Following types can be automatically decoded (DEFINED BY):
296 * :py:class:`pyderasn.Any`
297 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
298 * :py:class:`pyderasn.OctetString`
299 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
300 ``Any``/``BitString``/``OctetString``-s
302 When any of those fields is automatically decoded, then ``.defined``
303 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
304 was defined, ``value`` contains corresponding decoded value. For example
305 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
307 .. _defines_by_path_ctx:
309 defines_by_path context option
310 ______________________________
312 Sometimes you either can not or do not want to explicitly set *defines*
313 in the scheme. You can dynamically apply those definitions when calling
314 ``.decode()`` method.
316 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
317 value must be sequence of following tuples::
319 (decode_path, defines)
321 where ``decode_path`` is a tuple holding so-called decode path to the
322 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
323 ``defines``, holding exactly the same value as accepted in its keyword
326 For example, again for CMS, you want to automatically decode
327 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
328 structures it may hold. Also, automatically decode ``controlSequence``
331 content_info, tail = ContentInfo().decode(data, defines_by_path=(
334 ((("content",), {id_signedData: SignedData()}),),
339 DecodePathDefBy(id_signedData),
344 id_cct_PKIData: PKIData(),
345 id_cct_PKIResponse: PKIResponse(),
351 DecodePathDefBy(id_signedData),
354 DecodePathDefBy(id_cct_PKIResponse),
360 id_cmc_recipientNonce: RecipientNonce(),
361 id_cmc_senderNonce: SenderNonce(),
362 id_cmc_statusInfoV2: CMCStatusInfoV2(),
363 id_cmc_transactionId: TransactionId(),
368 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
369 First function is useful for path construction when some automatic
370 decoding is already done. ``any`` means literally any value it meet --
371 useful for SEQUENCE/SET OF-s.
378 By default PyDERASN accepts only DER encoded data. It always encodes to
379 DER. But you can optionally enable BER decoding with setting ``bered``
380 :ref:`context <ctx>` argument to True. Indefinite lengths and
381 constructed primitive types should be parsed successfully.
383 * If object is encoded in BER form (not the DER one), then ``bered``
384 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
385 STRING`` can contain it.
386 * If object has an indefinite length encoding, then its ``lenindef``
387 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
388 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
390 * If object has an indefinite length encoded explicit tag, then
391 ``expl_lenindef`` is set to True.
393 EOC (end-of-contents) token's length is taken in advance in object's
401 .. autoclass:: pyderasn.Boolean
406 .. autoclass:: pyderasn.Integer
411 .. autoclass:: pyderasn.BitString
416 .. autoclass:: pyderasn.OctetString
421 .. autoclass:: pyderasn.Null
426 .. autoclass:: pyderasn.ObjectIdentifier
431 .. autoclass:: pyderasn.Enumerated
435 .. autoclass:: pyderasn.CommonString
439 .. autoclass:: pyderasn.NumericString
443 .. autoclass:: pyderasn.UTCTime
444 :members: __init__, todatetime
448 .. autoclass:: pyderasn.GeneralizedTime
455 .. autoclass:: pyderasn.Choice
460 .. autoclass:: PrimitiveTypes
464 .. autoclass:: pyderasn.Any
472 .. autoclass:: pyderasn.Sequence
477 .. autoclass:: pyderasn.Set
482 .. autoclass:: pyderasn.SequenceOf
487 .. autoclass:: pyderasn.SetOf
493 .. autofunction:: pyderasn.abs_decode_path
494 .. autofunction:: pyderasn.hexenc
495 .. autofunction:: pyderasn.hexdec
496 .. autofunction:: pyderasn.tag_encode
497 .. autofunction:: pyderasn.tag_decode
498 .. autofunction:: pyderasn.tag_ctxp
499 .. autofunction:: pyderasn.tag_ctxc
500 .. autoclass:: pyderasn.Obj
501 .. autoclass:: pyderasn.DecodeError
503 .. autoclass:: pyderasn.NotEnoughData
504 .. autoclass:: pyderasn.LenIndefForm
505 .. autoclass:: pyderasn.TagMismatch
506 .. autoclass:: pyderasn.InvalidLength
507 .. autoclass:: pyderasn.InvalidOID
508 .. autoclass:: pyderasn.ObjUnknown
509 .. autoclass:: pyderasn.ObjNotReady
510 .. autoclass:: pyderasn.InvalidValueType
511 .. autoclass:: pyderasn.BoundsError
514 from codecs import getdecoder
515 from codecs import getencoder
516 from collections import namedtuple
517 from collections import OrderedDict
518 from datetime import datetime
519 from math import ceil
520 from os import environ
521 from string import digits
523 from six import add_metaclass
524 from six import binary_type
525 from six import byte2int
526 from six import indexbytes
527 from six import int2byte
528 from six import integer_types
529 from six import iterbytes
531 from six import string_types
532 from six import text_type
533 from six.moves import xrange as six_xrange
537 from termcolor import colored
539 def colored(what, *args):
583 "TagClassApplication",
587 "TagFormConstructed",
598 TagClassUniversal = 0
599 TagClassApplication = 1 << 6
600 TagClassContext = 1 << 7
601 TagClassPrivate = 1 << 6 | 1 << 7
603 TagFormConstructed = 1 << 5
606 TagClassApplication: "APPLICATION ",
607 TagClassPrivate: "PRIVATE ",
608 TagClassUniversal: "UNIV ",
612 LENINDEF = b"\x80" # length indefinite mark
613 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
616 ########################################################################
618 ########################################################################
620 class DecodeError(Exception):
621 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
623 :param str msg: reason of decode failing
624 :param klass: optional exact DecodeError inherited class (like
625 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
626 :py:exc:`InvalidLength`)
627 :param decode_path: tuple of strings. It contains human
628 readable names of the fields through which
629 decoding process has passed
630 :param int offset: binary offset where failure happened
632 super(DecodeError, self).__init__()
635 self.decode_path = decode_path
641 "" if self.klass is None else self.klass.__name__,
643 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
644 if len(self.decode_path) > 0 else ""
646 ("(at %d)" % self.offset) if self.offset > 0 else "",
652 return "%s(%s)" % (self.__class__.__name__, self)
655 class NotEnoughData(DecodeError):
659 class LenIndefForm(DecodeError):
663 class TagMismatch(DecodeError):
667 class InvalidLength(DecodeError):
671 class InvalidOID(DecodeError):
675 class ObjUnknown(ValueError):
676 def __init__(self, name):
677 super(ObjUnknown, self).__init__()
681 return "object is unknown: %s" % self.name
684 return "%s(%s)" % (self.__class__.__name__, self)
687 class ObjNotReady(ValueError):
688 def __init__(self, name):
689 super(ObjNotReady, self).__init__()
693 return "object is not ready: %s" % self.name
696 return "%s(%s)" % (self.__class__.__name__, self)
699 class InvalidValueType(ValueError):
700 def __init__(self, expected_types):
701 super(InvalidValueType, self).__init__()
702 self.expected_types = expected_types
705 return "invalid value type, expected: %s" % ", ".join(
706 [repr(t) for t in self.expected_types]
710 return "%s(%s)" % (self.__class__.__name__, self)
713 class BoundsError(ValueError):
714 def __init__(self, bound_min, value, bound_max):
715 super(BoundsError, self).__init__()
716 self.bound_min = bound_min
718 self.bound_max = bound_max
721 return "unsatisfied bounds: %s <= %s <= %s" % (
728 return "%s(%s)" % (self.__class__.__name__, self)
731 ########################################################################
733 ########################################################################
735 _hexdecoder = getdecoder("hex")
736 _hexencoder = getencoder("hex")
740 """Binary data to hexadecimal string convert
742 return _hexdecoder(data)[0]
746 """Hexadecimal string to binary data convert
748 return _hexencoder(data)[0].decode("ascii")
751 def int_bytes_len(num, byte_len=8):
754 return int(ceil(float(num.bit_length()) / byte_len))
757 def zero_ended_encode(num):
758 octets = bytearray(int_bytes_len(num, 7))
760 octets[i] = num & 0x7F
764 octets[i] = 0x80 | (num & 0x7F)
770 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
771 """Encode tag to binary form
773 :param int num: tag's number
774 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
775 :py:data:`pyderasn.TagClassContext`,
776 :py:data:`pyderasn.TagClassApplication`,
777 :py:data:`pyderasn.TagClassPrivate`)
778 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
779 :py:data:`pyderasn.TagFormConstructed`)
783 return int2byte(klass | form | num)
784 # [XX|X|11111][1.......][1.......] ... [0.......]
785 return int2byte(klass | form | 31) + zero_ended_encode(num)
789 """Decode tag from binary form
793 No validation is performed, assuming that it has already passed.
795 It returns tuple with three integers, as
796 :py:func:`pyderasn.tag_encode` accepts.
798 first_octet = byte2int(tag)
799 klass = first_octet & 0xC0
800 form = first_octet & 0x20
801 if first_octet & 0x1F < 0x1F:
802 return (klass, form, first_octet & 0x1F)
804 for octet in iterbytes(tag[1:]):
807 return (klass, form, num)
811 """Create CONTEXT PRIMITIVE tag
813 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
817 """Create CONTEXT CONSTRUCTED tag
819 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
823 """Take off tag from the data
825 :returns: (encoded tag, tag length, remaining data)
828 raise NotEnoughData("no data at all")
829 if byte2int(data) & 0x1F < 31:
830 return data[:1], 1, data[1:]
835 raise DecodeError("unfinished tag")
836 if indexbytes(data, i) & 0x80 == 0:
839 return data[:i], i, data[i:]
845 octets = bytearray(int_bytes_len(l) + 1)
846 octets[0] = 0x80 | (len(octets) - 1)
847 for i in six_xrange(len(octets) - 1, 0, -1):
853 def len_decode(data):
856 :returns: (decoded length, length's length, remaining data)
857 :raises LenIndefForm: if indefinite form encoding is met
860 raise NotEnoughData("no data at all")
861 first_octet = byte2int(data)
862 if first_octet & 0x80 == 0:
863 return first_octet, 1, data[1:]
864 octets_num = first_octet & 0x7F
865 if octets_num + 1 > len(data):
866 raise NotEnoughData("encoded length is longer than data")
869 if byte2int(data[1:]) == 0:
870 raise DecodeError("leading zeros")
872 for v in iterbytes(data[1:1 + octets_num]):
875 raise DecodeError("long form instead of short one")
876 return l, 1 + octets_num, data[1 + octets_num:]
879 ########################################################################
881 ########################################################################
883 class AutoAddSlots(type):
884 def __new__(mcs, name, bases, _dict):
885 _dict["__slots__"] = _dict.get("__slots__", ())
886 return type.__new__(mcs, name, bases, _dict)
889 @add_metaclass(AutoAddSlots)
891 """Common ASN.1 object class
893 All ASN.1 types are inherited from it. It has metaclass that
894 automatically adds ``__slots__`` to all inherited classes.
918 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
919 self._expl = getattr(self, "expl", None) if expl is None else expl
920 if self.tag != self.tag_default and self._expl is not None:
921 raise ValueError("implicit and explicit tags can not be set simultaneously")
922 if default is not None:
924 self.optional = optional
925 self.offset, self.llen, self.vlen = _decoded
927 self.expl_lenindef = False
928 self.lenindef = False
932 def ready(self): # pragma: no cover
933 """Is object ready to be encoded?
935 raise NotImplementedError()
937 def _assert_ready(self):
939 raise ObjNotReady(self.__class__.__name__)
943 """Is object decoded?
945 return (self.llen + self.vlen) > 0
947 def copy(self): # pragma: no cover
948 """Make a copy of object, safe to be mutated
950 raise NotImplementedError()
958 return self.tlen + self.llen + self.vlen
960 def __str__(self): # pragma: no cover
961 return self.__bytes__() if PY2 else self.__unicode__()
963 def __ne__(self, their):
964 return not(self == their)
966 def __gt__(self, their): # pragma: no cover
967 return not(self < their)
969 def __le__(self, their): # pragma: no cover
970 return (self == their) or (self < their)
972 def __ge__(self, their): # pragma: no cover
973 return (self == their) or (self > their)
975 def _encode(self): # pragma: no cover
976 raise NotImplementedError()
978 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
979 raise NotImplementedError()
983 if self._expl is None:
985 return b"".join((self._expl, len_encode(len(raw)), raw))
998 :param data: either binary or memoryview
999 :param int offset: initial data's offset
1000 :param bool leavemm: do we need to leave memoryview of remaining
1001 data as is, or convert it to bytes otherwise
1002 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1003 :param tag_only: decode only the tag, without length and contents
1004 (used only in Choice and Set structures, trying to
1005 determine if tag satisfies the scheme)
1006 :returns: (Obj, remaining data)
1010 tlv = memoryview(data)
1011 if self._expl is None:
1012 result = self._decode(
1015 decode_path=decode_path,
1024 t, tlen, lv = tag_strip(tlv)
1025 except DecodeError as err:
1026 raise err.__class__(
1028 klass=self.__class__,
1029 decode_path=decode_path,
1034 klass=self.__class__,
1035 decode_path=decode_path,
1039 l, llen, v = len_decode(lv)
1040 except LenIndefForm as err:
1041 if not ctx.get("bered", False):
1042 raise err.__class__(
1044 klass=self.__class__,
1045 decode_path=decode_path,
1049 offset += tlen + llen
1050 result = self._decode(
1053 decode_path=decode_path,
1060 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1061 if eoc_expected.tobytes() != EOC:
1064 klass=self.__class__,
1065 decode_path=decode_path,
1069 obj.expl_lenindef = True
1070 except DecodeError as err:
1071 raise err.__class__(
1073 klass=self.__class__,
1074 decode_path=decode_path,
1079 raise NotEnoughData(
1080 "encoded length is longer than data",
1081 klass=self.__class__,
1082 decode_path=decode_path,
1085 result = self._decode(
1087 offset=offset + tlen + llen,
1088 decode_path=decode_path,
1095 return obj, (tail if leavemm else tail.tobytes())
1099 return self._expl is not None
1106 def expl_tlen(self):
1107 return len(self._expl)
1110 def expl_llen(self):
1111 if self.expl_lenindef:
1113 return len(len_encode(self.tlvlen))
1116 def expl_offset(self):
1117 return self.offset - self.expl_tlen - self.expl_llen
1120 def expl_vlen(self):
1124 def expl_tlvlen(self):
1125 return self.expl_tlen + self.expl_llen + self.expl_vlen
1128 def fulloffset(self):
1129 return self.expl_offset if self.expled else self.offset
1133 return self.expl_tlvlen if self.expled else self.tlvlen
1135 def pps_lenindef(self, decode_path):
1138 asn1_type_name="EOC",
1140 decode_path=decode_path,
1142 self.offset + self.tlvlen -
1143 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1150 if self.expl_lenindef:
1152 asn1_type_name="EOC",
1153 obj_name="EXPLICIT",
1154 decode_path=decode_path,
1155 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1163 class DecodePathDefBy(object):
1164 """DEFINED BY representation inside decode path
1166 __slots__ = ("defined_by",)
1168 def __init__(self, defined_by):
1169 self.defined_by = defined_by
1171 def __ne__(self, their):
1172 return not(self == their)
1174 def __eq__(self, their):
1175 if not isinstance(their, self.__class__):
1177 return self.defined_by == their.defined_by
1180 return "DEFINED BY " + str(self.defined_by)
1183 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1186 ########################################################################
1188 ########################################################################
1190 PP = namedtuple("PP", (
1215 asn1_type_name="unknown",
1232 expl_lenindef=False,
1260 def _colourize(what, colour, with_colours, attrs=("bold",)):
1261 return colored(what, colour, attrs=attrs) if with_colours else what
1270 with_decode_path=False,
1271 decode_path_len_decrease=0,
1278 " " if pp.expl_offset is None else
1279 ("-%d" % (pp.offset - pp.expl_offset))
1281 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1283 cols.append(_colourize(col, "red", with_colours, ()))
1284 col = "[%d,%d,%4d]%s" % (
1288 LENINDEF_PP_CHAR if pp.lenindef else " "
1290 col = _colourize(col, "green", with_colours, ())
1292 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1293 if decode_path_len > 0:
1294 cols.append(" ." * decode_path_len)
1295 ent = pp.decode_path[-1]
1296 if isinstance(ent, DecodePathDefBy):
1297 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1298 value = str(ent.defined_by)
1300 oids is not None and
1301 ent.defined_by.asn1_type_name ==
1302 ObjectIdentifier.asn1_type_name and
1305 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1307 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1309 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1310 if pp.expl is not None:
1311 klass, _, num = pp.expl
1312 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1313 cols.append(_colourize(col, "blue", with_colours))
1314 if pp.impl is not None:
1315 klass, _, num = pp.impl
1316 col = "[%s%d]" % (TagClassReprs[klass], num)
1317 cols.append(_colourize(col, "blue", with_colours))
1318 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1319 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1321 cols.append(_colourize("BER", "red", with_colours))
1322 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1323 if pp.value is not None:
1325 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1327 oids is not None and
1328 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1331 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1333 if isinstance(pp.blob, binary_type):
1334 cols.append(hexenc(pp.blob))
1335 elif isinstance(pp.blob, tuple):
1336 cols.append(", ".join(pp.blob))
1338 cols.append(_colourize("OPTIONAL", "red", with_colours))
1340 cols.append(_colourize("DEFAULT", "red", with_colours))
1341 if with_decode_path:
1342 cols.append(_colourize(
1343 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1347 return " ".join(cols)
1350 def pp_console_blob(pp, decode_path_len_decrease=0):
1351 cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
1352 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1353 if decode_path_len > 0:
1354 cols.append(" ." * (decode_path_len + 1))
1355 if isinstance(pp.blob, binary_type):
1356 blob = hexenc(pp.blob).upper()
1357 for i in range(0, len(blob), 32):
1358 chunk = blob[i:i + 32]
1359 yield " ".join(cols + [":".join(
1360 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1362 elif isinstance(pp.blob, tuple):
1363 yield " ".join(cols + [", ".join(pp.blob)])
1371 with_decode_path=False,
1372 decode_path_only=(),
1374 """Pretty print object
1376 :param Obj obj: object you want to pretty print
1377 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1378 from it is met, then its humand readable form is printed
1379 :param big_blobs: if large binary objects are met (like OctetString
1380 values), do we need to print them too, on separate
1382 :param with_colours: colourize output, if ``termcolor`` library
1384 :param with_decode_path: print decode path
1385 :param decode_path_only: print only that specified decode path
1387 def _pprint_pps(pps):
1389 if hasattr(pp, "_fields"):
1391 decode_path_only != () and
1393 str(p) for p in pp.decode_path[:len(decode_path_only)]
1394 ) != decode_path_only
1398 yield pp_console_row(
1403 with_colours=with_colours,
1404 with_decode_path=with_decode_path,
1405 decode_path_len_decrease=len(decode_path_only),
1407 for row in pp_console_blob(
1409 decode_path_len_decrease=len(decode_path_only),
1413 yield pp_console_row(
1418 with_colours=with_colours,
1419 with_decode_path=with_decode_path,
1420 decode_path_len_decrease=len(decode_path_only),
1423 for row in _pprint_pps(pp):
1425 return "\n".join(_pprint_pps(obj.pps()))
1428 ########################################################################
1429 # ASN.1 primitive types
1430 ########################################################################
1433 """``BOOLEAN`` boolean type
1435 >>> b = Boolean(True)
1437 >>> b == Boolean(True)
1443 tag_default = tag_encode(1)
1444 asn1_type_name = "BOOLEAN"
1456 :param value: set the value. Either boolean type, or
1457 :py:class:`pyderasn.Boolean` object
1458 :param bytes impl: override default tag with ``IMPLICIT`` one
1459 :param bytes expl: override default tag with ``EXPLICIT`` one
1460 :param default: set default value. Type same as in ``value``
1461 :param bool optional: is object ``OPTIONAL`` in sequence
1463 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1464 self._value = None if value is None else self._value_sanitize(value)
1465 if default is not None:
1466 default = self._value_sanitize(default)
1467 self.default = self.__class__(
1473 self._value = default
1475 def _value_sanitize(self, value):
1476 if issubclass(value.__class__, Boolean):
1478 if isinstance(value, bool):
1480 raise InvalidValueType((self.__class__, bool))
1484 return self._value is not None
1487 obj = self.__class__()
1488 obj._value = self._value
1490 obj._expl = self._expl
1491 obj.default = self.default
1492 obj.optional = self.optional
1493 obj.offset = self.offset
1494 obj.llen = self.llen
1495 obj.vlen = self.vlen
1498 def __nonzero__(self):
1499 self._assert_ready()
1503 self._assert_ready()
1506 def __eq__(self, their):
1507 if isinstance(their, bool):
1508 return self._value == their
1509 if not issubclass(their.__class__, Boolean):
1512 self._value == their._value and
1513 self.tag == their.tag and
1514 self._expl == their._expl
1525 return self.__class__(
1527 impl=self.tag if impl is None else impl,
1528 expl=self._expl if expl is None else expl,
1529 default=self.default if default is None else default,
1530 optional=self.optional if optional is None else optional,
1534 self._assert_ready()
1538 (b"\xFF" if self._value else b"\x00"),
1541 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1543 t, _, lv = tag_strip(tlv)
1544 except DecodeError as err:
1545 raise err.__class__(
1547 klass=self.__class__,
1548 decode_path=decode_path,
1553 klass=self.__class__,
1554 decode_path=decode_path,
1560 l, _, v = len_decode(lv)
1561 except DecodeError as err:
1562 raise err.__class__(
1564 klass=self.__class__,
1565 decode_path=decode_path,
1569 raise InvalidLength(
1570 "Boolean's length must be equal to 1",
1571 klass=self.__class__,
1572 decode_path=decode_path,
1576 raise NotEnoughData(
1577 "encoded length is longer than data",
1578 klass=self.__class__,
1579 decode_path=decode_path,
1582 first_octet = byte2int(v)
1584 if first_octet == 0:
1586 elif first_octet == 0xFF:
1588 elif ctx.get("bered", False):
1593 "unacceptable Boolean value",
1594 klass=self.__class__,
1595 decode_path=decode_path,
1598 obj = self.__class__(
1602 default=self.default,
1603 optional=self.optional,
1604 _decoded=(offset, 1, 1),
1610 return pp_console_row(next(self.pps()))
1612 def pps(self, decode_path=()):
1614 asn1_type_name=self.asn1_type_name,
1615 obj_name=self.__class__.__name__,
1616 decode_path=decode_path,
1617 value=str(self._value) if self.ready else None,
1618 optional=self.optional,
1619 default=self == self.default,
1620 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1621 expl=None if self._expl is None else tag_decode(self._expl),
1626 expl_offset=self.expl_offset if self.expled else None,
1627 expl_tlen=self.expl_tlen if self.expled else None,
1628 expl_llen=self.expl_llen if self.expled else None,
1629 expl_vlen=self.expl_vlen if self.expled else None,
1630 expl_lenindef=self.expl_lenindef,
1633 for pp in self.pps_lenindef(decode_path):
1638 """``INTEGER`` integer type
1640 >>> b = Integer(-123)
1642 >>> b == Integer(-123)
1647 >>> Integer(2, bounds=(1, 3))
1649 >>> Integer(5, bounds=(1, 3))
1650 Traceback (most recent call last):
1651 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1655 class Version(Integer):
1662 >>> v = Version("v1")
1669 {'v3': 2, 'v1': 0, 'v2': 1}
1671 __slots__ = ("specs", "_bound_min", "_bound_max")
1672 tag_default = tag_encode(2)
1673 asn1_type_name = "INTEGER"
1687 :param value: set the value. Either integer type, named value
1688 (if ``schema`` is specified in the class), or
1689 :py:class:`pyderasn.Integer` object
1690 :param bounds: set ``(MIN, MAX)`` value constraint.
1691 (-inf, +inf) by default
1692 :param bytes impl: override default tag with ``IMPLICIT`` one
1693 :param bytes expl: override default tag with ``EXPLICIT`` one
1694 :param default: set default value. Type same as in ``value``
1695 :param bool optional: is object ``OPTIONAL`` in sequence
1697 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1699 specs = getattr(self, "schema", {}) if _specs is None else _specs
1700 self.specs = specs if isinstance(specs, dict) else dict(specs)
1701 self._bound_min, self._bound_max = getattr(
1704 (float("-inf"), float("+inf")),
1705 ) if bounds is None else bounds
1706 if value is not None:
1707 self._value = self._value_sanitize(value)
1708 if default is not None:
1709 default = self._value_sanitize(default)
1710 self.default = self.__class__(
1716 if self._value is None:
1717 self._value = default
1719 def _value_sanitize(self, value):
1720 if issubclass(value.__class__, Integer):
1721 value = value._value
1722 elif isinstance(value, integer_types):
1724 elif isinstance(value, str):
1725 value = self.specs.get(value)
1727 raise ObjUnknown("integer value: %s" % value)
1729 raise InvalidValueType((self.__class__, int, str))
1730 if not self._bound_min <= value <= self._bound_max:
1731 raise BoundsError(self._bound_min, value, self._bound_max)
1736 return self._value is not None
1739 obj = self.__class__(_specs=self.specs)
1740 obj._value = self._value
1741 obj._bound_min = self._bound_min
1742 obj._bound_max = self._bound_max
1744 obj._expl = self._expl
1745 obj.default = self.default
1746 obj.optional = self.optional
1747 obj.offset = self.offset
1748 obj.llen = self.llen
1749 obj.vlen = self.vlen
1753 self._assert_ready()
1754 return int(self._value)
1757 self._assert_ready()
1760 bytes(self._expl or b"") +
1761 str(self._value).encode("ascii"),
1764 def __eq__(self, their):
1765 if isinstance(their, integer_types):
1766 return self._value == their
1767 if not issubclass(their.__class__, Integer):
1770 self._value == their._value and
1771 self.tag == their.tag and
1772 self._expl == their._expl
1775 def __lt__(self, their):
1776 return self._value < their._value
1780 for name, value in self.specs.items():
1781 if value == self._value:
1793 return self.__class__(
1796 (self._bound_min, self._bound_max)
1797 if bounds is None else bounds
1799 impl=self.tag if impl is None else impl,
1800 expl=self._expl if expl is None else expl,
1801 default=self.default if default is None else default,
1802 optional=self.optional if optional is None else optional,
1807 self._assert_ready()
1811 octets = bytearray([0])
1815 octets = bytearray()
1817 octets.append((value & 0xFF) ^ 0xFF)
1819 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1822 octets = bytearray()
1824 octets.append(value & 0xFF)
1826 if octets[-1] & 0x80 > 0:
1829 octets = bytes(octets)
1831 bytes_len = ceil(value.bit_length() / 8) or 1
1834 octets = value.to_bytes(
1839 except OverflowError:
1843 return b"".join((self.tag, len_encode(len(octets)), octets))
1845 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1847 t, _, lv = tag_strip(tlv)
1848 except DecodeError as err:
1849 raise err.__class__(
1851 klass=self.__class__,
1852 decode_path=decode_path,
1857 klass=self.__class__,
1858 decode_path=decode_path,
1864 l, llen, v = len_decode(lv)
1865 except DecodeError as err:
1866 raise err.__class__(
1868 klass=self.__class__,
1869 decode_path=decode_path,
1873 raise NotEnoughData(
1874 "encoded length is longer than data",
1875 klass=self.__class__,
1876 decode_path=decode_path,
1880 raise NotEnoughData(
1882 klass=self.__class__,
1883 decode_path=decode_path,
1886 v, tail = v[:l], v[l:]
1887 first_octet = byte2int(v)
1889 second_octet = byte2int(v[1:])
1891 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1892 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1895 "non normalized integer",
1896 klass=self.__class__,
1897 decode_path=decode_path,
1902 if first_octet & 0x80 > 0:
1903 octets = bytearray()
1904 for octet in bytearray(v):
1905 octets.append(octet ^ 0xFF)
1906 for octet in octets:
1907 value = (value << 8) | octet
1911 for octet in bytearray(v):
1912 value = (value << 8) | octet
1914 value = int.from_bytes(v, byteorder="big", signed=True)
1916 obj = self.__class__(
1918 bounds=(self._bound_min, self._bound_max),
1921 default=self.default,
1922 optional=self.optional,
1924 _decoded=(offset, llen, l),
1926 except BoundsError as err:
1929 klass=self.__class__,
1930 decode_path=decode_path,
1936 return pp_console_row(next(self.pps()))
1938 def pps(self, decode_path=()):
1940 asn1_type_name=self.asn1_type_name,
1941 obj_name=self.__class__.__name__,
1942 decode_path=decode_path,
1943 value=(self.named or str(self._value)) if self.ready else None,
1944 optional=self.optional,
1945 default=self == self.default,
1946 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1947 expl=None if self._expl is None else tag_decode(self._expl),
1952 expl_offset=self.expl_offset if self.expled else None,
1953 expl_tlen=self.expl_tlen if self.expled else None,
1954 expl_llen=self.expl_llen if self.expled else None,
1955 expl_vlen=self.expl_vlen if self.expled else None,
1956 expl_lenindef=self.expl_lenindef,
1958 for pp in self.pps_lenindef(decode_path):
1962 class BitString(Obj):
1963 """``BIT STRING`` bit string type
1965 >>> BitString(b"hello world")
1966 BIT STRING 88 bits 68656c6c6f20776f726c64
1969 >>> b == b"hello world"
1974 >>> BitString("'0A3B5F291CD'H")
1975 BIT STRING 44 bits 0a3b5f291cd0
1976 >>> b = BitString("'010110000000'B")
1977 BIT STRING 12 bits 5800
1980 >>> b[0], b[1], b[2], b[3]
1981 (False, True, False, True)
1985 [False, True, False, True, True, False, False, False, False, False, False, False]
1989 class KeyUsage(BitString):
1991 ("digitalSignature", 0),
1992 ("nonRepudiation", 1),
1993 ("keyEncipherment", 2),
1996 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1997 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1999 ['nonRepudiation', 'keyEncipherment']
2001 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2005 Pay attention that BIT STRING can be encoded both in primitive
2006 and constructed forms. Decoder always checks constructed form tag
2007 additionally to specified primitive one. If BER decoding is
2008 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2009 of DER restrictions.
2011 __slots__ = ("tag_constructed", "specs", "defined")
2012 tag_default = tag_encode(3)
2013 asn1_type_name = "BIT STRING"
2026 :param value: set the value. Either binary type, tuple of named
2027 values (if ``schema`` is specified in the class),
2028 string in ``'XXX...'B`` form, or
2029 :py:class:`pyderasn.BitString` object
2030 :param bytes impl: override default tag with ``IMPLICIT`` one
2031 :param bytes expl: override default tag with ``EXPLICIT`` one
2032 :param default: set default value. Type same as in ``value``
2033 :param bool optional: is object ``OPTIONAL`` in sequence
2035 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2036 specs = getattr(self, "schema", {}) if _specs is None else _specs
2037 self.specs = specs if isinstance(specs, dict) else dict(specs)
2038 self._value = None if value is None else self._value_sanitize(value)
2039 if default is not None:
2040 default = self._value_sanitize(default)
2041 self.default = self.__class__(
2047 self._value = default
2049 tag_klass, _, tag_num = tag_decode(self.tag)
2050 self.tag_constructed = tag_encode(
2052 form=TagFormConstructed,
2056 def _bits2octets(self, bits):
2057 if len(self.specs) > 0:
2058 bits = bits.rstrip("0")
2060 bits += "0" * ((8 - (bit_len % 8)) % 8)
2061 octets = bytearray(len(bits) // 8)
2062 for i in six_xrange(len(octets)):
2063 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2064 return bit_len, bytes(octets)
2066 def _value_sanitize(self, value):
2067 if issubclass(value.__class__, BitString):
2069 if isinstance(value, (string_types, binary_type)):
2071 isinstance(value, string_types) and
2072 value.startswith("'")
2074 if value.endswith("'B"):
2076 if not set(value) <= set(("0", "1")):
2077 raise ValueError("B's coding contains unacceptable chars")
2078 return self._bits2octets(value)
2079 elif value.endswith("'H"):
2083 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2085 if isinstance(value, binary_type):
2086 return (len(value) * 8, value)
2088 raise InvalidValueType((self.__class__, string_types, binary_type))
2089 if isinstance(value, tuple):
2092 isinstance(value[0], integer_types) and
2093 isinstance(value[1], binary_type)
2098 bit = self.specs.get(name)
2100 raise ObjUnknown("BitString value: %s" % name)
2103 return self._bits2octets("")
2105 return self._bits2octets("".join(
2106 ("1" if bit in bits else "0")
2107 for bit in six_xrange(max(bits) + 1)
2109 raise InvalidValueType((self.__class__, binary_type, string_types))
2113 return self._value is not None
2116 obj = self.__class__(_specs=self.specs)
2118 if value is not None:
2119 value = (value[0], value[1])
2122 obj._expl = self._expl
2123 obj.default = self.default
2124 obj.optional = self.optional
2125 obj.offset = self.offset
2126 obj.llen = self.llen
2127 obj.vlen = self.vlen
2131 self._assert_ready()
2132 for i in six_xrange(self._value[0]):
2137 self._assert_ready()
2138 return self._value[0]
2140 def __bytes__(self):
2141 self._assert_ready()
2142 return self._value[1]
2144 def __eq__(self, their):
2145 if isinstance(their, bytes):
2146 return self._value[1] == their
2147 if not issubclass(their.__class__, BitString):
2150 self._value == their._value and
2151 self.tag == their.tag and
2152 self._expl == their._expl
2157 return [name for name, bit in self.specs.items() if self[bit]]
2167 return self.__class__(
2169 impl=self.tag if impl is None else impl,
2170 expl=self._expl if expl is None else expl,
2171 default=self.default if default is None else default,
2172 optional=self.optional if optional is None else optional,
2176 def __getitem__(self, key):
2177 if isinstance(key, int):
2178 bit_len, octets = self._value
2182 byte2int(memoryview(octets)[key // 8:]) >>
2185 if isinstance(key, string_types):
2186 value = self.specs.get(key)
2188 raise ObjUnknown("BitString value: %s" % key)
2190 raise InvalidValueType((int, str))
2193 self._assert_ready()
2194 bit_len, octets = self._value
2197 len_encode(len(octets) + 1),
2198 int2byte((8 - bit_len % 8) % 8),
2202 def _decode_chunk(self, lv, offset, decode_path, ctx):
2204 l, llen, v = len_decode(lv)
2205 except DecodeError as err:
2206 raise err.__class__(
2208 klass=self.__class__,
2209 decode_path=decode_path,
2213 raise NotEnoughData(
2214 "encoded length is longer than data",
2215 klass=self.__class__,
2216 decode_path=decode_path,
2220 raise NotEnoughData(
2222 klass=self.__class__,
2223 decode_path=decode_path,
2226 pad_size = byte2int(v)
2227 if l == 1 and pad_size != 0:
2229 "invalid empty value",
2230 klass=self.__class__,
2231 decode_path=decode_path,
2237 klass=self.__class__,
2238 decode_path=decode_path,
2241 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2244 klass=self.__class__,
2245 decode_path=decode_path,
2248 v, tail = v[:l], v[l:]
2249 obj = self.__class__(
2250 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2253 default=self.default,
2254 optional=self.optional,
2256 _decoded=(offset, llen, l),
2260 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2262 t, tlen, lv = tag_strip(tlv)
2263 except DecodeError as err:
2264 raise err.__class__(
2266 klass=self.__class__,
2267 decode_path=decode_path,
2273 return self._decode_chunk(lv, offset, decode_path, ctx)
2274 if t == self.tag_constructed:
2275 if not ctx.get("bered", False):
2277 "unallowed BER constructed encoding",
2278 klass=self.__class__,
2279 decode_path=decode_path,
2286 l, llen, v = len_decode(lv)
2287 except LenIndefForm:
2288 llen, l, v = 1, 0, lv[1:]
2290 except DecodeError as err:
2291 raise err.__class__(
2293 klass=self.__class__,
2294 decode_path=decode_path,
2298 raise NotEnoughData(
2299 "encoded length is longer than data",
2300 klass=self.__class__,
2301 decode_path=decode_path,
2304 if not lenindef and l == 0:
2305 raise NotEnoughData(
2307 klass=self.__class__,
2308 decode_path=decode_path,
2312 sub_offset = offset + tlen + llen
2316 if v[:EOC_LEN].tobytes() == EOC:
2323 "chunk out of bounds",
2324 klass=self.__class__,
2325 decode_path=decode_path + (str(len(chunks) - 1),),
2326 offset=chunks[-1].offset,
2328 sub_decode_path = decode_path + (str(len(chunks)),)
2330 chunk, v_tail = BitString().decode(
2333 decode_path=sub_decode_path,
2339 "expected BitString encoded chunk",
2340 klass=self.__class__,
2341 decode_path=sub_decode_path,
2344 chunks.append(chunk)
2345 sub_offset += chunk.tlvlen
2346 vlen += chunk.tlvlen
2348 if len(chunks) == 0:
2351 klass=self.__class__,
2352 decode_path=decode_path,
2357 for chunk_i, chunk in enumerate(chunks[:-1]):
2358 if chunk.bit_len % 8 != 0:
2360 "BitString chunk is not multiple of 8 bits",
2361 klass=self.__class__,
2362 decode_path=decode_path + (str(chunk_i),),
2363 offset=chunk.offset,
2365 values.append(bytes(chunk))
2366 bit_len += chunk.bit_len
2367 chunk_last = chunks[-1]
2368 values.append(bytes(chunk_last))
2369 bit_len += chunk_last.bit_len
2370 obj = self.__class__(
2371 value=(bit_len, b"".join(values)),
2374 default=self.default,
2375 optional=self.optional,
2377 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2379 obj.lenindef = lenindef
2381 return obj, (v[EOC_LEN:] if lenindef else v)
2383 klass=self.__class__,
2384 decode_path=decode_path,
2389 return pp_console_row(next(self.pps()))
2391 def pps(self, decode_path=()):
2395 bit_len, blob = self._value
2396 value = "%d bits" % bit_len
2397 if len(self.specs) > 0:
2398 blob = tuple(self.named)
2400 asn1_type_name=self.asn1_type_name,
2401 obj_name=self.__class__.__name__,
2402 decode_path=decode_path,
2405 optional=self.optional,
2406 default=self == self.default,
2407 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2408 expl=None if self._expl is None else tag_decode(self._expl),
2413 expl_offset=self.expl_offset if self.expled else None,
2414 expl_tlen=self.expl_tlen if self.expled else None,
2415 expl_llen=self.expl_llen if self.expled else None,
2416 expl_vlen=self.expl_vlen if self.expled else None,
2417 expl_lenindef=self.expl_lenindef,
2418 lenindef=self.lenindef,
2421 defined_by, defined = self.defined or (None, None)
2422 if defined_by is not None:
2424 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2426 for pp in self.pps_lenindef(decode_path):
2430 class OctetString(Obj):
2431 """``OCTET STRING`` binary string type
2433 >>> s = OctetString(b"hello world")
2434 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2435 >>> s == OctetString(b"hello world")
2440 >>> OctetString(b"hello", bounds=(4, 4))
2441 Traceback (most recent call last):
2442 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2443 >>> OctetString(b"hell", bounds=(4, 4))
2444 OCTET STRING 4 bytes 68656c6c
2448 Pay attention that OCTET STRING can be encoded both in primitive
2449 and constructed forms. Decoder always checks constructed form tag
2450 additionally to specified primitive one. If BER decoding is
2451 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2452 of DER restrictions.
2454 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2455 tag_default = tag_encode(4)
2456 asn1_type_name = "OCTET STRING"
2469 :param value: set the value. Either binary type, or
2470 :py:class:`pyderasn.OctetString` object
2471 :param bounds: set ``(MIN, MAX)`` value size constraint.
2472 (-inf, +inf) by default
2473 :param bytes impl: override default tag with ``IMPLICIT`` one
2474 :param bytes expl: override default tag with ``EXPLICIT`` one
2475 :param default: set default value. Type same as in ``value``
2476 :param bool optional: is object ``OPTIONAL`` in sequence
2478 super(OctetString, self).__init__(
2486 self._bound_min, self._bound_max = getattr(
2490 ) if bounds is None else bounds
2491 if value is not None:
2492 self._value = self._value_sanitize(value)
2493 if default is not None:
2494 default = self._value_sanitize(default)
2495 self.default = self.__class__(
2500 if self._value is None:
2501 self._value = default
2503 tag_klass, _, tag_num = tag_decode(self.tag)
2504 self.tag_constructed = tag_encode(
2506 form=TagFormConstructed,
2510 def _value_sanitize(self, value):
2511 if issubclass(value.__class__, OctetString):
2512 value = value._value
2513 elif isinstance(value, binary_type):
2516 raise InvalidValueType((self.__class__, bytes))
2517 if not self._bound_min <= len(value) <= self._bound_max:
2518 raise BoundsError(self._bound_min, len(value), self._bound_max)
2523 return self._value is not None
2526 obj = self.__class__()
2527 obj._value = self._value
2528 obj._bound_min = self._bound_min
2529 obj._bound_max = self._bound_max
2531 obj._expl = self._expl
2532 obj.default = self.default
2533 obj.optional = self.optional
2534 obj.offset = self.offset
2535 obj.llen = self.llen
2536 obj.vlen = self.vlen
2539 def __bytes__(self):
2540 self._assert_ready()
2543 def __eq__(self, their):
2544 if isinstance(their, binary_type):
2545 return self._value == their
2546 if not issubclass(their.__class__, OctetString):
2549 self._value == their._value and
2550 self.tag == their.tag and
2551 self._expl == their._expl
2554 def __lt__(self, their):
2555 return self._value < their._value
2566 return self.__class__(
2569 (self._bound_min, self._bound_max)
2570 if bounds is None else bounds
2572 impl=self.tag if impl is None else impl,
2573 expl=self._expl if expl is None else expl,
2574 default=self.default if default is None else default,
2575 optional=self.optional if optional is None else optional,
2579 self._assert_ready()
2582 len_encode(len(self._value)),
2586 def _decode_chunk(self, lv, offset, decode_path, ctx):
2588 l, llen, v = len_decode(lv)
2589 except DecodeError as err:
2590 raise err.__class__(
2592 klass=self.__class__,
2593 decode_path=decode_path,
2597 raise NotEnoughData(
2598 "encoded length is longer than data",
2599 klass=self.__class__,
2600 decode_path=decode_path,
2603 v, tail = v[:l], v[l:]
2605 obj = self.__class__(
2607 bounds=(self._bound_min, self._bound_max),
2610 default=self.default,
2611 optional=self.optional,
2612 _decoded=(offset, llen, l),
2614 except DecodeError as err:
2617 klass=self.__class__,
2618 decode_path=decode_path,
2621 except BoundsError as err:
2624 klass=self.__class__,
2625 decode_path=decode_path,
2630 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2632 t, tlen, lv = tag_strip(tlv)
2633 except DecodeError as err:
2634 raise err.__class__(
2636 klass=self.__class__,
2637 decode_path=decode_path,
2643 return self._decode_chunk(lv, offset, decode_path, ctx)
2644 if t == self.tag_constructed:
2645 if not ctx.get("bered", False):
2647 "unallowed BER constructed encoding",
2648 klass=self.__class__,
2649 decode_path=decode_path,
2656 l, llen, v = len_decode(lv)
2657 except LenIndefForm:
2658 llen, l, v = 1, 0, lv[1:]
2660 except DecodeError as err:
2661 raise err.__class__(
2663 klass=self.__class__,
2664 decode_path=decode_path,
2668 raise NotEnoughData(
2669 "encoded length is longer than data",
2670 klass=self.__class__,
2671 decode_path=decode_path,
2675 sub_offset = offset + tlen + llen
2679 if v[:EOC_LEN].tobytes() == EOC:
2686 "chunk out of bounds",
2687 klass=self.__class__,
2688 decode_path=decode_path + (str(len(chunks) - 1),),
2689 offset=chunks[-1].offset,
2691 sub_decode_path = decode_path + (str(len(chunks)),)
2693 chunk, v_tail = OctetString().decode(
2696 decode_path=sub_decode_path,
2702 "expected OctetString encoded chunk",
2703 klass=self.__class__,
2704 decode_path=sub_decode_path,
2707 chunks.append(chunk)
2708 sub_offset += chunk.tlvlen
2709 vlen += chunk.tlvlen
2712 obj = self.__class__(
2713 value=b"".join(bytes(chunk) for chunk in chunks),
2714 bounds=(self._bound_min, self._bound_max),
2717 default=self.default,
2718 optional=self.optional,
2719 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2721 except DecodeError as err:
2724 klass=self.__class__,
2725 decode_path=decode_path,
2728 except BoundsError as err:
2731 klass=self.__class__,
2732 decode_path=decode_path,
2735 obj.lenindef = lenindef
2737 return obj, (v[EOC_LEN:] if lenindef else v)
2739 klass=self.__class__,
2740 decode_path=decode_path,
2745 return pp_console_row(next(self.pps()))
2747 def pps(self, decode_path=()):
2749 asn1_type_name=self.asn1_type_name,
2750 obj_name=self.__class__.__name__,
2751 decode_path=decode_path,
2752 value=("%d bytes" % len(self._value)) if self.ready else None,
2753 blob=self._value if self.ready else None,
2754 optional=self.optional,
2755 default=self == self.default,
2756 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2757 expl=None if self._expl is None else tag_decode(self._expl),
2762 expl_offset=self.expl_offset if self.expled else None,
2763 expl_tlen=self.expl_tlen if self.expled else None,
2764 expl_llen=self.expl_llen if self.expled else None,
2765 expl_vlen=self.expl_vlen if self.expled else None,
2766 expl_lenindef=self.expl_lenindef,
2767 lenindef=self.lenindef,
2770 defined_by, defined = self.defined or (None, None)
2771 if defined_by is not None:
2773 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2775 for pp in self.pps_lenindef(decode_path):
2780 """``NULL`` null object
2788 tag_default = tag_encode(5)
2789 asn1_type_name = "NULL"
2793 value=None, # unused, but Sequence passes it
2800 :param bytes impl: override default tag with ``IMPLICIT`` one
2801 :param bytes expl: override default tag with ``EXPLICIT`` one
2802 :param bool optional: is object ``OPTIONAL`` in sequence
2804 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2812 obj = self.__class__()
2814 obj._expl = self._expl
2815 obj.default = self.default
2816 obj.optional = self.optional
2817 obj.offset = self.offset
2818 obj.llen = self.llen
2819 obj.vlen = self.vlen
2822 def __eq__(self, their):
2823 if not issubclass(their.__class__, Null):
2826 self.tag == their.tag and
2827 self._expl == their._expl
2837 return self.__class__(
2838 impl=self.tag if impl is None else impl,
2839 expl=self._expl if expl is None else expl,
2840 optional=self.optional if optional is None else optional,
2844 return self.tag + len_encode(0)
2846 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2848 t, _, lv = tag_strip(tlv)
2849 except DecodeError as err:
2850 raise err.__class__(
2852 klass=self.__class__,
2853 decode_path=decode_path,
2858 klass=self.__class__,
2859 decode_path=decode_path,
2865 l, _, v = len_decode(lv)
2866 except DecodeError as err:
2867 raise err.__class__(
2869 klass=self.__class__,
2870 decode_path=decode_path,
2874 raise InvalidLength(
2875 "Null must have zero length",
2876 klass=self.__class__,
2877 decode_path=decode_path,
2880 obj = self.__class__(
2883 optional=self.optional,
2884 _decoded=(offset, 1, 0),
2889 return pp_console_row(next(self.pps()))
2891 def pps(self, decode_path=()):
2893 asn1_type_name=self.asn1_type_name,
2894 obj_name=self.__class__.__name__,
2895 decode_path=decode_path,
2896 optional=self.optional,
2897 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2898 expl=None if self._expl is None else tag_decode(self._expl),
2903 expl_offset=self.expl_offset if self.expled else None,
2904 expl_tlen=self.expl_tlen if self.expled else None,
2905 expl_llen=self.expl_llen if self.expled else None,
2906 expl_vlen=self.expl_vlen if self.expled else None,
2907 expl_lenindef=self.expl_lenindef,
2909 for pp in self.pps_lenindef(decode_path):
2913 class ObjectIdentifier(Obj):
2914 """``OBJECT IDENTIFIER`` OID type
2916 >>> oid = ObjectIdentifier((1, 2, 3))
2917 OBJECT IDENTIFIER 1.2.3
2918 >>> oid == ObjectIdentifier("1.2.3")
2924 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2925 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2927 >>> str(ObjectIdentifier((3, 1)))
2928 Traceback (most recent call last):
2929 pyderasn.InvalidOID: unacceptable first arc value
2931 __slots__ = ("defines",)
2932 tag_default = tag_encode(6)
2933 asn1_type_name = "OBJECT IDENTIFIER"
2946 :param value: set the value. Either tuples of integers,
2947 string of "."-concatenated integers, or
2948 :py:class:`pyderasn.ObjectIdentifier` object
2949 :param defines: sequence of tuples. Each tuple has two elements.
2950 First one is relative to current one decode
2951 path, aiming to the field defined by that OID.
2952 Read about relative path in
2953 :py:func:`pyderasn.abs_decode_path`. Second
2954 tuple element is ``{OID: pyderasn.Obj()}``
2955 dictionary, mapping between current OID value
2956 and structure applied to defined field.
2957 :ref:`Read about DEFINED BY <definedby>`
2958 :param bytes impl: override default tag with ``IMPLICIT`` one
2959 :param bytes expl: override default tag with ``EXPLICIT`` one
2960 :param default: set default value. Type same as in ``value``
2961 :param bool optional: is object ``OPTIONAL`` in sequence
2963 super(ObjectIdentifier, self).__init__(
2971 if value is not None:
2972 self._value = self._value_sanitize(value)
2973 if default is not None:
2974 default = self._value_sanitize(default)
2975 self.default = self.__class__(
2980 if self._value is None:
2981 self._value = default
2982 self.defines = defines
2984 def __add__(self, their):
2985 if isinstance(their, self.__class__):
2986 return self.__class__(self._value + their._value)
2987 if isinstance(their, tuple):
2988 return self.__class__(self._value + their)
2989 raise InvalidValueType((self.__class__, tuple))
2991 def _value_sanitize(self, value):
2992 if issubclass(value.__class__, ObjectIdentifier):
2994 if isinstance(value, string_types):
2996 value = tuple(int(arc) for arc in value.split("."))
2998 raise InvalidOID("unacceptable arcs values")
2999 if isinstance(value, tuple):
3001 raise InvalidOID("less than 2 arcs")
3002 first_arc = value[0]
3003 if first_arc in (0, 1):
3004 if not (0 <= value[1] <= 39):
3005 raise InvalidOID("second arc is too wide")
3006 elif first_arc == 2:
3009 raise InvalidOID("unacceptable first arc value")
3011 raise InvalidValueType((self.__class__, str, tuple))
3015 return self._value is not None
3018 obj = self.__class__()
3019 obj._value = self._value
3020 obj.defines = self.defines
3022 obj._expl = self._expl
3023 obj.default = self.default
3024 obj.optional = self.optional
3025 obj.offset = self.offset
3026 obj.llen = self.llen
3027 obj.vlen = self.vlen
3031 self._assert_ready()
3032 return iter(self._value)
3035 return ".".join(str(arc) for arc in self._value or ())
3038 self._assert_ready()
3041 bytes(self._expl or b"") +
3042 str(self._value).encode("ascii"),
3045 def __eq__(self, their):
3046 if isinstance(their, tuple):
3047 return self._value == their
3048 if not issubclass(their.__class__, ObjectIdentifier):
3051 self.tag == their.tag and
3052 self._expl == their._expl and
3053 self._value == their._value
3056 def __lt__(self, their):
3057 return self._value < their._value
3068 return self.__class__(
3070 defines=self.defines if defines is None else defines,
3071 impl=self.tag if impl is None else impl,
3072 expl=self._expl if expl is None else expl,
3073 default=self.default if default is None else default,
3074 optional=self.optional if optional is None else optional,
3078 self._assert_ready()
3080 first_value = value[1]
3081 first_arc = value[0]
3084 elif first_arc == 1:
3086 elif first_arc == 2:
3088 else: # pragma: no cover
3089 raise RuntimeError("invalid arc is stored")
3090 octets = [zero_ended_encode(first_value)]
3091 for arc in value[2:]:
3092 octets.append(zero_ended_encode(arc))
3093 v = b"".join(octets)
3094 return b"".join((self.tag, len_encode(len(v)), v))
3096 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3098 t, _, lv = tag_strip(tlv)
3099 except DecodeError as err:
3100 raise err.__class__(
3102 klass=self.__class__,
3103 decode_path=decode_path,
3108 klass=self.__class__,
3109 decode_path=decode_path,
3115 l, llen, v = len_decode(lv)
3116 except DecodeError as err:
3117 raise err.__class__(
3119 klass=self.__class__,
3120 decode_path=decode_path,
3124 raise NotEnoughData(
3125 "encoded length is longer than data",
3126 klass=self.__class__,
3127 decode_path=decode_path,
3131 raise NotEnoughData(
3133 klass=self.__class__,
3134 decode_path=decode_path,
3137 v, tail = v[:l], v[l:]
3143 octet = indexbytes(v, i)
3144 arc = (arc << 7) | (octet & 0x7F)
3145 if octet & 0x80 == 0:
3153 klass=self.__class__,
3154 decode_path=decode_path,
3158 second_arc = arcs[0]
3159 if 0 <= second_arc <= 39:
3161 elif 40 <= second_arc <= 79:
3167 obj = self.__class__(
3168 value=tuple([first_arc, second_arc] + arcs[1:]),
3171 default=self.default,
3172 optional=self.optional,
3173 _decoded=(offset, llen, l),
3178 return pp_console_row(next(self.pps()))
3180 def pps(self, decode_path=()):
3182 asn1_type_name=self.asn1_type_name,
3183 obj_name=self.__class__.__name__,
3184 decode_path=decode_path,
3185 value=str(self) if self.ready else None,
3186 optional=self.optional,
3187 default=self == self.default,
3188 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3189 expl=None if self._expl is None else tag_decode(self._expl),
3194 expl_offset=self.expl_offset if self.expled else None,
3195 expl_tlen=self.expl_tlen if self.expled else None,
3196 expl_llen=self.expl_llen if self.expled else None,
3197 expl_vlen=self.expl_vlen if self.expled else None,
3198 expl_lenindef=self.expl_lenindef,
3200 for pp in self.pps_lenindef(decode_path):
3204 class Enumerated(Integer):
3205 """``ENUMERATED`` integer type
3207 This type is identical to :py:class:`pyderasn.Integer`, but requires
3208 schema to be specified and does not accept values missing from it.
3211 tag_default = tag_encode(10)
3212 asn1_type_name = "ENUMERATED"
3223 bounds=None, # dummy argument, workability for Integer.decode
3225 super(Enumerated, self).__init__(
3234 if len(self.specs) == 0:
3235 raise ValueError("schema must be specified")
3237 def _value_sanitize(self, value):
3238 if isinstance(value, self.__class__):
3239 value = value._value
3240 elif isinstance(value, integer_types):
3241 if value not in list(self.specs.values()):
3243 "unknown integer value: %s" % value,
3244 klass=self.__class__,
3246 elif isinstance(value, string_types):
3247 value = self.specs.get(value)
3249 raise ObjUnknown("integer value: %s" % value)
3251 raise InvalidValueType((self.__class__, int, str))
3255 obj = self.__class__(_specs=self.specs)
3256 obj._value = self._value
3257 obj._bound_min = self._bound_min
3258 obj._bound_max = self._bound_max
3260 obj._expl = self._expl
3261 obj.default = self.default
3262 obj.optional = self.optional
3263 obj.offset = self.offset
3264 obj.llen = self.llen
3265 obj.vlen = self.vlen
3277 return self.__class__(
3279 impl=self.tag if impl is None else impl,
3280 expl=self._expl if expl is None else expl,
3281 default=self.default if default is None else default,
3282 optional=self.optional if optional is None else optional,
3287 class CommonString(OctetString):
3288 """Common class for all strings
3290 Everything resembles :py:class:`pyderasn.OctetString`, except
3291 ability to deal with unicode text strings.
3293 >>> hexenc("привет мир".encode("utf-8"))
3294 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3295 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3297 >>> s = UTF8String("привет мир")
3298 UTF8String UTF8String привет мир
3300 'привет мир'
3301 >>> hexenc(bytes(s))
3302 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3304 >>> PrintableString("привет мир")
3305 Traceback (most recent call last):
3306 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3308 >>> BMPString("ада", bounds=(2, 2))
3309 Traceback (most recent call last):
3310 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3311 >>> s = BMPString("ад", bounds=(2, 2))
3314 >>> hexenc(bytes(s))
3322 * - :py:class:`pyderasn.UTF8String`
3324 * - :py:class:`pyderasn.NumericString`
3326 * - :py:class:`pyderasn.PrintableString`
3328 * - :py:class:`pyderasn.TeletexString`
3330 * - :py:class:`pyderasn.T61String`
3332 * - :py:class:`pyderasn.VideotexString`
3334 * - :py:class:`pyderasn.IA5String`
3336 * - :py:class:`pyderasn.GraphicString`
3338 * - :py:class:`pyderasn.VisibleString`
3340 * - :py:class:`pyderasn.ISO646String`
3342 * - :py:class:`pyderasn.GeneralString`
3344 * - :py:class:`pyderasn.UniversalString`
3346 * - :py:class:`pyderasn.BMPString`
3349 __slots__ = ("encoding",)
3351 def _value_sanitize(self, value):
3353 value_decoded = None
3354 if isinstance(value, self.__class__):
3355 value_raw = value._value
3356 elif isinstance(value, text_type):
3357 value_decoded = value
3358 elif isinstance(value, binary_type):
3361 raise InvalidValueType((self.__class__, text_type, binary_type))
3364 value_decoded.encode(self.encoding)
3365 if value_raw is None else value_raw
3368 value_raw.decode(self.encoding)
3369 if value_decoded is None else value_decoded
3371 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3372 raise DecodeError(str(err))
3373 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3381 def __eq__(self, their):
3382 if isinstance(their, binary_type):
3383 return self._value == their
3384 if isinstance(their, text_type):
3385 return self._value == their.encode(self.encoding)
3386 if not isinstance(their, self.__class__):
3389 self._value == their._value and
3390 self.tag == their.tag and
3391 self._expl == their._expl
3394 def __unicode__(self):
3396 return self._value.decode(self.encoding)
3397 return text_type(self._value)
3400 return pp_console_row(next(self.pps(no_unicode=PY2)))
3402 def pps(self, decode_path=(), no_unicode=False):
3405 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3407 asn1_type_name=self.asn1_type_name,
3408 obj_name=self.__class__.__name__,
3409 decode_path=decode_path,
3411 optional=self.optional,
3412 default=self == self.default,
3413 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3414 expl=None if self._expl is None else tag_decode(self._expl),
3419 expl_offset=self.expl_offset if self.expled else None,
3420 expl_tlen=self.expl_tlen if self.expled else None,
3421 expl_llen=self.expl_llen if self.expled else None,
3422 expl_vlen=self.expl_vlen if self.expled else None,
3423 expl_lenindef=self.expl_lenindef,
3425 for pp in self.pps_lenindef(decode_path):
3429 class UTF8String(CommonString):
3431 tag_default = tag_encode(12)
3433 asn1_type_name = "UTF8String"
3436 class NumericString(CommonString):
3439 Its value is properly sanitized: only ASCII digits can be stored.
3442 tag_default = tag_encode(18)
3444 asn1_type_name = "NumericString"
3445 allowable_chars = set(digits.encode("ascii"))
3447 def _value_sanitize(self, value):
3448 value = super(NumericString, self)._value_sanitize(value)
3449 if not set(value) <= self.allowable_chars:
3450 raise DecodeError("non-numeric value")
3454 class PrintableString(CommonString):
3456 tag_default = tag_encode(19)
3458 asn1_type_name = "PrintableString"
3461 class TeletexString(CommonString):
3463 tag_default = tag_encode(20)
3465 asn1_type_name = "TeletexString"
3468 class T61String(TeletexString):
3470 asn1_type_name = "T61String"
3473 class VideotexString(CommonString):
3475 tag_default = tag_encode(21)
3476 encoding = "iso-8859-1"
3477 asn1_type_name = "VideotexString"
3480 class IA5String(CommonString):
3482 tag_default = tag_encode(22)
3484 asn1_type_name = "IA5"
3487 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3488 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3489 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3492 class UTCTime(CommonString):
3493 """``UTCTime`` datetime type
3495 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3496 UTCTime UTCTime 2017-09-30T22:07:50
3502 datetime.datetime(2017, 9, 30, 22, 7, 50)
3503 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3504 datetime.datetime(1957, 9, 30, 22, 7, 50)
3507 tag_default = tag_encode(23)
3509 asn1_type_name = "UTCTime"
3511 fmt = "%y%m%d%H%M%SZ"
3521 bounds=None, # dummy argument, workability for OctetString.decode
3524 :param value: set the value. Either datetime type, or
3525 :py:class:`pyderasn.UTCTime` object
3526 :param bytes impl: override default tag with ``IMPLICIT`` one
3527 :param bytes expl: override default tag with ``EXPLICIT`` one
3528 :param default: set default value. Type same as in ``value``
3529 :param bool optional: is object ``OPTIONAL`` in sequence
3531 super(UTCTime, self).__init__(
3539 if value is not None:
3540 self._value = self._value_sanitize(value)
3541 if default is not None:
3542 default = self._value_sanitize(default)
3543 self.default = self.__class__(
3548 if self._value is None:
3549 self._value = default
3551 def _value_sanitize(self, value):
3552 if isinstance(value, self.__class__):
3554 if isinstance(value, datetime):
3555 return value.strftime(self.fmt).encode("ascii")
3556 if isinstance(value, binary_type):
3558 value_decoded = value.decode("ascii")
3559 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3560 raise DecodeError("invalid UTCTime encoding")
3561 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3563 datetime.strptime(value_decoded, self.fmt)
3564 except (TypeError, ValueError):
3565 raise DecodeError("invalid UTCTime format")
3568 raise DecodeError("invalid UTCTime length")
3569 raise InvalidValueType((self.__class__, datetime))
3571 def __eq__(self, their):
3572 if isinstance(their, binary_type):
3573 return self._value == their
3574 if isinstance(their, datetime):
3575 return self.todatetime() == their
3576 if not isinstance(their, self.__class__):
3579 self._value == their._value and
3580 self.tag == their.tag and
3581 self._expl == their._expl
3584 def todatetime(self):
3585 """Convert to datetime
3589 Pay attention that UTCTime can not hold full year, so all years
3590 having < 50 years are treated as 20xx, 19xx otherwise, according
3591 to X.509 recomendation.
3593 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3594 year = value.year % 100
3596 year=(2000 + year) if year < 50 else (1900 + year),
3600 minute=value.minute,
3601 second=value.second,
3605 return pp_console_row(next(self.pps()))
3607 def pps(self, decode_path=()):
3609 asn1_type_name=self.asn1_type_name,
3610 obj_name=self.__class__.__name__,
3611 decode_path=decode_path,
3612 value=self.todatetime().isoformat() if self.ready else None,
3613 optional=self.optional,
3614 default=self == self.default,
3615 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3616 expl=None if self._expl is None else tag_decode(self._expl),
3621 expl_offset=self.expl_offset if self.expled else None,
3622 expl_tlen=self.expl_tlen if self.expled else None,
3623 expl_llen=self.expl_llen if self.expled else None,
3624 expl_vlen=self.expl_vlen if self.expled else None,
3625 expl_lenindef=self.expl_lenindef,
3627 for pp in self.pps_lenindef(decode_path):
3631 class GeneralizedTime(UTCTime):
3632 """``GeneralizedTime`` datetime type
3634 This type is similar to :py:class:`pyderasn.UTCTime`.
3636 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3637 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3639 '20170930220750.000123Z'
3640 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3641 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3644 tag_default = tag_encode(24)
3645 asn1_type_name = "GeneralizedTime"
3647 fmt = "%Y%m%d%H%M%SZ"
3648 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3650 def _value_sanitize(self, value):
3651 if isinstance(value, self.__class__):
3653 if isinstance(value, datetime):
3654 return value.strftime(
3655 self.fmt_ms if value.microsecond > 0 else self.fmt
3657 if isinstance(value, binary_type):
3659 value_decoded = value.decode("ascii")
3660 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3661 raise DecodeError("invalid GeneralizedTime encoding")
3662 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3664 datetime.strptime(value_decoded, self.fmt)
3665 except (TypeError, ValueError):
3667 "invalid GeneralizedTime (without ms) format",
3670 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3672 datetime.strptime(value_decoded, self.fmt_ms)
3673 except (TypeError, ValueError):
3675 "invalid GeneralizedTime (with ms) format",
3680 "invalid GeneralizedTime length",
3681 klass=self.__class__,
3683 raise InvalidValueType((self.__class__, datetime))
3685 def todatetime(self):
3686 value = self._value.decode("ascii")
3687 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3688 return datetime.strptime(value, self.fmt)
3689 return datetime.strptime(value, self.fmt_ms)
3692 class GraphicString(CommonString):
3694 tag_default = tag_encode(25)
3695 encoding = "iso-8859-1"
3696 asn1_type_name = "GraphicString"
3699 class VisibleString(CommonString):
3701 tag_default = tag_encode(26)
3703 asn1_type_name = "VisibleString"
3706 class ISO646String(VisibleString):
3708 asn1_type_name = "ISO646String"
3711 class GeneralString(CommonString):
3713 tag_default = tag_encode(27)
3714 encoding = "iso-8859-1"
3715 asn1_type_name = "GeneralString"
3718 class UniversalString(CommonString):
3720 tag_default = tag_encode(28)
3721 encoding = "utf-32-be"
3722 asn1_type_name = "UniversalString"
3725 class BMPString(CommonString):
3727 tag_default = tag_encode(30)
3728 encoding = "utf-16-be"
3729 asn1_type_name = "BMPString"
3733 """``CHOICE`` special type
3737 class GeneralName(Choice):
3739 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3740 ("dNSName", IA5String(impl=tag_ctxp(2))),
3743 >>> gn = GeneralName()
3745 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3746 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3747 >>> gn["dNSName"] = IA5String("bar.baz")
3748 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3749 >>> gn["rfc822Name"]
3752 [2] IA5String IA5 bar.baz
3755 >>> gn.value == gn["dNSName"]
3758 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3760 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3761 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3763 __slots__ = ("specs",)
3765 asn1_type_name = "CHOICE"
3778 :param value: set the value. Either ``(choice, value)`` tuple, or
3779 :py:class:`pyderasn.Choice` object
3780 :param bytes impl: can not be set, do **not** use it
3781 :param bytes expl: override default tag with ``EXPLICIT`` one
3782 :param default: set default value. Type same as in ``value``
3783 :param bool optional: is object ``OPTIONAL`` in sequence
3785 if impl is not None:
3786 raise ValueError("no implicit tag allowed for CHOICE")
3787 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3789 schema = getattr(self, "schema", ())
3790 if len(schema) == 0:
3791 raise ValueError("schema must be specified")
3793 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3796 if value is not None:
3797 self._value = self._value_sanitize(value)
3798 if default is not None:
3799 default_value = self._value_sanitize(default)
3800 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3801 default_obj.specs = self.specs
3802 default_obj._value = default_value
3803 self.default = default_obj
3805 self._value = default_obj.copy()._value
3807 def _value_sanitize(self, value):
3808 if isinstance(value, self.__class__):
3810 if isinstance(value, tuple) and len(value) == 2:
3812 spec = self.specs.get(choice)
3814 raise ObjUnknown(choice)
3815 if not isinstance(obj, spec.__class__):
3816 raise InvalidValueType((spec,))
3817 return (choice, spec(obj))
3818 raise InvalidValueType((self.__class__, tuple))
3822 return self._value is not None and self._value[1].ready
3825 obj = self.__class__(schema=self.specs)
3826 obj._expl = self._expl
3827 obj.default = self.default
3828 obj.optional = self.optional
3829 obj.offset = self.offset
3830 obj.llen = self.llen
3831 obj.vlen = self.vlen
3833 if value is not None:
3834 obj._value = (value[0], value[1].copy())
3837 def __eq__(self, their):
3838 if isinstance(their, tuple) and len(their) == 2:
3839 return self._value == their
3840 if not isinstance(their, self.__class__):
3843 self.specs == their.specs and
3844 self._value == their._value
3854 return self.__class__(
3857 expl=self._expl if expl is None else expl,
3858 default=self.default if default is None else default,
3859 optional=self.optional if optional is None else optional,
3864 self._assert_ready()
3865 return self._value[0]
3869 self._assert_ready()
3870 return self._value[1]
3872 def __getitem__(self, key):
3873 if key not in self.specs:
3874 raise ObjUnknown(key)
3875 if self._value is None:
3877 choice, value = self._value
3882 def __setitem__(self, key, value):
3883 spec = self.specs.get(key)
3885 raise ObjUnknown(key)
3886 if not isinstance(value, spec.__class__):
3887 raise InvalidValueType((spec.__class__,))
3888 self._value = (key, spec(value))
3896 return self._value[1].decoded if self.ready else False
3899 self._assert_ready()
3900 return self._value[1].encode()
3902 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3903 for choice, spec in self.specs.items():
3904 sub_decode_path = decode_path + (choice,)
3910 decode_path=sub_decode_path,
3919 klass=self.__class__,
3920 decode_path=decode_path,
3925 value, tail = spec.decode(
3929 decode_path=sub_decode_path,
3932 obj = self.__class__(
3935 default=self.default,
3936 optional=self.optional,
3937 _decoded=(offset, 0, value.fulllen),
3939 obj._value = (choice, value)
3943 value = pp_console_row(next(self.pps()))
3945 value = "%s[%r]" % (value, self.value)
3948 def pps(self, decode_path=()):
3950 asn1_type_name=self.asn1_type_name,
3951 obj_name=self.__class__.__name__,
3952 decode_path=decode_path,
3953 value=self.choice if self.ready else None,
3954 optional=self.optional,
3955 default=self == self.default,
3956 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3957 expl=None if self._expl is None else tag_decode(self._expl),
3962 expl_lenindef=self.expl_lenindef,
3965 yield self.value.pps(decode_path=decode_path + (self.choice,))
3966 for pp in self.pps_lenindef(decode_path):
3970 class PrimitiveTypes(Choice):
3971 """Predefined ``CHOICE`` for all generic primitive types
3973 It could be useful for general decoding of some unspecified values:
3975 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3976 OCTET STRING 3 bytes 666f6f
3977 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3981 schema = tuple((klass.__name__, klass()) for klass in (
4006 """``ANY`` special type
4008 >>> Any(Integer(-123))
4010 >>> a = Any(OctetString(b"hello world").encode())
4011 ANY 040b68656c6c6f20776f726c64
4012 >>> hexenc(bytes(a))
4013 b'0x040x0bhello world'
4015 __slots__ = ("defined",)
4016 tag_default = tag_encode(0)
4017 asn1_type_name = "ANY"
4027 :param value: set the value. Either any kind of pyderasn's
4028 **ready** object, or bytes. Pay attention that
4029 **no** validation is performed is raw binary value
4031 :param bytes expl: override default tag with ``EXPLICIT`` one
4032 :param bool optional: is object ``OPTIONAL`` in sequence
4034 super(Any, self).__init__(None, expl, None, optional, _decoded)
4035 self._value = None if value is None else self._value_sanitize(value)
4038 def _value_sanitize(self, value):
4039 if isinstance(value, self.__class__):
4041 if isinstance(value, Obj):
4042 return value.encode()
4043 if isinstance(value, binary_type):
4045 raise InvalidValueType((self.__class__, Obj, binary_type))
4049 return self._value is not None
4052 obj = self.__class__()
4053 obj._value = self._value
4055 obj._expl = self._expl
4056 obj.optional = self.optional
4057 obj.offset = self.offset
4058 obj.llen = self.llen
4059 obj.vlen = self.vlen
4062 def __eq__(self, their):
4063 if isinstance(their, binary_type):
4064 return self._value == their
4065 if issubclass(their.__class__, Any):
4066 return self._value == their._value
4075 return self.__class__(
4077 expl=self._expl if expl is None else expl,
4078 optional=self.optional if optional is None else optional,
4081 def __bytes__(self):
4082 self._assert_ready()
4090 self._assert_ready()
4093 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4095 t, tlen, lv = tag_strip(tlv)
4096 except DecodeError as err:
4097 raise err.__class__(
4099 klass=self.__class__,
4100 decode_path=decode_path,
4104 l, llen, v = len_decode(lv)
4105 except LenIndefForm as err:
4106 if not ctx.get("bered", False):
4107 raise err.__class__(
4109 klass=self.__class__,
4110 decode_path=decode_path,
4113 llen, vlen, v = 1, 0, lv[1:]
4114 sub_offset = offset + tlen + llen
4116 while v[:EOC_LEN].tobytes() != EOC:
4117 chunk, v = Any().decode(
4120 decode_path=decode_path + (str(chunk_i),),
4124 vlen += chunk.tlvlen
4125 sub_offset += chunk.tlvlen
4127 tlvlen = tlen + llen + vlen + EOC_LEN
4128 obj = self.__class__(
4129 value=tlv[:tlvlen].tobytes(),
4131 optional=self.optional,
4132 _decoded=(offset, 0, tlvlen),
4136 return obj, v[EOC_LEN:]
4137 except DecodeError as err:
4138 raise err.__class__(
4140 klass=self.__class__,
4141 decode_path=decode_path,
4145 raise NotEnoughData(
4146 "encoded length is longer than data",
4147 klass=self.__class__,
4148 decode_path=decode_path,
4151 tlvlen = tlen + llen + l
4152 v, tail = tlv[:tlvlen], v[l:]
4153 obj = self.__class__(
4156 optional=self.optional,
4157 _decoded=(offset, 0, tlvlen),
4163 return pp_console_row(next(self.pps()))
4165 def pps(self, decode_path=()):
4167 asn1_type_name=self.asn1_type_name,
4168 obj_name=self.__class__.__name__,
4169 decode_path=decode_path,
4170 blob=self._value if self.ready else None,
4171 optional=self.optional,
4172 default=self == self.default,
4173 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4174 expl=None if self._expl is None else tag_decode(self._expl),
4179 expl_offset=self.expl_offset if self.expled else None,
4180 expl_tlen=self.expl_tlen if self.expled else None,
4181 expl_llen=self.expl_llen if self.expled else None,
4182 expl_vlen=self.expl_vlen if self.expled else None,
4183 expl_lenindef=self.expl_lenindef,
4184 lenindef=self.lenindef,
4186 defined_by, defined = self.defined or (None, None)
4187 if defined_by is not None:
4189 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4191 for pp in self.pps_lenindef(decode_path):
4195 ########################################################################
4196 # ASN.1 constructed types
4197 ########################################################################
4199 def get_def_by_path(defines_by_path, sub_decode_path):
4200 """Get define by decode path
4202 for path, define in defines_by_path:
4203 if len(path) != len(sub_decode_path):
4205 for p1, p2 in zip(path, sub_decode_path):
4206 if (p1 != any) and (p1 != p2):
4212 def abs_decode_path(decode_path, rel_path):
4213 """Create an absolute decode path from current and relative ones
4215 :param decode_path: current decode path, starting point.
4217 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4218 If first tuple's element is "/", then treat it as
4219 an absolute path, ignoring ``decode_path`` as
4220 starting point. Also this tuple can contain ".."
4221 elements, stripping the leading element from
4224 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4225 ("foo", "bar", "baz", "whatever")
4226 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4228 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4231 if rel_path[0] == "/":
4233 if rel_path[0] == "..":
4234 return abs_decode_path(decode_path[:-1], rel_path[1:])
4235 return decode_path + rel_path
4238 class Sequence(Obj):
4239 """``SEQUENCE`` structure type
4241 You have to make specification of sequence::
4243 class Extension(Sequence):
4245 ("extnID", ObjectIdentifier()),
4246 ("critical", Boolean(default=False)),
4247 ("extnValue", OctetString()),
4250 Then, you can work with it as with dictionary.
4252 >>> ext = Extension()
4253 >>> Extension().specs
4255 ('extnID', OBJECT IDENTIFIER),
4256 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4257 ('extnValue', OCTET STRING),
4259 >>> ext["extnID"] = "1.2.3"
4260 Traceback (most recent call last):
4261 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4262 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4264 You can determine if sequence is ready to be encoded:
4269 Traceback (most recent call last):
4270 pyderasn.ObjNotReady: object is not ready: extnValue
4271 >>> ext["extnValue"] = OctetString(b"foobar")
4275 Value you want to assign, must have the same **type** as in
4276 corresponding specification, but it can have different tags,
4277 optional/default attributes -- they will be taken from specification
4280 class TBSCertificate(Sequence):
4282 ("version", Version(expl=tag_ctxc(0), default="v1")),
4285 >>> tbs = TBSCertificate()
4286 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4288 Assign ``None`` to remove value from sequence.
4290 You can set values in Sequence during its initialization:
4292 >>> AlgorithmIdentifier((
4293 ("algorithm", ObjectIdentifier("1.2.3")),
4294 ("parameters", Any(Null()))
4296 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4298 You can determine if value exists/set in the sequence and take its value:
4300 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4303 OBJECT IDENTIFIER 1.2.3
4305 But pay attention that if value has default, then it won't be (not
4306 in) in the sequence (because ``DEFAULT`` must not be encoded in
4307 DER), but you can read its value:
4309 >>> "critical" in ext, ext["critical"]
4310 (False, BOOLEAN False)
4311 >>> ext["critical"] = Boolean(True)
4312 >>> "critical" in ext, ext["critical"]
4313 (True, BOOLEAN True)
4315 All defaulted values are always optional.
4317 .. _strict_default_existence_ctx:
4321 When decoded DER contains defaulted value inside, then
4322 technically this is not valid DER encoding. But we allow and pass
4323 it **by default**. Of course reencoding of that kind of DER will
4324 result in different binary representation (validly without
4325 defaulted value inside). You can enable strict defaulted values
4326 existence validation by setting ``"strict_default_existence":
4327 True`` :ref:`context <ctx>` option -- decoding process will raise
4328 an exception if defaulted value is met.
4330 Two sequences are equal if they have equal specification (schema),
4331 implicit/explicit tagging and the same values.
4333 __slots__ = ("specs",)
4334 tag_default = tag_encode(form=TagFormConstructed, num=16)
4335 asn1_type_name = "SEQUENCE"
4347 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4349 schema = getattr(self, "schema", ())
4351 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4354 if value is not None:
4355 if issubclass(value.__class__, Sequence):
4356 self._value = value._value
4357 elif hasattr(value, "__iter__"):
4358 for seq_key, seq_value in value:
4359 self[seq_key] = seq_value
4361 raise InvalidValueType((Sequence,))
4362 if default is not None:
4363 if not issubclass(default.__class__, Sequence):
4364 raise InvalidValueType((Sequence,))
4365 default_value = default._value
4366 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4367 default_obj.specs = self.specs
4368 default_obj._value = default_value
4369 self.default = default_obj
4371 self._value = default_obj.copy()._value
4375 for name, spec in self.specs.items():
4376 value = self._value.get(name)
4387 obj = self.__class__(schema=self.specs)
4389 obj._expl = self._expl
4390 obj.default = self.default
4391 obj.optional = self.optional
4392 obj.offset = self.offset
4393 obj.llen = self.llen
4394 obj.vlen = self.vlen
4395 obj._value = {k: v.copy() for k, v in self._value.items()}
4398 def __eq__(self, their):
4399 if not isinstance(their, self.__class__):
4402 self.specs == their.specs and
4403 self.tag == their.tag and
4404 self._expl == their._expl and
4405 self._value == their._value
4416 return self.__class__(
4419 impl=self.tag if impl is None else impl,
4420 expl=self._expl if expl is None else expl,
4421 default=self.default if default is None else default,
4422 optional=self.optional if optional is None else optional,
4425 def __contains__(self, key):
4426 return key in self._value
4428 def __setitem__(self, key, value):
4429 spec = self.specs.get(key)
4431 raise ObjUnknown(key)
4433 self._value.pop(key, None)
4435 if not isinstance(value, spec.__class__):
4436 raise InvalidValueType((spec.__class__,))
4437 value = spec(value=value)
4438 if spec.default is not None and value == spec.default:
4439 self._value.pop(key, None)
4441 self._value[key] = value
4443 def __getitem__(self, key):
4444 value = self._value.get(key)
4445 if value is not None:
4447 spec = self.specs.get(key)
4449 raise ObjUnknown(key)
4450 if spec.default is not None:
4454 def _encoded_values(self):
4456 for name, spec in self.specs.items():
4457 value = self._value.get(name)
4461 raise ObjNotReady(name)
4462 raws.append(value.encode())
4466 v = b"".join(self._encoded_values())
4467 return b"".join((self.tag, len_encode(len(v)), v))
4469 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4471 t, tlen, lv = tag_strip(tlv)
4472 except DecodeError as err:
4473 raise err.__class__(
4475 klass=self.__class__,
4476 decode_path=decode_path,
4481 klass=self.__class__,
4482 decode_path=decode_path,
4489 l, llen, v = len_decode(lv)
4490 except LenIndefForm as err:
4491 if not ctx.get("bered", False):
4492 raise err.__class__(
4494 klass=self.__class__,
4495 decode_path=decode_path,
4498 l, llen, v = 0, 1, lv[1:]
4500 except DecodeError as err:
4501 raise err.__class__(
4503 klass=self.__class__,
4504 decode_path=decode_path,
4508 raise NotEnoughData(
4509 "encoded length is longer than data",
4510 klass=self.__class__,
4511 decode_path=decode_path,
4515 v, tail = v[:l], v[l:]
4517 sub_offset = offset + tlen + llen
4519 for name, spec in self.specs.items():
4520 if spec.optional and (
4521 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4525 sub_decode_path = decode_path + (name,)
4527 value, v_tail = spec.decode(
4531 decode_path=sub_decode_path,
4539 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4540 if defined is not None:
4541 defined_by, defined_spec = defined
4542 if issubclass(value.__class__, SequenceOf):
4543 for i, _value in enumerate(value):
4544 sub_sub_decode_path = sub_decode_path + (
4546 DecodePathDefBy(defined_by),
4548 defined_value, defined_tail = defined_spec.decode(
4549 memoryview(bytes(_value)),
4551 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4552 if value.expled else (value.tlen + value.llen)
4555 decode_path=sub_sub_decode_path,
4558 if len(defined_tail) > 0:
4561 klass=self.__class__,
4562 decode_path=sub_sub_decode_path,
4565 _value.defined = (defined_by, defined_value)
4567 defined_value, defined_tail = defined_spec.decode(
4568 memoryview(bytes(value)),
4570 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4571 if value.expled else (value.tlen + value.llen)
4574 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4577 if len(defined_tail) > 0:
4580 klass=self.__class__,
4581 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4584 value.defined = (defined_by, defined_value)
4586 value_len = value.fulllen
4588 sub_offset += value_len
4590 if spec.default is not None and value == spec.default:
4591 if ctx.get("strict_default_existence", False):
4593 "DEFAULT value met",
4594 klass=self.__class__,
4595 decode_path=sub_decode_path,
4600 values[name] = value
4602 spec_defines = getattr(spec, "defines", ())
4603 if len(spec_defines) == 0:
4604 defines_by_path = ctx.get("defines_by_path", ())
4605 if len(defines_by_path) > 0:
4606 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4607 if spec_defines is not None and len(spec_defines) > 0:
4608 for rel_path, schema in spec_defines:
4609 defined = schema.get(value, None)
4610 if defined is not None:
4611 ctx.setdefault("_defines", []).append((
4612 abs_decode_path(sub_decode_path[:-1], rel_path),
4616 if v[:EOC_LEN].tobytes() != EOC:
4619 klass=self.__class__,
4620 decode_path=decode_path,
4628 klass=self.__class__,
4629 decode_path=decode_path,
4632 obj = self.__class__(
4636 default=self.default,
4637 optional=self.optional,
4638 _decoded=(offset, llen, vlen),
4641 obj.lenindef = lenindef
4645 value = pp_console_row(next(self.pps()))
4647 for name in self.specs:
4648 _value = self._value.get(name)
4651 cols.append("%s: %s" % (name, repr(_value)))
4652 return "%s[%s]" % (value, "; ".join(cols))
4654 def pps(self, decode_path=()):
4656 asn1_type_name=self.asn1_type_name,
4657 obj_name=self.__class__.__name__,
4658 decode_path=decode_path,
4659 optional=self.optional,
4660 default=self == self.default,
4661 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4662 expl=None if self._expl is None else tag_decode(self._expl),
4667 expl_offset=self.expl_offset if self.expled else None,
4668 expl_tlen=self.expl_tlen if self.expled else None,
4669 expl_llen=self.expl_llen if self.expled else None,
4670 expl_vlen=self.expl_vlen if self.expled else None,
4671 expl_lenindef=self.expl_lenindef,
4672 lenindef=self.lenindef,
4674 for name in self.specs:
4675 value = self._value.get(name)
4678 yield value.pps(decode_path=decode_path + (name,))
4679 for pp in self.pps_lenindef(decode_path):
4683 class Set(Sequence):
4684 """``SET`` structure type
4686 Its usage is identical to :py:class:`pyderasn.Sequence`.
4689 tag_default = tag_encode(form=TagFormConstructed, num=17)
4690 asn1_type_name = "SET"
4693 raws = self._encoded_values()
4696 return b"".join((self.tag, len_encode(len(v)), v))
4698 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4700 t, tlen, lv = tag_strip(tlv)
4701 except DecodeError as err:
4702 raise err.__class__(
4704 klass=self.__class__,
4705 decode_path=decode_path,
4710 klass=self.__class__,
4711 decode_path=decode_path,
4718 l, llen, v = len_decode(lv)
4719 except LenIndefForm as err:
4720 if not ctx.get("bered", False):
4721 raise err.__class__(
4723 klass=self.__class__,
4724 decode_path=decode_path,
4727 l, llen, v = 0, 1, lv[1:]
4729 except DecodeError as err:
4730 raise err.__class__(
4732 klass=self.__class__,
4733 decode_path=decode_path,
4737 raise NotEnoughData(
4738 "encoded length is longer than data",
4739 klass=self.__class__,
4743 v, tail = v[:l], v[l:]
4745 sub_offset = offset + tlen + llen
4747 specs_items = self.specs.items
4749 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4751 for name, spec in specs_items():
4752 sub_decode_path = decode_path + (name,)
4758 decode_path=sub_decode_path,
4767 klass=self.__class__,
4768 decode_path=decode_path,
4771 value, v_tail = spec.decode(
4775 decode_path=sub_decode_path,
4778 value_len = value.fulllen
4779 sub_offset += value_len
4782 if spec.default is None or value != spec.default: # pragma: no cover
4783 # SeqMixing.test_encoded_default_accepted covers that place
4784 values[name] = value
4785 obj = self.__class__(
4789 default=self.default,
4790 optional=self.optional,
4791 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4795 if v[:EOC_LEN].tobytes() != EOC:
4798 klass=self.__class__,
4799 decode_path=decode_path,
4806 "not all values are ready",
4807 klass=self.__class__,
4808 decode_path=decode_path,
4814 class SequenceOf(Obj):
4815 """``SEQUENCE OF`` sequence type
4817 For that kind of type you must specify the object it will carry on
4818 (bounds are for example here, not required)::
4820 class Ints(SequenceOf):
4825 >>> ints.append(Integer(123))
4826 >>> ints.append(Integer(234))
4828 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4829 >>> [int(i) for i in ints]
4831 >>> ints.append(Integer(345))
4832 Traceback (most recent call last):
4833 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4836 >>> ints[1] = Integer(345)
4838 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4840 Also you can initialize sequence with preinitialized values:
4842 >>> ints = Ints([Integer(123), Integer(234)])
4844 __slots__ = ("spec", "_bound_min", "_bound_max")
4845 tag_default = tag_encode(form=TagFormConstructed, num=16)
4846 asn1_type_name = "SEQUENCE OF"
4859 super(SequenceOf, self).__init__(
4867 schema = getattr(self, "schema", None)
4869 raise ValueError("schema must be specified")
4871 self._bound_min, self._bound_max = getattr(
4875 ) if bounds is None else bounds
4877 if value is not None:
4878 self._value = self._value_sanitize(value)
4879 if default is not None:
4880 default_value = self._value_sanitize(default)
4881 default_obj = self.__class__(
4886 default_obj._value = default_value
4887 self.default = default_obj
4889 self._value = default_obj.copy()._value
4891 def _value_sanitize(self, value):
4892 if issubclass(value.__class__, SequenceOf):
4893 value = value._value
4894 elif hasattr(value, "__iter__"):
4897 raise InvalidValueType((self.__class__, iter))
4898 if not self._bound_min <= len(value) <= self._bound_max:
4899 raise BoundsError(self._bound_min, len(value), self._bound_max)
4901 if not isinstance(v, self.spec.__class__):
4902 raise InvalidValueType((self.spec.__class__,))
4907 return all(v.ready for v in self._value)
4910 obj = self.__class__(schema=self.spec)
4911 obj._bound_min = self._bound_min
4912 obj._bound_max = self._bound_max
4914 obj._expl = self._expl
4915 obj.default = self.default
4916 obj.optional = self.optional
4917 obj.offset = self.offset
4918 obj.llen = self.llen
4919 obj.vlen = self.vlen
4920 obj._value = [v.copy() for v in self._value]
4923 def __eq__(self, their):
4924 if isinstance(their, self.__class__):
4926 self.spec == their.spec and
4927 self.tag == their.tag and
4928 self._expl == their._expl and
4929 self._value == their._value
4931 if hasattr(their, "__iter__"):
4932 return self._value == list(their)
4944 return self.__class__(
4948 (self._bound_min, self._bound_max)
4949 if bounds is None else bounds
4951 impl=self.tag if impl is None else impl,
4952 expl=self._expl if expl is None else expl,
4953 default=self.default if default is None else default,
4954 optional=self.optional if optional is None else optional,
4957 def __contains__(self, key):
4958 return key in self._value
4960 def append(self, value):
4961 if not isinstance(value, self.spec.__class__):
4962 raise InvalidValueType((self.spec.__class__,))
4963 if len(self._value) + 1 > self._bound_max:
4966 len(self._value) + 1,
4969 self._value.append(value)
4972 self._assert_ready()
4973 return iter(self._value)
4976 self._assert_ready()
4977 return len(self._value)
4979 def __setitem__(self, key, value):
4980 if not isinstance(value, self.spec.__class__):
4981 raise InvalidValueType((self.spec.__class__,))
4982 self._value[key] = self.spec(value=value)
4984 def __getitem__(self, key):
4985 return self._value[key]
4987 def _encoded_values(self):
4988 return [v.encode() for v in self._value]
4991 v = b"".join(self._encoded_values())
4992 return b"".join((self.tag, len_encode(len(v)), v))
4994 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4996 t, tlen, lv = tag_strip(tlv)
4997 except DecodeError as err:
4998 raise err.__class__(
5000 klass=self.__class__,
5001 decode_path=decode_path,
5006 klass=self.__class__,
5007 decode_path=decode_path,
5014 l, llen, v = len_decode(lv)
5015 except LenIndefForm as err:
5016 if not ctx.get("bered", False):
5017 raise err.__class__(
5019 klass=self.__class__,
5020 decode_path=decode_path,
5023 l, llen, v = 0, 1, lv[1:]
5025 except DecodeError as err:
5026 raise err.__class__(
5028 klass=self.__class__,
5029 decode_path=decode_path,
5033 raise NotEnoughData(
5034 "encoded length is longer than data",
5035 klass=self.__class__,
5036 decode_path=decode_path,
5040 v, tail = v[:l], v[l:]
5042 sub_offset = offset + tlen + llen
5046 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5048 value, v_tail = spec.decode(
5052 decode_path=decode_path + (str(len(_value)),),
5055 value_len = value.fulllen
5056 sub_offset += value_len
5059 _value.append(value)
5061 obj = self.__class__(
5064 bounds=(self._bound_min, self._bound_max),
5067 default=self.default,
5068 optional=self.optional,
5069 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5071 except BoundsError as err:
5074 klass=self.__class__,
5075 decode_path=decode_path,
5079 if v[:EOC_LEN].tobytes() != EOC:
5082 klass=self.__class__,
5083 decode_path=decode_path,
5092 pp_console_row(next(self.pps())),
5093 ", ".join(repr(v) for v in self._value),
5096 def pps(self, decode_path=()):
5098 asn1_type_name=self.asn1_type_name,
5099 obj_name=self.__class__.__name__,
5100 decode_path=decode_path,
5101 optional=self.optional,
5102 default=self == self.default,
5103 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5104 expl=None if self._expl is None else tag_decode(self._expl),
5109 expl_offset=self.expl_offset if self.expled else None,
5110 expl_tlen=self.expl_tlen if self.expled else None,
5111 expl_llen=self.expl_llen if self.expled else None,
5112 expl_vlen=self.expl_vlen if self.expled else None,
5113 expl_lenindef=self.expl_lenindef,
5114 lenindef=self.lenindef,
5116 for i, value in enumerate(self._value):
5117 yield value.pps(decode_path=decode_path + (str(i),))
5118 for pp in self.pps_lenindef(decode_path):
5122 class SetOf(SequenceOf):
5123 """``SET OF`` sequence type
5125 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5128 tag_default = tag_encode(form=TagFormConstructed, num=17)
5129 asn1_type_name = "SET OF"
5132 raws = self._encoded_values()
5135 return b"".join((self.tag, len_encode(len(v)), v))
5138 def obj_by_path(pypath): # pragma: no cover
5139 """Import object specified as string Python path
5141 Modules must be separated from classes/functions with ``:``.
5143 >>> obj_by_path("foo.bar:Baz")
5144 <class 'foo.bar.Baz'>
5145 >>> obj_by_path("foo.bar:Baz.boo")
5146 <classmethod 'foo.bar.Baz.boo'>
5148 mod, objs = pypath.rsplit(":", 1)
5149 from importlib import import_module
5150 obj = import_module(mod)
5151 for obj_name in objs.split("."):
5152 obj = getattr(obj, obj_name)
5156 def generic_decoder(): # pragma: no cover
5157 # All of this below is a big hack with self references
5158 choice = PrimitiveTypes()
5159 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5160 choice.specs["SetOf"] = SetOf(schema=choice)
5162 choice.specs["SequenceOf%d" % i] = SequenceOf(
5166 choice.specs["Any"] = Any()
5168 # Class name equals to type name, to omit it from output
5169 class SEQUENCEOF(SequenceOf):
5177 with_decode_path=False,
5178 decode_path_only=(),
5180 def _pprint_pps(pps):
5182 if hasattr(pp, "_fields"):
5184 decode_path_only != () and
5185 pp.decode_path[:len(decode_path_only)] != decode_path_only
5188 if pp.asn1_type_name == Choice.asn1_type_name:
5190 pp_kwargs = pp._asdict()
5191 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5192 pp = _pp(**pp_kwargs)
5193 yield pp_console_row(
5198 with_colours=with_colours,
5199 with_decode_path=with_decode_path,
5200 decode_path_len_decrease=len(decode_path_only),
5202 for row in pp_console_blob(
5204 decode_path_len_decrease=len(decode_path_only),
5208 for row in _pprint_pps(pp):
5210 return "\n".join(_pprint_pps(obj.pps()))
5211 return SEQUENCEOF(), pprint_any
5214 def main(): # pragma: no cover
5216 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5217 parser.add_argument(
5221 help="Skip that number of bytes from the beginning",
5223 parser.add_argument(
5225 help="Python path to dictionary with OIDs",
5227 parser.add_argument(
5229 help="Python path to schema definition to use",
5231 parser.add_argument(
5232 "--defines-by-path",
5233 help="Python path to decoder's defines_by_path",
5235 parser.add_argument(
5237 action='store_true',
5238 help="Disallow BER encoding",
5240 parser.add_argument(
5241 "--print-decode-path",
5242 action='store_true',
5243 help="Print decode paths",
5245 parser.add_argument(
5246 "--decode-path-only",
5247 help="Print only specified decode path",
5249 parser.add_argument(
5251 type=argparse.FileType("rb"),
5252 help="Path to DER file you want to decode",
5254 args = parser.parse_args()
5255 args.DERFile.seek(args.skip)
5256 der = memoryview(args.DERFile.read())
5257 args.DERFile.close()
5258 oids = obj_by_path(args.oids) if args.oids else {}
5260 schema = obj_by_path(args.schema)
5261 from functools import partial
5262 pprinter = partial(pprint, big_blobs=True)
5264 schema, pprinter = generic_decoder()
5265 ctx = {"bered": not args.nobered}
5266 if args.defines_by_path is not None:
5267 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5268 obj, tail = schema().decode(der, ctx=ctx)
5272 with_colours=True if environ.get("NO_COLOR") is None else False,
5273 with_decode_path=args.print_decode_path,
5275 () if args.decode_path_only is None else
5276 tuple(args.decode_path_only.split(":"))
5280 print("\nTrailing data: %s" % hexenc(tail))
5283 if __name__ == "__main__":