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:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
203 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`bered <bered_ctx>`
210 * :ref:`defines_by_path <defines_by_path_ctx>`
211 * :ref:`strict_default_existence <strict_default_existence_ctx>`
218 All objects have ``pps()`` method, that is a generator of
219 :py:class:`pyderasn.PP` namedtuple, holding various raw information
220 about the object. If ``pps`` is called on sequences, then all underlying
221 ``PP`` will be yielded.
223 You can use :py:func:`pyderasn.pp_console_row` function, converting
224 those ``PP`` to human readable string. Actually exactly it is used for
225 all object ``repr``. But it is easy to write custom formatters.
227 >>> from pyderasn import pprint
228 >>> encoded = Integer(-12345).encode()
229 >>> obj, tail = Integer().decode(encoded)
230 >>> print(pprint(obj))
231 0 [1,1, 2] INTEGER -12345
238 ASN.1 structures often have ANY and OCTET STRING fields, that are
239 DEFINED BY some previously met ObjectIdentifier. This library provides
240 ability to specify mapping between some OID and field that must be
241 decoded with specific specification.
246 :py:class:`pyderasn.ObjectIdentifier` field inside
247 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
248 necessary for decoding structures. For example, CMS (:rfc:`5652`)
251 class ContentInfo(Sequence):
253 ("contentType", ContentType(defines=((("content",), {
254 id_digestedData: DigestedData(),
255 id_signedData: SignedData(),
257 ("content", Any(expl=tag_ctxc(0))),
260 ``contentType`` field tells that it defines that ``content`` must be
261 decoded with ``SignedData`` specification, if ``contentType`` equals to
262 ``id-signedData``. The same applies to ``DigestedData``. If
263 ``contentType`` contains unknown OID, then no automatic decoding is
266 You can specify multiple fields, that will be autodecoded -- that is why
267 ``defines`` kwarg is a sequence. You can specify defined field
268 relatively or absolutely to current decode path. For example ``defines``
269 for AlgorithmIdentifier of X.509's
270 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
274 id_ecPublicKey: ECParameters(),
275 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
277 (("..", "subjectPublicKey"), {
278 id_rsaEncryption: RSAPublicKey(),
279 id_GostR3410_2001: OctetString(),
283 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
284 autodecode its parameters inside SPKI's algorithm and its public key
287 Following types can be automatically decoded (DEFINED BY):
289 * :py:class:`pyderasn.Any`
290 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
291 * :py:class:`pyderasn.OctetString`
292 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
293 ``Any``/``BitString``/``OctetString``-s
295 When any of those fields is automatically decoded, then ``.defined``
296 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
297 was defined, ``value`` contains corresponding decoded value. For example
298 above, ``content_info["content"].defined == (id_signedData,
301 .. _defines_by_path_ctx:
303 defines_by_path context option
304 ______________________________
306 Sometimes you either can not or do not want to explicitly set *defines*
307 in the scheme. You can dynamically apply those definitions when calling
308 ``.decode()`` method.
310 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
311 value must be sequence of following tuples::
313 (decode_path, defines)
315 where ``decode_path`` is a tuple holding so-called decode path to the
316 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
317 ``defines``, holding exactly the same value as accepted in its keyword
320 For example, again for CMS, you want to automatically decode
321 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
322 structures it may hold. Also, automatically decode ``controlSequence``
325 content_info, tail = ContentInfo().decode(data, defines_by_path=(
328 ((("content",), {id_signedData: SignedData()}),),
333 DecodePathDefBy(id_signedData),
338 id_cct_PKIData: PKIData(),
339 id_cct_PKIResponse: PKIResponse(),
345 DecodePathDefBy(id_signedData),
348 DecodePathDefBy(id_cct_PKIResponse),
354 id_cmc_recipientNonce: RecipientNonce(),
355 id_cmc_senderNonce: SenderNonce(),
356 id_cmc_statusInfoV2: CMCStatusInfoV2(),
357 id_cmc_transactionId: TransactionId(),
362 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
363 First function is useful for path construction when some automatic
364 decoding is already done. ``any`` means literally any value it meet --
365 useful for SEQUENCE/SET OF-s.
372 By default PyDERASN accepts only DER encoded data. It always encodes to
373 DER. But you can optionally enable BER decoding with setting ``bered``
374 :ref:`context <ctx>` argument to True. Indefinite lengths and
375 constructed primitive types should be parsed successfully.
377 * If object is encoded in BER form (not the DER one), then ``bered``
378 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
379 STRING`` can contain it.
380 * If object has an indefinite length encoding, then its ``lenindef``
381 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
382 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
384 * If object has an indefinite length encoded explicit tag, then
385 ``expl_lenindef`` is set to True.
387 EOC (end-of-contents) token's length is taken in advance in object's
395 .. autoclass:: pyderasn.Boolean
400 .. autoclass:: pyderasn.Integer
405 .. autoclass:: pyderasn.BitString
410 .. autoclass:: pyderasn.OctetString
415 .. autoclass:: pyderasn.Null
420 .. autoclass:: pyderasn.ObjectIdentifier
425 .. autoclass:: pyderasn.Enumerated
429 .. autoclass:: pyderasn.CommonString
433 .. autoclass:: pyderasn.NumericString
437 .. autoclass:: pyderasn.UTCTime
438 :members: __init__, todatetime
442 .. autoclass:: pyderasn.GeneralizedTime
449 .. autoclass:: pyderasn.Choice
454 .. autoclass:: PrimitiveTypes
458 .. autoclass:: pyderasn.Any
466 .. autoclass:: pyderasn.Sequence
471 .. autoclass:: pyderasn.Set
476 .. autoclass:: pyderasn.SequenceOf
481 .. autoclass:: pyderasn.SetOf
487 .. autofunction:: pyderasn.abs_decode_path
488 .. autofunction:: pyderasn.hexenc
489 .. autofunction:: pyderasn.hexdec
490 .. autofunction:: pyderasn.tag_encode
491 .. autofunction:: pyderasn.tag_decode
492 .. autofunction:: pyderasn.tag_ctxp
493 .. autofunction:: pyderasn.tag_ctxc
494 .. autoclass:: pyderasn.Obj
495 .. autoclass:: pyderasn.DecodeError
497 .. autoclass:: pyderasn.NotEnoughData
498 .. autoclass:: pyderasn.LenIndefForm
499 .. autoclass:: pyderasn.TagMismatch
500 .. autoclass:: pyderasn.InvalidLength
501 .. autoclass:: pyderasn.InvalidOID
502 .. autoclass:: pyderasn.ObjUnknown
503 .. autoclass:: pyderasn.ObjNotReady
504 .. autoclass:: pyderasn.InvalidValueType
505 .. autoclass:: pyderasn.BoundsError
508 from codecs import getdecoder
509 from codecs import getencoder
510 from collections import namedtuple
511 from collections import OrderedDict
512 from datetime import datetime
513 from math import ceil
514 from os import environ
515 from string import digits
517 from six import add_metaclass
518 from six import binary_type
519 from six import byte2int
520 from six import indexbytes
521 from six import int2byte
522 from six import integer_types
523 from six import iterbytes
525 from six import string_types
526 from six import text_type
527 from six.moves import xrange as six_xrange
531 from termcolor import colored
533 def colored(what, *args):
577 "TagClassApplication",
581 "TagFormConstructed",
592 TagClassUniversal = 0
593 TagClassApplication = 1 << 6
594 TagClassContext = 1 << 7
595 TagClassPrivate = 1 << 6 | 1 << 7
597 TagFormConstructed = 1 << 5
600 TagClassApplication: "APPLICATION ",
601 TagClassPrivate: "PRIVATE ",
602 TagClassUniversal: "UNIV ",
606 LENINDEF = b"\x80" # length indefinite mark
607 LENINDEF_PP_CHAR = "∞"
610 ########################################################################
612 ########################################################################
614 class DecodeError(Exception):
615 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
617 :param str msg: reason of decode failing
618 :param klass: optional exact DecodeError inherited class (like
619 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
620 :py:exc:`InvalidLength`)
621 :param decode_path: tuple of strings. It contains human
622 readable names of the fields through which
623 decoding process has passed
624 :param int offset: binary offset where failure happened
626 super(DecodeError, self).__init__()
629 self.decode_path = decode_path
635 "" if self.klass is None else self.klass.__name__,
637 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
638 if len(self.decode_path) > 0 else ""
640 ("(at %d)" % self.offset) if self.offset > 0 else "",
646 return "%s(%s)" % (self.__class__.__name__, self)
649 class NotEnoughData(DecodeError):
653 class LenIndefForm(DecodeError):
657 class TagMismatch(DecodeError):
661 class InvalidLength(DecodeError):
665 class InvalidOID(DecodeError):
669 class ObjUnknown(ValueError):
670 def __init__(self, name):
671 super(ObjUnknown, self).__init__()
675 return "object is unknown: %s" % self.name
678 return "%s(%s)" % (self.__class__.__name__, self)
681 class ObjNotReady(ValueError):
682 def __init__(self, name):
683 super(ObjNotReady, self).__init__()
687 return "object is not ready: %s" % self.name
690 return "%s(%s)" % (self.__class__.__name__, self)
693 class InvalidValueType(ValueError):
694 def __init__(self, expected_types):
695 super(InvalidValueType, self).__init__()
696 self.expected_types = expected_types
699 return "invalid value type, expected: %s" % ", ".join(
700 [repr(t) for t in self.expected_types]
704 return "%s(%s)" % (self.__class__.__name__, self)
707 class BoundsError(ValueError):
708 def __init__(self, bound_min, value, bound_max):
709 super(BoundsError, self).__init__()
710 self.bound_min = bound_min
712 self.bound_max = bound_max
715 return "unsatisfied bounds: %s <= %s <= %s" % (
722 return "%s(%s)" % (self.__class__.__name__, self)
725 ########################################################################
727 ########################################################################
729 _hexdecoder = getdecoder("hex")
730 _hexencoder = getencoder("hex")
734 """Binary data to hexadecimal string convert
736 return _hexdecoder(data)[0]
740 """Hexadecimal string to binary data convert
742 return _hexencoder(data)[0].decode("ascii")
745 def int_bytes_len(num, byte_len=8):
748 return int(ceil(float(num.bit_length()) / byte_len))
751 def zero_ended_encode(num):
752 octets = bytearray(int_bytes_len(num, 7))
754 octets[i] = num & 0x7F
758 octets[i] = 0x80 | (num & 0x7F)
764 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
765 """Encode tag to binary form
767 :param int num: tag's number
768 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
769 :py:data:`pyderasn.TagClassContext`,
770 :py:data:`pyderasn.TagClassApplication`,
771 :py:data:`pyderasn.TagClassPrivate`)
772 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
773 :py:data:`pyderasn.TagFormConstructed`)
777 return int2byte(klass | form | num)
778 # [XX|X|11111][1.......][1.......] ... [0.......]
779 return int2byte(klass | form | 31) + zero_ended_encode(num)
783 """Decode tag from binary form
787 No validation is performed, assuming that it has already passed.
789 It returns tuple with three integers, as
790 :py:func:`pyderasn.tag_encode` accepts.
792 first_octet = byte2int(tag)
793 klass = first_octet & 0xC0
794 form = first_octet & 0x20
795 if first_octet & 0x1F < 0x1F:
796 return (klass, form, first_octet & 0x1F)
798 for octet in iterbytes(tag[1:]):
801 return (klass, form, num)
805 """Create CONTEXT PRIMITIVE tag
807 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
811 """Create CONTEXT CONSTRUCTED tag
813 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
817 """Take off tag from the data
819 :returns: (encoded tag, tag length, remaining data)
822 raise NotEnoughData("no data at all")
823 if byte2int(data) & 0x1F < 31:
824 return data[:1], 1, data[1:]
829 raise DecodeError("unfinished tag")
830 if indexbytes(data, i) & 0x80 == 0:
833 return data[:i], i, data[i:]
839 octets = bytearray(int_bytes_len(l) + 1)
840 octets[0] = 0x80 | (len(octets) - 1)
841 for i in six_xrange(len(octets) - 1, 0, -1):
847 def len_decode(data):
850 :returns: (decoded length, length's length, remaining data)
851 :raises LenIndefForm: if indefinite form encoding is met
854 raise NotEnoughData("no data at all")
855 first_octet = byte2int(data)
856 if first_octet & 0x80 == 0:
857 return first_octet, 1, data[1:]
858 octets_num = first_octet & 0x7F
859 if octets_num + 1 > len(data):
860 raise NotEnoughData("encoded length is longer than data")
863 if byte2int(data[1:]) == 0:
864 raise DecodeError("leading zeros")
866 for v in iterbytes(data[1:1 + octets_num]):
869 raise DecodeError("long form instead of short one")
870 return l, 1 + octets_num, data[1 + octets_num:]
873 ########################################################################
875 ########################################################################
877 class AutoAddSlots(type):
878 def __new__(mcs, name, bases, _dict):
879 _dict["__slots__"] = _dict.get("__slots__", ())
880 return type.__new__(mcs, name, bases, _dict)
883 @add_metaclass(AutoAddSlots)
885 """Common ASN.1 object class
887 All ASN.1 types are inherited from it. It has metaclass that
888 automatically adds ``__slots__`` to all inherited classes.
912 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
913 self._expl = getattr(self, "expl", None) if expl is None else expl
914 if self.tag != self.tag_default and self._expl is not None:
915 raise ValueError("implicit and explicit tags can not be set simultaneously")
916 if default is not None:
918 self.optional = optional
919 self.offset, self.llen, self.vlen = _decoded
921 self.expl_lenindef = False
922 self.lenindef = False
926 def ready(self): # pragma: no cover
927 """Is object ready to be encoded?
929 raise NotImplementedError()
931 def _assert_ready(self):
933 raise ObjNotReady(self.__class__.__name__)
937 """Is object decoded?
939 return (self.llen + self.vlen) > 0
941 def copy(self): # pragma: no cover
942 """Make a copy of object, safe to be mutated
944 raise NotImplementedError()
952 return self.tlen + self.llen + self.vlen
954 def __str__(self): # pragma: no cover
955 return self.__bytes__() if PY2 else self.__unicode__()
957 def __ne__(self, their):
958 return not(self == their)
960 def __gt__(self, their): # pragma: no cover
961 return not(self < their)
963 def __le__(self, their): # pragma: no cover
964 return (self == their) or (self < their)
966 def __ge__(self, their): # pragma: no cover
967 return (self == their) or (self > their)
969 def _encode(self): # pragma: no cover
970 raise NotImplementedError()
972 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
973 raise NotImplementedError()
977 if self._expl is None:
979 return b"".join((self._expl, len_encode(len(raw)), raw))
992 :param data: either binary or memoryview
993 :param int offset: initial data's offset
994 :param bool leavemm: do we need to leave memoryview of remaining
995 data as is, or convert it to bytes otherwise
996 :param ctx: optional :ref:`context <ctx>` governing decoding process.
997 :param tag_only: decode only the tag, without length and contents
998 (used only in Choice and Set structures, trying to
999 determine if tag satisfies the scheme)
1000 :returns: (Obj, remaining data)
1004 tlv = memoryview(data)
1005 if self._expl is None:
1006 result = self._decode(
1009 decode_path=decode_path,
1018 t, tlen, lv = tag_strip(tlv)
1019 except DecodeError as err:
1020 raise err.__class__(
1022 klass=self.__class__,
1023 decode_path=decode_path,
1028 klass=self.__class__,
1029 decode_path=decode_path,
1033 l, llen, v = len_decode(lv)
1034 except LenIndefForm as err:
1035 if not ctx.get("bered", False):
1036 raise err.__class__(
1038 klass=self.__class__,
1039 decode_path=decode_path,
1043 offset += tlen + llen
1044 result = self._decode(
1047 decode_path=decode_path,
1054 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1055 if eoc_expected.tobytes() != EOC:
1058 klass=self.__class__,
1059 decode_path=decode_path,
1063 obj.expl_lenindef = True
1064 except DecodeError as err:
1065 raise err.__class__(
1067 klass=self.__class__,
1068 decode_path=decode_path,
1073 raise NotEnoughData(
1074 "encoded length is longer than data",
1075 klass=self.__class__,
1076 decode_path=decode_path,
1079 result = self._decode(
1081 offset=offset + tlen + llen,
1082 decode_path=decode_path,
1089 return obj, (tail if leavemm else tail.tobytes())
1093 return self._expl is not None
1100 def expl_tlen(self):
1101 return len(self._expl)
1104 def expl_llen(self):
1105 if self.expl_lenindef:
1107 return len(len_encode(self.tlvlen))
1110 def expl_offset(self):
1111 return self.offset - self.expl_tlen - self.expl_llen
1114 def expl_vlen(self):
1118 def expl_tlvlen(self):
1119 return self.expl_tlen + self.expl_llen + self.expl_vlen
1121 def pps_lenindef(self, decode_path):
1124 asn1_type_name="EOC",
1126 decode_path=decode_path,
1128 self.offset + self.tlvlen -
1129 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1136 if self.expl_lenindef:
1138 asn1_type_name="EOC",
1139 obj_name="EXPLICIT",
1140 decode_path=decode_path,
1141 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1149 class DecodePathDefBy(object):
1150 """DEFINED BY representation inside decode path
1152 __slots__ = ("defined_by",)
1154 def __init__(self, defined_by):
1155 self.defined_by = defined_by
1157 def __ne__(self, their):
1158 return not(self == their)
1160 def __eq__(self, their):
1161 if not isinstance(their, self.__class__):
1163 return self.defined_by == their.defined_by
1166 return "DEFINED BY " + str(self.defined_by)
1169 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1172 ########################################################################
1174 ########################################################################
1176 PP = namedtuple("PP", (
1201 asn1_type_name="unknown",
1218 expl_lenindef=False,
1246 def _colorize(what, colour, with_colours, attrs=("bold",)):
1247 return colored(what, colour, attrs=attrs) if with_colours else what
1262 " " if pp.expl_offset is None else
1263 ("-%d" % (pp.offset - pp.expl_offset))
1265 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1267 cols.append(_colorize(col, "red", with_colours, ()))
1268 col = "[%d,%d,%4d]%s" % (
1272 LENINDEF_PP_CHAR if pp.lenindef else " "
1274 col = _colorize(col, "green", with_colours, ())
1276 if len(pp.decode_path) > 0:
1277 cols.append(" ." * (len(pp.decode_path)))
1278 ent = pp.decode_path[-1]
1279 if isinstance(ent, DecodePathDefBy):
1280 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1281 value = str(ent.defined_by)
1283 oids is not None and
1284 ent.defined_by.asn1_type_name ==
1285 ObjectIdentifier.asn1_type_name and
1288 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1290 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1292 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1293 if pp.expl is not None:
1294 klass, _, num = pp.expl
1295 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1296 cols.append(_colorize(col, "blue", with_colours))
1297 if pp.impl is not None:
1298 klass, _, num = pp.impl
1299 col = "[%s%d]" % (TagClassReprs[klass], num)
1300 cols.append(_colorize(col, "blue", with_colours))
1301 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1302 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1304 cols.append(_colorize("BER", "red", with_colours))
1305 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1306 if pp.value is not None:
1308 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1310 oids is not None and
1311 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1314 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1316 if isinstance(pp.blob, binary_type):
1317 cols.append(hexenc(pp.blob))
1318 elif isinstance(pp.blob, tuple):
1319 cols.append(", ".join(pp.blob))
1321 cols.append(_colorize("OPTIONAL", "red", with_colours))
1323 cols.append(_colorize("DEFAULT", "red", with_colours))
1324 return " ".join(cols)
1327 def pp_console_blob(pp):
1328 cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
1329 if len(pp.decode_path) > 0:
1330 cols.append(" ." * (len(pp.decode_path) + 1))
1331 if isinstance(pp.blob, binary_type):
1332 blob = hexenc(pp.blob).upper()
1333 for i in range(0, len(blob), 32):
1334 chunk = blob[i:i + 32]
1335 yield " ".join(cols + [":".join(
1336 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1338 elif isinstance(pp.blob, tuple):
1339 yield " ".join(cols + [", ".join(pp.blob)])
1342 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1343 """Pretty print object
1345 :param Obj obj: object you want to pretty print
1346 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1347 from it is met, then its humand readable form is printed
1348 :param big_blobs: if large binary objects are met (like OctetString
1349 values), do we need to print them too, on separate
1351 :param with_colours: colourize output, if ``termcolor`` library
1354 def _pprint_pps(pps):
1356 if hasattr(pp, "_fields"):
1358 yield pp_console_row(
1363 with_colours=with_colours,
1365 for row in pp_console_blob(pp):
1368 yield pp_console_row(
1373 with_colours=with_colours,
1376 for row in _pprint_pps(pp):
1378 return "\n".join(_pprint_pps(obj.pps()))
1381 ########################################################################
1382 # ASN.1 primitive types
1383 ########################################################################
1386 """``BOOLEAN`` boolean type
1388 >>> b = Boolean(True)
1390 >>> b == Boolean(True)
1396 tag_default = tag_encode(1)
1397 asn1_type_name = "BOOLEAN"
1409 :param value: set the value. Either boolean type, or
1410 :py:class:`pyderasn.Boolean` object
1411 :param bytes impl: override default tag with ``IMPLICIT`` one
1412 :param bytes expl: override default tag with ``EXPLICIT`` one
1413 :param default: set default value. Type same as in ``value``
1414 :param bool optional: is object ``OPTIONAL`` in sequence
1416 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1417 self._value = None if value is None else self._value_sanitize(value)
1418 if default is not None:
1419 default = self._value_sanitize(default)
1420 self.default = self.__class__(
1426 self._value = default
1428 def _value_sanitize(self, value):
1429 if issubclass(value.__class__, Boolean):
1431 if isinstance(value, bool):
1433 raise InvalidValueType((self.__class__, bool))
1437 return self._value is not None
1440 obj = self.__class__()
1441 obj._value = self._value
1443 obj._expl = self._expl
1444 obj.default = self.default
1445 obj.optional = self.optional
1446 obj.offset = self.offset
1447 obj.llen = self.llen
1448 obj.vlen = self.vlen
1451 def __nonzero__(self):
1452 self._assert_ready()
1456 self._assert_ready()
1459 def __eq__(self, their):
1460 if isinstance(their, bool):
1461 return self._value == their
1462 if not issubclass(their.__class__, Boolean):
1465 self._value == their._value and
1466 self.tag == their.tag and
1467 self._expl == their._expl
1478 return self.__class__(
1480 impl=self.tag if impl is None else impl,
1481 expl=self._expl if expl is None else expl,
1482 default=self.default if default is None else default,
1483 optional=self.optional if optional is None else optional,
1487 self._assert_ready()
1491 (b"\xFF" if self._value else b"\x00"),
1494 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1496 t, _, lv = tag_strip(tlv)
1497 except DecodeError as err:
1498 raise err.__class__(
1500 klass=self.__class__,
1501 decode_path=decode_path,
1506 klass=self.__class__,
1507 decode_path=decode_path,
1513 l, _, v = len_decode(lv)
1514 except DecodeError as err:
1515 raise err.__class__(
1517 klass=self.__class__,
1518 decode_path=decode_path,
1522 raise InvalidLength(
1523 "Boolean's length must be equal to 1",
1524 klass=self.__class__,
1525 decode_path=decode_path,
1529 raise NotEnoughData(
1530 "encoded length is longer than data",
1531 klass=self.__class__,
1532 decode_path=decode_path,
1535 first_octet = byte2int(v)
1537 if first_octet == 0:
1539 elif first_octet == 0xFF:
1541 elif ctx.get("bered", False):
1546 "unacceptable Boolean value",
1547 klass=self.__class__,
1548 decode_path=decode_path,
1551 obj = self.__class__(
1555 default=self.default,
1556 optional=self.optional,
1557 _decoded=(offset, 1, 1),
1563 return pp_console_row(next(self.pps()))
1565 def pps(self, decode_path=()):
1567 asn1_type_name=self.asn1_type_name,
1568 obj_name=self.__class__.__name__,
1569 decode_path=decode_path,
1570 value=str(self._value) if self.ready else None,
1571 optional=self.optional,
1572 default=self == self.default,
1573 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1574 expl=None if self._expl is None else tag_decode(self._expl),
1579 expl_offset=self.expl_offset if self.expled else None,
1580 expl_tlen=self.expl_tlen if self.expled else None,
1581 expl_llen=self.expl_llen if self.expled else None,
1582 expl_vlen=self.expl_vlen if self.expled else None,
1583 expl_lenindef=self.expl_lenindef,
1586 for pp in self.pps_lenindef(decode_path):
1591 """``INTEGER`` integer type
1593 >>> b = Integer(-123)
1595 >>> b == Integer(-123)
1600 >>> Integer(2, bounds=(1, 3))
1602 >>> Integer(5, bounds=(1, 3))
1603 Traceback (most recent call last):
1604 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1608 class Version(Integer):
1615 >>> v = Version("v1")
1622 {'v3': 2, 'v1': 0, 'v2': 1}
1624 __slots__ = ("specs", "_bound_min", "_bound_max")
1625 tag_default = tag_encode(2)
1626 asn1_type_name = "INTEGER"
1640 :param value: set the value. Either integer type, named value
1641 (if ``schema`` is specified in the class), or
1642 :py:class:`pyderasn.Integer` object
1643 :param bounds: set ``(MIN, MAX)`` value constraint.
1644 (-inf, +inf) by default
1645 :param bytes impl: override default tag with ``IMPLICIT`` one
1646 :param bytes expl: override default tag with ``EXPLICIT`` one
1647 :param default: set default value. Type same as in ``value``
1648 :param bool optional: is object ``OPTIONAL`` in sequence
1650 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1652 specs = getattr(self, "schema", {}) if _specs is None else _specs
1653 self.specs = specs if isinstance(specs, dict) else dict(specs)
1654 self._bound_min, self._bound_max = getattr(
1657 (float("-inf"), float("+inf")),
1658 ) if bounds is None else bounds
1659 if value is not None:
1660 self._value = self._value_sanitize(value)
1661 if default is not None:
1662 default = self._value_sanitize(default)
1663 self.default = self.__class__(
1669 if self._value is None:
1670 self._value = default
1672 def _value_sanitize(self, value):
1673 if issubclass(value.__class__, Integer):
1674 value = value._value
1675 elif isinstance(value, integer_types):
1677 elif isinstance(value, str):
1678 value = self.specs.get(value)
1680 raise ObjUnknown("integer value: %s" % value)
1682 raise InvalidValueType((self.__class__, int, str))
1683 if not self._bound_min <= value <= self._bound_max:
1684 raise BoundsError(self._bound_min, value, self._bound_max)
1689 return self._value is not None
1692 obj = self.__class__(_specs=self.specs)
1693 obj._value = self._value
1694 obj._bound_min = self._bound_min
1695 obj._bound_max = self._bound_max
1697 obj._expl = self._expl
1698 obj.default = self.default
1699 obj.optional = self.optional
1700 obj.offset = self.offset
1701 obj.llen = self.llen
1702 obj.vlen = self.vlen
1706 self._assert_ready()
1707 return int(self._value)
1710 self._assert_ready()
1713 bytes(self._expl or b"") +
1714 str(self._value).encode("ascii"),
1717 def __eq__(self, their):
1718 if isinstance(their, integer_types):
1719 return self._value == their
1720 if not issubclass(their.__class__, Integer):
1723 self._value == their._value and
1724 self.tag == their.tag and
1725 self._expl == their._expl
1728 def __lt__(self, their):
1729 return self._value < their._value
1733 for name, value in self.specs.items():
1734 if value == self._value:
1746 return self.__class__(
1749 (self._bound_min, self._bound_max)
1750 if bounds is None else bounds
1752 impl=self.tag if impl is None else impl,
1753 expl=self._expl if expl is None else expl,
1754 default=self.default if default is None else default,
1755 optional=self.optional if optional is None else optional,
1760 self._assert_ready()
1764 octets = bytearray([0])
1768 octets = bytearray()
1770 octets.append((value & 0xFF) ^ 0xFF)
1772 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1775 octets = bytearray()
1777 octets.append(value & 0xFF)
1779 if octets[-1] & 0x80 > 0:
1782 octets = bytes(octets)
1784 bytes_len = ceil(value.bit_length() / 8) or 1
1787 octets = value.to_bytes(
1792 except OverflowError:
1796 return b"".join((self.tag, len_encode(len(octets)), octets))
1798 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1800 t, _, lv = tag_strip(tlv)
1801 except DecodeError as err:
1802 raise err.__class__(
1804 klass=self.__class__,
1805 decode_path=decode_path,
1810 klass=self.__class__,
1811 decode_path=decode_path,
1817 l, llen, v = len_decode(lv)
1818 except DecodeError as err:
1819 raise err.__class__(
1821 klass=self.__class__,
1822 decode_path=decode_path,
1826 raise NotEnoughData(
1827 "encoded length is longer than data",
1828 klass=self.__class__,
1829 decode_path=decode_path,
1833 raise NotEnoughData(
1835 klass=self.__class__,
1836 decode_path=decode_path,
1839 v, tail = v[:l], v[l:]
1840 first_octet = byte2int(v)
1842 second_octet = byte2int(v[1:])
1844 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1845 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1848 "non normalized integer",
1849 klass=self.__class__,
1850 decode_path=decode_path,
1855 if first_octet & 0x80 > 0:
1856 octets = bytearray()
1857 for octet in bytearray(v):
1858 octets.append(octet ^ 0xFF)
1859 for octet in octets:
1860 value = (value << 8) | octet
1864 for octet in bytearray(v):
1865 value = (value << 8) | octet
1867 value = int.from_bytes(v, byteorder="big", signed=True)
1869 obj = self.__class__(
1871 bounds=(self._bound_min, self._bound_max),
1874 default=self.default,
1875 optional=self.optional,
1877 _decoded=(offset, llen, l),
1879 except BoundsError as err:
1882 klass=self.__class__,
1883 decode_path=decode_path,
1889 return pp_console_row(next(self.pps()))
1891 def pps(self, decode_path=()):
1893 asn1_type_name=self.asn1_type_name,
1894 obj_name=self.__class__.__name__,
1895 decode_path=decode_path,
1896 value=(self.named or str(self._value)) if self.ready else None,
1897 optional=self.optional,
1898 default=self == self.default,
1899 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1900 expl=None if self._expl is None else tag_decode(self._expl),
1905 expl_offset=self.expl_offset if self.expled else None,
1906 expl_tlen=self.expl_tlen if self.expled else None,
1907 expl_llen=self.expl_llen if self.expled else None,
1908 expl_vlen=self.expl_vlen if self.expled else None,
1909 expl_lenindef=self.expl_lenindef,
1911 for pp in self.pps_lenindef(decode_path):
1915 class BitString(Obj):
1916 """``BIT STRING`` bit string type
1918 >>> BitString(b"hello world")
1919 BIT STRING 88 bits 68656c6c6f20776f726c64
1922 >>> b == b"hello world"
1927 >>> BitString("'0A3B5F291CD'H")
1928 BIT STRING 44 bits 0a3b5f291cd0
1929 >>> b = BitString("'010110000000'B")
1930 BIT STRING 12 bits 5800
1933 >>> b[0], b[1], b[2], b[3]
1934 (False, True, False, True)
1938 [False, True, False, True, True, False, False, False, False, False, False, False]
1942 class KeyUsage(BitString):
1944 ("digitalSignature", 0),
1945 ("nonRepudiation", 1),
1946 ("keyEncipherment", 2),
1949 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1950 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1952 ['nonRepudiation', 'keyEncipherment']
1954 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1958 Pay attention that BIT STRING can be encoded both in primitive
1959 and constructed forms. Decoder always checks constructed form tag
1960 additionally to specified primitive one. If BER decoding is
1961 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
1962 of DER restrictions.
1964 __slots__ = ("tag_constructed", "specs", "defined")
1965 tag_default = tag_encode(3)
1966 asn1_type_name = "BIT STRING"
1979 :param value: set the value. Either binary type, tuple of named
1980 values (if ``schema`` is specified in the class),
1981 string in ``'XXX...'B`` form, or
1982 :py:class:`pyderasn.BitString` object
1983 :param bytes impl: override default tag with ``IMPLICIT`` one
1984 :param bytes expl: override default tag with ``EXPLICIT`` one
1985 :param default: set default value. Type same as in ``value``
1986 :param bool optional: is object ``OPTIONAL`` in sequence
1988 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1989 specs = getattr(self, "schema", {}) if _specs is None else _specs
1990 self.specs = specs if isinstance(specs, dict) else dict(specs)
1991 self._value = None if value is None else self._value_sanitize(value)
1992 if default is not None:
1993 default = self._value_sanitize(default)
1994 self.default = self.__class__(
2000 self._value = default
2002 tag_klass, _, tag_num = tag_decode(self.tag)
2003 self.tag_constructed = tag_encode(
2005 form=TagFormConstructed,
2009 def _bits2octets(self, bits):
2010 if len(self.specs) > 0:
2011 bits = bits.rstrip("0")
2013 bits += "0" * ((8 - (bit_len % 8)) % 8)
2014 octets = bytearray(len(bits) // 8)
2015 for i in six_xrange(len(octets)):
2016 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2017 return bit_len, bytes(octets)
2019 def _value_sanitize(self, value):
2020 if issubclass(value.__class__, BitString):
2022 if isinstance(value, (string_types, binary_type)):
2024 isinstance(value, string_types) and
2025 value.startswith("'")
2027 if value.endswith("'B"):
2029 if not set(value) <= set(("0", "1")):
2030 raise ValueError("B's coding contains unacceptable chars")
2031 return self._bits2octets(value)
2032 elif value.endswith("'H"):
2036 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2038 if isinstance(value, binary_type):
2039 return (len(value) * 8, value)
2041 raise InvalidValueType((self.__class__, string_types, binary_type))
2042 if isinstance(value, tuple):
2045 isinstance(value[0], integer_types) and
2046 isinstance(value[1], binary_type)
2051 bit = self.specs.get(name)
2053 raise ObjUnknown("BitString value: %s" % name)
2056 return self._bits2octets("")
2058 return self._bits2octets("".join(
2059 ("1" if bit in bits else "0")
2060 for bit in six_xrange(max(bits) + 1)
2062 raise InvalidValueType((self.__class__, binary_type, string_types))
2066 return self._value is not None
2069 obj = self.__class__(_specs=self.specs)
2071 if value is not None:
2072 value = (value[0], value[1])
2075 obj._expl = self._expl
2076 obj.default = self.default
2077 obj.optional = self.optional
2078 obj.offset = self.offset
2079 obj.llen = self.llen
2080 obj.vlen = self.vlen
2084 self._assert_ready()
2085 for i in six_xrange(self._value[0]):
2090 self._assert_ready()
2091 return self._value[0]
2093 def __bytes__(self):
2094 self._assert_ready()
2095 return self._value[1]
2097 def __eq__(self, their):
2098 if isinstance(their, bytes):
2099 return self._value[1] == their
2100 if not issubclass(their.__class__, BitString):
2103 self._value == their._value and
2104 self.tag == their.tag and
2105 self._expl == their._expl
2110 return [name for name, bit in self.specs.items() if self[bit]]
2120 return self.__class__(
2122 impl=self.tag if impl is None else impl,
2123 expl=self._expl if expl is None else expl,
2124 default=self.default if default is None else default,
2125 optional=self.optional if optional is None else optional,
2129 def __getitem__(self, key):
2130 if isinstance(key, int):
2131 bit_len, octets = self._value
2135 byte2int(memoryview(octets)[key // 8:]) >>
2138 if isinstance(key, string_types):
2139 value = self.specs.get(key)
2141 raise ObjUnknown("BitString value: %s" % key)
2143 raise InvalidValueType((int, str))
2146 self._assert_ready()
2147 bit_len, octets = self._value
2150 len_encode(len(octets) + 1),
2151 int2byte((8 - bit_len % 8) % 8),
2155 def _decode_chunk(self, lv, offset, decode_path, ctx):
2157 l, llen, v = len_decode(lv)
2158 except DecodeError as err:
2159 raise err.__class__(
2161 klass=self.__class__,
2162 decode_path=decode_path,
2166 raise NotEnoughData(
2167 "encoded length is longer than data",
2168 klass=self.__class__,
2169 decode_path=decode_path,
2173 raise NotEnoughData(
2175 klass=self.__class__,
2176 decode_path=decode_path,
2179 pad_size = byte2int(v)
2180 if l == 1 and pad_size != 0:
2182 "invalid empty value",
2183 klass=self.__class__,
2184 decode_path=decode_path,
2190 klass=self.__class__,
2191 decode_path=decode_path,
2194 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2197 klass=self.__class__,
2198 decode_path=decode_path,
2201 v, tail = v[:l], v[l:]
2202 obj = self.__class__(
2203 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2206 default=self.default,
2207 optional=self.optional,
2209 _decoded=(offset, llen, l),
2213 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2215 t, tlen, lv = tag_strip(tlv)
2216 except DecodeError as err:
2217 raise err.__class__(
2219 klass=self.__class__,
2220 decode_path=decode_path,
2226 return self._decode_chunk(lv, offset, decode_path, ctx)
2227 if t == self.tag_constructed:
2228 if not ctx.get("bered", False):
2230 "unallowed BER constructed encoding",
2231 klass=self.__class__,
2232 decode_path=decode_path,
2239 l, llen, v = len_decode(lv)
2240 except LenIndefForm:
2241 llen, l, v = 1, 0, lv[1:]
2243 except DecodeError as err:
2244 raise err.__class__(
2246 klass=self.__class__,
2247 decode_path=decode_path,
2251 raise NotEnoughData(
2252 "encoded length is longer than data",
2253 klass=self.__class__,
2254 decode_path=decode_path,
2257 if not lenindef and l == 0:
2258 raise NotEnoughData(
2260 klass=self.__class__,
2261 decode_path=decode_path,
2265 sub_offset = offset + tlen + llen
2269 if v[:EOC_LEN].tobytes() == EOC:
2276 "chunk out of bounds",
2277 klass=self.__class__,
2278 decode_path=decode_path + (str(len(chunks) - 1),),
2279 offset=chunks[-1].offset,
2281 sub_decode_path = decode_path + (str(len(chunks)),)
2283 chunk, v_tail = BitString().decode(
2286 decode_path=sub_decode_path,
2292 "expected BitString encoded chunk",
2293 klass=self.__class__,
2294 decode_path=sub_decode_path,
2297 chunks.append(chunk)
2298 sub_offset += chunk.tlvlen
2299 vlen += chunk.tlvlen
2301 if len(chunks) == 0:
2304 klass=self.__class__,
2305 decode_path=decode_path,
2310 for chunk_i, chunk in enumerate(chunks[:-1]):
2311 if chunk.bit_len % 8 != 0:
2313 "BitString chunk is not multiple of 8 bits",
2314 klass=self.__class__,
2315 decode_path=decode_path + (str(chunk_i),),
2316 offset=chunk.offset,
2318 values.append(bytes(chunk))
2319 bit_len += chunk.bit_len
2320 chunk_last = chunks[-1]
2321 values.append(bytes(chunk_last))
2322 bit_len += chunk_last.bit_len
2323 obj = self.__class__(
2324 value=(bit_len, b"".join(values)),
2327 default=self.default,
2328 optional=self.optional,
2330 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2332 obj.lenindef = lenindef
2334 return obj, (v[EOC_LEN:] if lenindef else v)
2336 klass=self.__class__,
2337 decode_path=decode_path,
2342 return pp_console_row(next(self.pps()))
2344 def pps(self, decode_path=()):
2348 bit_len, blob = self._value
2349 value = "%d bits" % bit_len
2350 if len(self.specs) > 0:
2351 blob = tuple(self.named)
2353 asn1_type_name=self.asn1_type_name,
2354 obj_name=self.__class__.__name__,
2355 decode_path=decode_path,
2358 optional=self.optional,
2359 default=self == self.default,
2360 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2361 expl=None if self._expl is None else tag_decode(self._expl),
2366 expl_offset=self.expl_offset if self.expled else None,
2367 expl_tlen=self.expl_tlen if self.expled else None,
2368 expl_llen=self.expl_llen if self.expled else None,
2369 expl_vlen=self.expl_vlen if self.expled else None,
2370 expl_lenindef=self.expl_lenindef,
2371 lenindef=self.lenindef,
2374 defined_by, defined = self.defined or (None, None)
2375 if defined_by is not None:
2377 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2379 for pp in self.pps_lenindef(decode_path):
2383 class OctetString(Obj):
2384 """``OCTET STRING`` binary string type
2386 >>> s = OctetString(b"hello world")
2387 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2388 >>> s == OctetString(b"hello world")
2393 >>> OctetString(b"hello", bounds=(4, 4))
2394 Traceback (most recent call last):
2395 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2396 >>> OctetString(b"hell", bounds=(4, 4))
2397 OCTET STRING 4 bytes 68656c6c
2401 Pay attention that OCTET STRING can be encoded both in primitive
2402 and constructed forms. Decoder always checks constructed form tag
2403 additionally to specified primitive one. If BER decoding is
2404 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2405 of DER restrictions.
2407 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2408 tag_default = tag_encode(4)
2409 asn1_type_name = "OCTET STRING"
2422 :param value: set the value. Either binary type, or
2423 :py:class:`pyderasn.OctetString` object
2424 :param bounds: set ``(MIN, MAX)`` value size constraint.
2425 (-inf, +inf) by default
2426 :param bytes impl: override default tag with ``IMPLICIT`` one
2427 :param bytes expl: override default tag with ``EXPLICIT`` one
2428 :param default: set default value. Type same as in ``value``
2429 :param bool optional: is object ``OPTIONAL`` in sequence
2431 super(OctetString, self).__init__(
2439 self._bound_min, self._bound_max = getattr(
2443 ) if bounds is None else bounds
2444 if value is not None:
2445 self._value = self._value_sanitize(value)
2446 if default is not None:
2447 default = self._value_sanitize(default)
2448 self.default = self.__class__(
2453 if self._value is None:
2454 self._value = default
2456 tag_klass, _, tag_num = tag_decode(self.tag)
2457 self.tag_constructed = tag_encode(
2459 form=TagFormConstructed,
2463 def _value_sanitize(self, value):
2464 if issubclass(value.__class__, OctetString):
2465 value = value._value
2466 elif isinstance(value, binary_type):
2469 raise InvalidValueType((self.__class__, bytes))
2470 if not self._bound_min <= len(value) <= self._bound_max:
2471 raise BoundsError(self._bound_min, len(value), self._bound_max)
2476 return self._value is not None
2479 obj = self.__class__()
2480 obj._value = self._value
2481 obj._bound_min = self._bound_min
2482 obj._bound_max = self._bound_max
2484 obj._expl = self._expl
2485 obj.default = self.default
2486 obj.optional = self.optional
2487 obj.offset = self.offset
2488 obj.llen = self.llen
2489 obj.vlen = self.vlen
2492 def __bytes__(self):
2493 self._assert_ready()
2496 def __eq__(self, their):
2497 if isinstance(their, binary_type):
2498 return self._value == their
2499 if not issubclass(their.__class__, OctetString):
2502 self._value == their._value and
2503 self.tag == their.tag and
2504 self._expl == their._expl
2507 def __lt__(self, their):
2508 return self._value < their._value
2519 return self.__class__(
2522 (self._bound_min, self._bound_max)
2523 if bounds is None else bounds
2525 impl=self.tag if impl is None else impl,
2526 expl=self._expl if expl is None else expl,
2527 default=self.default if default is None else default,
2528 optional=self.optional if optional is None else optional,
2532 self._assert_ready()
2535 len_encode(len(self._value)),
2539 def _decode_chunk(self, lv, offset, decode_path, ctx):
2541 l, llen, v = len_decode(lv)
2542 except DecodeError as err:
2543 raise err.__class__(
2545 klass=self.__class__,
2546 decode_path=decode_path,
2550 raise NotEnoughData(
2551 "encoded length is longer than data",
2552 klass=self.__class__,
2553 decode_path=decode_path,
2556 v, tail = v[:l], v[l:]
2558 obj = self.__class__(
2560 bounds=(self._bound_min, self._bound_max),
2563 default=self.default,
2564 optional=self.optional,
2565 _decoded=(offset, llen, l),
2567 except DecodeError as err:
2570 klass=self.__class__,
2571 decode_path=decode_path,
2574 except BoundsError as err:
2577 klass=self.__class__,
2578 decode_path=decode_path,
2583 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2585 t, tlen, lv = tag_strip(tlv)
2586 except DecodeError as err:
2587 raise err.__class__(
2589 klass=self.__class__,
2590 decode_path=decode_path,
2596 return self._decode_chunk(lv, offset, decode_path, ctx)
2597 if t == self.tag_constructed:
2598 if not ctx.get("bered", False):
2600 "unallowed BER constructed encoding",
2601 klass=self.__class__,
2602 decode_path=decode_path,
2609 l, llen, v = len_decode(lv)
2610 except LenIndefForm:
2611 llen, l, v = 1, 0, lv[1:]
2613 except DecodeError as err:
2614 raise err.__class__(
2616 klass=self.__class__,
2617 decode_path=decode_path,
2621 raise NotEnoughData(
2622 "encoded length is longer than data",
2623 klass=self.__class__,
2624 decode_path=decode_path,
2628 sub_offset = offset + tlen + llen
2632 if v[:EOC_LEN].tobytes() == EOC:
2639 "chunk out of bounds",
2640 klass=self.__class__,
2641 decode_path=decode_path + (str(len(chunks) - 1),),
2642 offset=chunks[-1].offset,
2644 sub_decode_path = decode_path + (str(len(chunks)),)
2646 chunk, v_tail = OctetString().decode(
2649 decode_path=sub_decode_path,
2655 "expected OctetString encoded chunk",
2656 klass=self.__class__,
2657 decode_path=sub_decode_path,
2660 chunks.append(chunk)
2661 sub_offset += chunk.tlvlen
2662 vlen += chunk.tlvlen
2665 obj = self.__class__(
2666 value=b"".join(bytes(chunk) for chunk in chunks),
2667 bounds=(self._bound_min, self._bound_max),
2670 default=self.default,
2671 optional=self.optional,
2672 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2674 except DecodeError as err:
2677 klass=self.__class__,
2678 decode_path=decode_path,
2681 except BoundsError as err:
2684 klass=self.__class__,
2685 decode_path=decode_path,
2688 obj.lenindef = lenindef
2690 return obj, (v[EOC_LEN:] if lenindef else v)
2692 klass=self.__class__,
2693 decode_path=decode_path,
2698 return pp_console_row(next(self.pps()))
2700 def pps(self, decode_path=()):
2702 asn1_type_name=self.asn1_type_name,
2703 obj_name=self.__class__.__name__,
2704 decode_path=decode_path,
2705 value=("%d bytes" % len(self._value)) if self.ready else None,
2706 blob=self._value if self.ready else None,
2707 optional=self.optional,
2708 default=self == self.default,
2709 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2710 expl=None if self._expl is None else tag_decode(self._expl),
2715 expl_offset=self.expl_offset if self.expled else None,
2716 expl_tlen=self.expl_tlen if self.expled else None,
2717 expl_llen=self.expl_llen if self.expled else None,
2718 expl_vlen=self.expl_vlen if self.expled else None,
2719 expl_lenindef=self.expl_lenindef,
2720 lenindef=self.lenindef,
2723 defined_by, defined = self.defined or (None, None)
2724 if defined_by is not None:
2726 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2728 for pp in self.pps_lenindef(decode_path):
2733 """``NULL`` null object
2741 tag_default = tag_encode(5)
2742 asn1_type_name = "NULL"
2746 value=None, # unused, but Sequence passes it
2753 :param bytes impl: override default tag with ``IMPLICIT`` one
2754 :param bytes expl: override default tag with ``EXPLICIT`` one
2755 :param bool optional: is object ``OPTIONAL`` in sequence
2757 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2765 obj = self.__class__()
2767 obj._expl = self._expl
2768 obj.default = self.default
2769 obj.optional = self.optional
2770 obj.offset = self.offset
2771 obj.llen = self.llen
2772 obj.vlen = self.vlen
2775 def __eq__(self, their):
2776 if not issubclass(their.__class__, Null):
2779 self.tag == their.tag and
2780 self._expl == their._expl
2790 return self.__class__(
2791 impl=self.tag if impl is None else impl,
2792 expl=self._expl if expl is None else expl,
2793 optional=self.optional if optional is None else optional,
2797 return self.tag + len_encode(0)
2799 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2801 t, _, lv = tag_strip(tlv)
2802 except DecodeError as err:
2803 raise err.__class__(
2805 klass=self.__class__,
2806 decode_path=decode_path,
2811 klass=self.__class__,
2812 decode_path=decode_path,
2818 l, _, v = len_decode(lv)
2819 except DecodeError as err:
2820 raise err.__class__(
2822 klass=self.__class__,
2823 decode_path=decode_path,
2827 raise InvalidLength(
2828 "Null must have zero length",
2829 klass=self.__class__,
2830 decode_path=decode_path,
2833 obj = self.__class__(
2836 optional=self.optional,
2837 _decoded=(offset, 1, 0),
2842 return pp_console_row(next(self.pps()))
2844 def pps(self, decode_path=()):
2846 asn1_type_name=self.asn1_type_name,
2847 obj_name=self.__class__.__name__,
2848 decode_path=decode_path,
2849 optional=self.optional,
2850 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2851 expl=None if self._expl is None else tag_decode(self._expl),
2856 expl_offset=self.expl_offset if self.expled else None,
2857 expl_tlen=self.expl_tlen if self.expled else None,
2858 expl_llen=self.expl_llen if self.expled else None,
2859 expl_vlen=self.expl_vlen if self.expled else None,
2860 expl_lenindef=self.expl_lenindef,
2862 for pp in self.pps_lenindef(decode_path):
2866 class ObjectIdentifier(Obj):
2867 """``OBJECT IDENTIFIER`` OID type
2869 >>> oid = ObjectIdentifier((1, 2, 3))
2870 OBJECT IDENTIFIER 1.2.3
2871 >>> oid == ObjectIdentifier("1.2.3")
2877 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2878 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2880 >>> str(ObjectIdentifier((3, 1)))
2881 Traceback (most recent call last):
2882 pyderasn.InvalidOID: unacceptable first arc value
2884 __slots__ = ("defines",)
2885 tag_default = tag_encode(6)
2886 asn1_type_name = "OBJECT IDENTIFIER"
2899 :param value: set the value. Either tuples of integers,
2900 string of "."-concatenated integers, or
2901 :py:class:`pyderasn.ObjectIdentifier` object
2902 :param defines: sequence of tuples. Each tuple has two elements.
2903 First one is relative to current one decode
2904 path, aiming to the field defined by that OID.
2905 Read about relative path in
2906 :py:func:`pyderasn.abs_decode_path`. Second
2907 tuple element is ``{OID: pyderasn.Obj()}``
2908 dictionary, mapping between current OID value
2909 and structure applied to defined field.
2910 :ref:`Read about DEFINED BY <definedby>`
2911 :param bytes impl: override default tag with ``IMPLICIT`` one
2912 :param bytes expl: override default tag with ``EXPLICIT`` one
2913 :param default: set default value. Type same as in ``value``
2914 :param bool optional: is object ``OPTIONAL`` in sequence
2916 super(ObjectIdentifier, self).__init__(
2924 if value is not None:
2925 self._value = self._value_sanitize(value)
2926 if default is not None:
2927 default = self._value_sanitize(default)
2928 self.default = self.__class__(
2933 if self._value is None:
2934 self._value = default
2935 self.defines = defines
2937 def __add__(self, their):
2938 if isinstance(their, self.__class__):
2939 return self.__class__(self._value + their._value)
2940 if isinstance(their, tuple):
2941 return self.__class__(self._value + their)
2942 raise InvalidValueType((self.__class__, tuple))
2944 def _value_sanitize(self, value):
2945 if issubclass(value.__class__, ObjectIdentifier):
2947 if isinstance(value, string_types):
2949 value = tuple(int(arc) for arc in value.split("."))
2951 raise InvalidOID("unacceptable arcs values")
2952 if isinstance(value, tuple):
2954 raise InvalidOID("less than 2 arcs")
2955 first_arc = value[0]
2956 if first_arc in (0, 1):
2957 if not (0 <= value[1] <= 39):
2958 raise InvalidOID("second arc is too wide")
2959 elif first_arc == 2:
2962 raise InvalidOID("unacceptable first arc value")
2964 raise InvalidValueType((self.__class__, str, tuple))
2968 return self._value is not None
2971 obj = self.__class__()
2972 obj._value = self._value
2973 obj.defines = self.defines
2975 obj._expl = self._expl
2976 obj.default = self.default
2977 obj.optional = self.optional
2978 obj.offset = self.offset
2979 obj.llen = self.llen
2980 obj.vlen = self.vlen
2984 self._assert_ready()
2985 return iter(self._value)
2988 return ".".join(str(arc) for arc in self._value or ())
2991 self._assert_ready()
2994 bytes(self._expl or b"") +
2995 str(self._value).encode("ascii"),
2998 def __eq__(self, their):
2999 if isinstance(their, tuple):
3000 return self._value == their
3001 if not issubclass(their.__class__, ObjectIdentifier):
3004 self.tag == their.tag and
3005 self._expl == their._expl and
3006 self._value == their._value
3009 def __lt__(self, their):
3010 return self._value < their._value
3021 return self.__class__(
3023 defines=self.defines if defines is None else defines,
3024 impl=self.tag if impl is None else impl,
3025 expl=self._expl if expl is None else expl,
3026 default=self.default if default is None else default,
3027 optional=self.optional if optional is None else optional,
3031 self._assert_ready()
3033 first_value = value[1]
3034 first_arc = value[0]
3037 elif first_arc == 1:
3039 elif first_arc == 2:
3041 else: # pragma: no cover
3042 raise RuntimeError("invalid arc is stored")
3043 octets = [zero_ended_encode(first_value)]
3044 for arc in value[2:]:
3045 octets.append(zero_ended_encode(arc))
3046 v = b"".join(octets)
3047 return b"".join((self.tag, len_encode(len(v)), v))
3049 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3051 t, _, lv = tag_strip(tlv)
3052 except DecodeError as err:
3053 raise err.__class__(
3055 klass=self.__class__,
3056 decode_path=decode_path,
3061 klass=self.__class__,
3062 decode_path=decode_path,
3068 l, llen, v = len_decode(lv)
3069 except DecodeError as err:
3070 raise err.__class__(
3072 klass=self.__class__,
3073 decode_path=decode_path,
3077 raise NotEnoughData(
3078 "encoded length is longer than data",
3079 klass=self.__class__,
3080 decode_path=decode_path,
3084 raise NotEnoughData(
3086 klass=self.__class__,
3087 decode_path=decode_path,
3090 v, tail = v[:l], v[l:]
3096 octet = indexbytes(v, i)
3097 arc = (arc << 7) | (octet & 0x7F)
3098 if octet & 0x80 == 0:
3106 klass=self.__class__,
3107 decode_path=decode_path,
3111 second_arc = arcs[0]
3112 if 0 <= second_arc <= 39:
3114 elif 40 <= second_arc <= 79:
3120 obj = self.__class__(
3121 value=tuple([first_arc, second_arc] + arcs[1:]),
3124 default=self.default,
3125 optional=self.optional,
3126 _decoded=(offset, llen, l),
3131 return pp_console_row(next(self.pps()))
3133 def pps(self, decode_path=()):
3135 asn1_type_name=self.asn1_type_name,
3136 obj_name=self.__class__.__name__,
3137 decode_path=decode_path,
3138 value=str(self) if self.ready else None,
3139 optional=self.optional,
3140 default=self == self.default,
3141 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3142 expl=None if self._expl is None else tag_decode(self._expl),
3147 expl_offset=self.expl_offset if self.expled else None,
3148 expl_tlen=self.expl_tlen if self.expled else None,
3149 expl_llen=self.expl_llen if self.expled else None,
3150 expl_vlen=self.expl_vlen if self.expled else None,
3151 expl_lenindef=self.expl_lenindef,
3153 for pp in self.pps_lenindef(decode_path):
3157 class Enumerated(Integer):
3158 """``ENUMERATED`` integer type
3160 This type is identical to :py:class:`pyderasn.Integer`, but requires
3161 schema to be specified and does not accept values missing from it.
3164 tag_default = tag_encode(10)
3165 asn1_type_name = "ENUMERATED"
3176 bounds=None, # dummy argument, workability for Integer.decode
3178 super(Enumerated, self).__init__(
3187 if len(self.specs) == 0:
3188 raise ValueError("schema must be specified")
3190 def _value_sanitize(self, value):
3191 if isinstance(value, self.__class__):
3192 value = value._value
3193 elif isinstance(value, integer_types):
3194 if value not in list(self.specs.values()):
3196 "unknown integer value: %s" % value,
3197 klass=self.__class__,
3199 elif isinstance(value, string_types):
3200 value = self.specs.get(value)
3202 raise ObjUnknown("integer value: %s" % value)
3204 raise InvalidValueType((self.__class__, int, str))
3208 obj = self.__class__(_specs=self.specs)
3209 obj._value = self._value
3210 obj._bound_min = self._bound_min
3211 obj._bound_max = self._bound_max
3213 obj._expl = self._expl
3214 obj.default = self.default
3215 obj.optional = self.optional
3216 obj.offset = self.offset
3217 obj.llen = self.llen
3218 obj.vlen = self.vlen
3230 return self.__class__(
3232 impl=self.tag if impl is None else impl,
3233 expl=self._expl if expl is None else expl,
3234 default=self.default if default is None else default,
3235 optional=self.optional if optional is None else optional,
3240 class CommonString(OctetString):
3241 """Common class for all strings
3243 Everything resembles :py:class:`pyderasn.OctetString`, except
3244 ability to deal with unicode text strings.
3246 >>> hexenc("привет мир".encode("utf-8"))
3247 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3248 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3250 >>> s = UTF8String("привет мир")
3251 UTF8String UTF8String привет мир
3253 'привет мир'
3254 >>> hexenc(bytes(s))
3255 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3257 >>> PrintableString("привет мир")
3258 Traceback (most recent call last):
3259 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3261 >>> BMPString("ада", bounds=(2, 2))
3262 Traceback (most recent call last):
3263 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3264 >>> s = BMPString("ад", bounds=(2, 2))
3267 >>> hexenc(bytes(s))
3275 * - :py:class:`pyderasn.UTF8String`
3277 * - :py:class:`pyderasn.NumericString`
3279 * - :py:class:`pyderasn.PrintableString`
3281 * - :py:class:`pyderasn.TeletexString`
3283 * - :py:class:`pyderasn.T61String`
3285 * - :py:class:`pyderasn.VideotexString`
3287 * - :py:class:`pyderasn.IA5String`
3289 * - :py:class:`pyderasn.GraphicString`
3291 * - :py:class:`pyderasn.VisibleString`
3293 * - :py:class:`pyderasn.ISO646String`
3295 * - :py:class:`pyderasn.GeneralString`
3297 * - :py:class:`pyderasn.UniversalString`
3299 * - :py:class:`pyderasn.BMPString`
3302 __slots__ = ("encoding",)
3304 def _value_sanitize(self, value):
3306 value_decoded = None
3307 if isinstance(value, self.__class__):
3308 value_raw = value._value
3309 elif isinstance(value, text_type):
3310 value_decoded = value
3311 elif isinstance(value, binary_type):
3314 raise InvalidValueType((self.__class__, text_type, binary_type))
3317 value_decoded.encode(self.encoding)
3318 if value_raw is None else value_raw
3321 value_raw.decode(self.encoding)
3322 if value_decoded is None else value_decoded
3324 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3325 raise DecodeError(str(err))
3326 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3334 def __eq__(self, their):
3335 if isinstance(their, binary_type):
3336 return self._value == their
3337 if isinstance(their, text_type):
3338 return self._value == their.encode(self.encoding)
3339 if not isinstance(their, self.__class__):
3342 self._value == their._value and
3343 self.tag == their.tag and
3344 self._expl == their._expl
3347 def __unicode__(self):
3349 return self._value.decode(self.encoding)
3350 return text_type(self._value)
3353 return pp_console_row(next(self.pps(no_unicode=PY2)))
3355 def pps(self, decode_path=(), no_unicode=False):
3358 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3360 asn1_type_name=self.asn1_type_name,
3361 obj_name=self.__class__.__name__,
3362 decode_path=decode_path,
3364 optional=self.optional,
3365 default=self == self.default,
3366 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3367 expl=None if self._expl is None else tag_decode(self._expl),
3372 expl_offset=self.expl_offset if self.expled else None,
3373 expl_tlen=self.expl_tlen if self.expled else None,
3374 expl_llen=self.expl_llen if self.expled else None,
3375 expl_vlen=self.expl_vlen if self.expled else None,
3376 expl_lenindef=self.expl_lenindef,
3378 for pp in self.pps_lenindef(decode_path):
3382 class UTF8String(CommonString):
3384 tag_default = tag_encode(12)
3386 asn1_type_name = "UTF8String"
3389 class NumericString(CommonString):
3392 Its value is properly sanitized: only ASCII digits can be stored.
3395 tag_default = tag_encode(18)
3397 asn1_type_name = "NumericString"
3398 allowable_chars = set(digits.encode("ascii"))
3400 def _value_sanitize(self, value):
3401 value = super(NumericString, self)._value_sanitize(value)
3402 if not set(value) <= self.allowable_chars:
3403 raise DecodeError("non-numeric value")
3407 class PrintableString(CommonString):
3409 tag_default = tag_encode(19)
3411 asn1_type_name = "PrintableString"
3414 class TeletexString(CommonString):
3416 tag_default = tag_encode(20)
3418 asn1_type_name = "TeletexString"
3421 class T61String(TeletexString):
3423 asn1_type_name = "T61String"
3426 class VideotexString(CommonString):
3428 tag_default = tag_encode(21)
3429 encoding = "iso-8859-1"
3430 asn1_type_name = "VideotexString"
3433 class IA5String(CommonString):
3435 tag_default = tag_encode(22)
3437 asn1_type_name = "IA5"
3440 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3441 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3442 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3445 class UTCTime(CommonString):
3446 """``UTCTime`` datetime type
3448 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3449 UTCTime UTCTime 2017-09-30T22:07:50
3455 datetime.datetime(2017, 9, 30, 22, 7, 50)
3456 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3457 datetime.datetime(1957, 9, 30, 22, 7, 50)
3460 tag_default = tag_encode(23)
3462 asn1_type_name = "UTCTime"
3464 fmt = "%y%m%d%H%M%SZ"
3474 bounds=None, # dummy argument, workability for OctetString.decode
3477 :param value: set the value. Either datetime type, or
3478 :py:class:`pyderasn.UTCTime` object
3479 :param bytes impl: override default tag with ``IMPLICIT`` one
3480 :param bytes expl: override default tag with ``EXPLICIT`` one
3481 :param default: set default value. Type same as in ``value``
3482 :param bool optional: is object ``OPTIONAL`` in sequence
3484 super(UTCTime, self).__init__(
3492 if value is not None:
3493 self._value = self._value_sanitize(value)
3494 if default is not None:
3495 default = self._value_sanitize(default)
3496 self.default = self.__class__(
3501 if self._value is None:
3502 self._value = default
3504 def _value_sanitize(self, value):
3505 if isinstance(value, self.__class__):
3507 if isinstance(value, datetime):
3508 return value.strftime(self.fmt).encode("ascii")
3509 if isinstance(value, binary_type):
3510 value_decoded = value.decode("ascii")
3511 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3513 datetime.strptime(value_decoded, self.fmt)
3515 raise DecodeError("invalid UTCTime format")
3518 raise DecodeError("invalid UTCTime length")
3519 raise InvalidValueType((self.__class__, datetime))
3521 def __eq__(self, their):
3522 if isinstance(their, binary_type):
3523 return self._value == their
3524 if isinstance(their, datetime):
3525 return self.todatetime() == their
3526 if not isinstance(their, self.__class__):
3529 self._value == their._value and
3530 self.tag == their.tag and
3531 self._expl == their._expl
3534 def todatetime(self):
3535 """Convert to datetime
3539 Pay attention that UTCTime can not hold full year, so all years
3540 having < 50 years are treated as 20xx, 19xx otherwise, according
3541 to X.509 recomendation.
3543 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3544 year = value.year % 100
3546 year=(2000 + year) if year < 50 else (1900 + year),
3550 minute=value.minute,
3551 second=value.second,
3555 return pp_console_row(next(self.pps()))
3557 def pps(self, decode_path=()):
3559 asn1_type_name=self.asn1_type_name,
3560 obj_name=self.__class__.__name__,
3561 decode_path=decode_path,
3562 value=self.todatetime().isoformat() if self.ready else None,
3563 optional=self.optional,
3564 default=self == self.default,
3565 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3566 expl=None if self._expl is None else tag_decode(self._expl),
3571 expl_offset=self.expl_offset if self.expled else None,
3572 expl_tlen=self.expl_tlen if self.expled else None,
3573 expl_llen=self.expl_llen if self.expled else None,
3574 expl_vlen=self.expl_vlen if self.expled else None,
3575 expl_lenindef=self.expl_lenindef,
3577 for pp in self.pps_lenindef(decode_path):
3581 class GeneralizedTime(UTCTime):
3582 """``GeneralizedTime`` datetime type
3584 This type is similar to :py:class:`pyderasn.UTCTime`.
3586 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3587 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3589 '20170930220750.000123Z'
3590 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3591 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3594 tag_default = tag_encode(24)
3595 asn1_type_name = "GeneralizedTime"
3597 fmt = "%Y%m%d%H%M%SZ"
3598 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3600 def _value_sanitize(self, value):
3601 if isinstance(value, self.__class__):
3603 if isinstance(value, datetime):
3604 return value.strftime(
3605 self.fmt_ms if value.microsecond > 0 else self.fmt
3607 if isinstance(value, binary_type):
3608 value_decoded = value.decode("ascii")
3609 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3611 datetime.strptime(value_decoded, self.fmt)
3614 "invalid GeneralizedTime (without ms) format",
3617 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3619 datetime.strptime(value_decoded, self.fmt_ms)
3622 "invalid GeneralizedTime (with ms) format",
3627 "invalid GeneralizedTime length",
3628 klass=self.__class__,
3630 raise InvalidValueType((self.__class__, datetime))
3632 def todatetime(self):
3633 value = self._value.decode("ascii")
3634 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3635 return datetime.strptime(value, self.fmt)
3636 return datetime.strptime(value, self.fmt_ms)
3639 class GraphicString(CommonString):
3641 tag_default = tag_encode(25)
3642 encoding = "iso-8859-1"
3643 asn1_type_name = "GraphicString"
3646 class VisibleString(CommonString):
3648 tag_default = tag_encode(26)
3650 asn1_type_name = "VisibleString"
3653 class ISO646String(VisibleString):
3655 asn1_type_name = "ISO646String"
3658 class GeneralString(CommonString):
3660 tag_default = tag_encode(27)
3661 encoding = "iso-8859-1"
3662 asn1_type_name = "GeneralString"
3665 class UniversalString(CommonString):
3667 tag_default = tag_encode(28)
3668 encoding = "utf-32-be"
3669 asn1_type_name = "UniversalString"
3672 class BMPString(CommonString):
3674 tag_default = tag_encode(30)
3675 encoding = "utf-16-be"
3676 asn1_type_name = "BMPString"
3680 """``CHOICE`` special type
3684 class GeneralName(Choice):
3686 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3687 ("dNSName", IA5String(impl=tag_ctxp(2))),
3690 >>> gn = GeneralName()
3692 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3693 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3694 >>> gn["dNSName"] = IA5String("bar.baz")
3695 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3696 >>> gn["rfc822Name"]
3699 [2] IA5String IA5 bar.baz
3702 >>> gn.value == gn["dNSName"]
3705 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3707 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3708 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3710 __slots__ = ("specs",)
3712 asn1_type_name = "CHOICE"
3725 :param value: set the value. Either ``(choice, value)`` tuple, or
3726 :py:class:`pyderasn.Choice` object
3727 :param bytes impl: can not be set, do **not** use it
3728 :param bytes expl: override default tag with ``EXPLICIT`` one
3729 :param default: set default value. Type same as in ``value``
3730 :param bool optional: is object ``OPTIONAL`` in sequence
3732 if impl is not None:
3733 raise ValueError("no implicit tag allowed for CHOICE")
3734 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3736 schema = getattr(self, "schema", ())
3737 if len(schema) == 0:
3738 raise ValueError("schema must be specified")
3740 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3743 if value is not None:
3744 self._value = self._value_sanitize(value)
3745 if default is not None:
3746 default_value = self._value_sanitize(default)
3747 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3748 default_obj.specs = self.specs
3749 default_obj._value = default_value
3750 self.default = default_obj
3752 self._value = default_obj.copy()._value
3754 def _value_sanitize(self, value):
3755 if isinstance(value, self.__class__):
3757 if isinstance(value, tuple) and len(value) == 2:
3759 spec = self.specs.get(choice)
3761 raise ObjUnknown(choice)
3762 if not isinstance(obj, spec.__class__):
3763 raise InvalidValueType((spec,))
3764 return (choice, spec(obj))
3765 raise InvalidValueType((self.__class__, tuple))
3769 return self._value is not None and self._value[1].ready
3772 obj = self.__class__(schema=self.specs)
3773 obj._expl = self._expl
3774 obj.default = self.default
3775 obj.optional = self.optional
3776 obj.offset = self.offset
3777 obj.llen = self.llen
3778 obj.vlen = self.vlen
3780 if value is not None:
3781 obj._value = (value[0], value[1].copy())
3784 def __eq__(self, their):
3785 if isinstance(their, tuple) and len(their) == 2:
3786 return self._value == their
3787 if not isinstance(their, self.__class__):
3790 self.specs == their.specs and
3791 self._value == their._value
3801 return self.__class__(
3804 expl=self._expl if expl is None else expl,
3805 default=self.default if default is None else default,
3806 optional=self.optional if optional is None else optional,
3811 self._assert_ready()
3812 return self._value[0]
3816 self._assert_ready()
3817 return self._value[1]
3819 def __getitem__(self, key):
3820 if key not in self.specs:
3821 raise ObjUnknown(key)
3822 if self._value is None:
3824 choice, value = self._value
3829 def __setitem__(self, key, value):
3830 spec = self.specs.get(key)
3832 raise ObjUnknown(key)
3833 if not isinstance(value, spec.__class__):
3834 raise InvalidValueType((spec.__class__,))
3835 self._value = (key, spec(value))
3843 return self._value[1].decoded if self.ready else False
3846 self._assert_ready()
3847 return self._value[1].encode()
3849 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3850 for choice, spec in self.specs.items():
3851 sub_decode_path = decode_path + (choice,)
3857 decode_path=sub_decode_path,
3866 klass=self.__class__,
3867 decode_path=decode_path,
3872 value, tail = spec.decode(
3876 decode_path=sub_decode_path,
3879 obj = self.__class__(
3882 default=self.default,
3883 optional=self.optional,
3884 _decoded=(offset, 0, value.tlvlen),
3886 obj._value = (choice, value)
3890 value = pp_console_row(next(self.pps()))
3892 value = "%s[%r]" % (value, self.value)
3895 def pps(self, decode_path=()):
3897 asn1_type_name=self.asn1_type_name,
3898 obj_name=self.__class__.__name__,
3899 decode_path=decode_path,
3900 value=self.choice if self.ready else None,
3901 optional=self.optional,
3902 default=self == self.default,
3903 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3904 expl=None if self._expl is None else tag_decode(self._expl),
3909 expl_lenindef=self.expl_lenindef,
3912 yield self.value.pps(decode_path=decode_path + (self.choice,))
3913 for pp in self.pps_lenindef(decode_path):
3917 class PrimitiveTypes(Choice):
3918 """Predefined ``CHOICE`` for all generic primitive types
3920 It could be useful for general decoding of some unspecified values:
3922 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3923 OCTET STRING 3 bytes 666f6f
3924 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3928 schema = tuple((klass.__name__, klass()) for klass in (
3953 """``ANY`` special type
3955 >>> Any(Integer(-123))
3957 >>> a = Any(OctetString(b"hello world").encode())
3958 ANY 040b68656c6c6f20776f726c64
3959 >>> hexenc(bytes(a))
3960 b'0x040x0bhello world'
3962 __slots__ = ("defined",)
3963 tag_default = tag_encode(0)
3964 asn1_type_name = "ANY"
3974 :param value: set the value. Either any kind of pyderasn's
3975 **ready** object, or bytes. Pay attention that
3976 **no** validation is performed is raw binary value
3978 :param bytes expl: override default tag with ``EXPLICIT`` one
3979 :param bool optional: is object ``OPTIONAL`` in sequence
3981 super(Any, self).__init__(None, expl, None, optional, _decoded)
3982 self._value = None if value is None else self._value_sanitize(value)
3985 def _value_sanitize(self, value):
3986 if isinstance(value, self.__class__):
3988 if isinstance(value, Obj):
3989 return value.encode()
3990 if isinstance(value, binary_type):
3992 raise InvalidValueType((self.__class__, Obj, binary_type))
3996 return self._value is not None
3999 obj = self.__class__()
4000 obj._value = self._value
4002 obj._expl = self._expl
4003 obj.optional = self.optional
4004 obj.offset = self.offset
4005 obj.llen = self.llen
4006 obj.vlen = self.vlen
4009 def __eq__(self, their):
4010 if isinstance(their, binary_type):
4011 return self._value == their
4012 if issubclass(their.__class__, Any):
4013 return self._value == their._value
4022 return self.__class__(
4024 expl=self._expl if expl is None else expl,
4025 optional=self.optional if optional is None else optional,
4028 def __bytes__(self):
4029 self._assert_ready()
4037 self._assert_ready()
4040 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4042 t, tlen, lv = tag_strip(tlv)
4043 except DecodeError as err:
4044 raise err.__class__(
4046 klass=self.__class__,
4047 decode_path=decode_path,
4051 l, llen, v = len_decode(lv)
4052 except LenIndefForm as err:
4053 if not ctx.get("bered", False):
4054 raise err.__class__(
4056 klass=self.__class__,
4057 decode_path=decode_path,
4060 llen, vlen, v = 1, 0, lv[1:]
4061 sub_offset = offset + tlen + llen
4063 while v[:EOC_LEN].tobytes() != EOC:
4064 chunk, v = Any().decode(
4067 decode_path=decode_path + (str(chunk_i),),
4071 vlen += chunk.tlvlen
4072 sub_offset += chunk.tlvlen
4074 tlvlen = tlen + llen + vlen + EOC_LEN
4075 obj = self.__class__(
4076 value=tlv[:tlvlen].tobytes(),
4078 optional=self.optional,
4079 _decoded=(offset, 0, tlvlen),
4083 return obj, v[EOC_LEN:]
4084 except DecodeError as err:
4085 raise err.__class__(
4087 klass=self.__class__,
4088 decode_path=decode_path,
4092 raise NotEnoughData(
4093 "encoded length is longer than data",
4094 klass=self.__class__,
4095 decode_path=decode_path,
4098 tlvlen = tlen + llen + l
4099 v, tail = tlv[:tlvlen], v[l:]
4100 obj = self.__class__(
4103 optional=self.optional,
4104 _decoded=(offset, 0, tlvlen),
4110 return pp_console_row(next(self.pps()))
4112 def pps(self, decode_path=()):
4114 asn1_type_name=self.asn1_type_name,
4115 obj_name=self.__class__.__name__,
4116 decode_path=decode_path,
4117 blob=self._value if self.ready else None,
4118 optional=self.optional,
4119 default=self == self.default,
4120 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4121 expl=None if self._expl is None else tag_decode(self._expl),
4126 expl_offset=self.expl_offset if self.expled else None,
4127 expl_tlen=self.expl_tlen if self.expled else None,
4128 expl_llen=self.expl_llen if self.expled else None,
4129 expl_vlen=self.expl_vlen if self.expled else None,
4130 expl_lenindef=self.expl_lenindef,
4131 lenindef=self.lenindef,
4133 defined_by, defined = self.defined or (None, None)
4134 if defined_by is not None:
4136 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4138 for pp in self.pps_lenindef(decode_path):
4142 ########################################################################
4143 # ASN.1 constructed types
4144 ########################################################################
4146 def get_def_by_path(defines_by_path, sub_decode_path):
4147 """Get define by decode path
4149 for path, define in defines_by_path:
4150 if len(path) != len(sub_decode_path):
4152 for p1, p2 in zip(path, sub_decode_path):
4153 if (p1 != any) and (p1 != p2):
4159 def abs_decode_path(decode_path, rel_path):
4160 """Create an absolute decode path from current and relative ones
4162 :param decode_path: current decode path, starting point.
4164 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4165 If first tuple's element is "/", then treat it as
4166 an absolute path, ignoring ``decode_path`` as
4167 starting point. Also this tuple can contain ".."
4168 elements, stripping the leading element from
4171 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4172 ("foo", "bar", "baz", "whatever")
4173 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4175 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4178 if rel_path[0] == "/":
4180 if rel_path[0] == "..":
4181 return abs_decode_path(decode_path[:-1], rel_path[1:])
4182 return decode_path + rel_path
4185 class Sequence(Obj):
4186 """``SEQUENCE`` structure type
4188 You have to make specification of sequence::
4190 class Extension(Sequence):
4192 ("extnID", ObjectIdentifier()),
4193 ("critical", Boolean(default=False)),
4194 ("extnValue", OctetString()),
4197 Then, you can work with it as with dictionary.
4199 >>> ext = Extension()
4200 >>> Extension().specs
4202 ('extnID', OBJECT IDENTIFIER),
4203 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4204 ('extnValue', OCTET STRING),
4206 >>> ext["extnID"] = "1.2.3"
4207 Traceback (most recent call last):
4208 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4209 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4211 You can determine if sequence is ready to be encoded:
4216 Traceback (most recent call last):
4217 pyderasn.ObjNotReady: object is not ready: extnValue
4218 >>> ext["extnValue"] = OctetString(b"foobar")
4222 Value you want to assign, must have the same **type** as in
4223 corresponding specification, but it can have different tags,
4224 optional/default attributes -- they will be taken from specification
4227 class TBSCertificate(Sequence):
4229 ("version", Version(expl=tag_ctxc(0), default="v1")),
4232 >>> tbs = TBSCertificate()
4233 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4235 Assign ``None`` to remove value from sequence.
4237 You can set values in Sequence during its initialization:
4239 >>> AlgorithmIdentifier((
4240 ("algorithm", ObjectIdentifier("1.2.3")),
4241 ("parameters", Any(Null()))
4243 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4245 You can determine if value exists/set in the sequence and take its value:
4247 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4250 OBJECT IDENTIFIER 1.2.3
4252 But pay attention that if value has default, then it won't be (not
4253 in) in the sequence (because ``DEFAULT`` must not be encoded in
4254 DER), but you can read its value:
4256 >>> "critical" in ext, ext["critical"]
4257 (False, BOOLEAN False)
4258 >>> ext["critical"] = Boolean(True)
4259 >>> "critical" in ext, ext["critical"]
4260 (True, BOOLEAN True)
4262 All defaulted values are always optional.
4264 .. _strict_default_existence_ctx:
4268 When decoded DER contains defaulted value inside, then
4269 technically this is not valid DER encoding. But we allow and pass
4270 it **by default**. Of course reencoding of that kind of DER will
4271 result in different binary representation (validly without
4272 defaulted value inside). You can enable strict defaulted values
4273 existence validation by setting ``"strict_default_existence":
4274 True`` :ref:`context <ctx>` option -- decoding process will raise
4275 an exception if defaulted value is met.
4277 Two sequences are equal if they have equal specification (schema),
4278 implicit/explicit tagging and the same values.
4280 __slots__ = ("specs",)
4281 tag_default = tag_encode(form=TagFormConstructed, num=16)
4282 asn1_type_name = "SEQUENCE"
4294 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4296 schema = getattr(self, "schema", ())
4298 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4301 if value is not None:
4302 if issubclass(value.__class__, Sequence):
4303 self._value = value._value
4304 elif hasattr(value, "__iter__"):
4305 for seq_key, seq_value in value:
4306 self[seq_key] = seq_value
4308 raise InvalidValueType((Sequence,))
4309 if default is not None:
4310 if not issubclass(default.__class__, Sequence):
4311 raise InvalidValueType((Sequence,))
4312 default_value = default._value
4313 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4314 default_obj.specs = self.specs
4315 default_obj._value = default_value
4316 self.default = default_obj
4318 self._value = default_obj.copy()._value
4322 for name, spec in self.specs.items():
4323 value = self._value.get(name)
4334 obj = self.__class__(schema=self.specs)
4336 obj._expl = self._expl
4337 obj.default = self.default
4338 obj.optional = self.optional
4339 obj.offset = self.offset
4340 obj.llen = self.llen
4341 obj.vlen = self.vlen
4342 obj._value = {k: v.copy() for k, v in self._value.items()}
4345 def __eq__(self, their):
4346 if not isinstance(their, self.__class__):
4349 self.specs == their.specs and
4350 self.tag == their.tag and
4351 self._expl == their._expl and
4352 self._value == their._value
4363 return self.__class__(
4366 impl=self.tag if impl is None else impl,
4367 expl=self._expl if expl is None else expl,
4368 default=self.default if default is None else default,
4369 optional=self.optional if optional is None else optional,
4372 def __contains__(self, key):
4373 return key in self._value
4375 def __setitem__(self, key, value):
4376 spec = self.specs.get(key)
4378 raise ObjUnknown(key)
4380 self._value.pop(key, None)
4382 if not isinstance(value, spec.__class__):
4383 raise InvalidValueType((spec.__class__,))
4384 value = spec(value=value)
4385 if spec.default is not None and value == spec.default:
4386 self._value.pop(key, None)
4388 self._value[key] = value
4390 def __getitem__(self, key):
4391 value = self._value.get(key)
4392 if value is not None:
4394 spec = self.specs.get(key)
4396 raise ObjUnknown(key)
4397 if spec.default is not None:
4401 def _encoded_values(self):
4403 for name, spec in self.specs.items():
4404 value = self._value.get(name)
4408 raise ObjNotReady(name)
4409 raws.append(value.encode())
4413 v = b"".join(self._encoded_values())
4414 return b"".join((self.tag, len_encode(len(v)), v))
4416 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4418 t, tlen, lv = tag_strip(tlv)
4419 except DecodeError as err:
4420 raise err.__class__(
4422 klass=self.__class__,
4423 decode_path=decode_path,
4428 klass=self.__class__,
4429 decode_path=decode_path,
4436 l, llen, v = len_decode(lv)
4437 except LenIndefForm as err:
4438 if not ctx.get("bered", False):
4439 raise err.__class__(
4441 klass=self.__class__,
4442 decode_path=decode_path,
4445 l, llen, v = 0, 1, lv[1:]
4447 except DecodeError as err:
4448 raise err.__class__(
4450 klass=self.__class__,
4451 decode_path=decode_path,
4455 raise NotEnoughData(
4456 "encoded length is longer than data",
4457 klass=self.__class__,
4458 decode_path=decode_path,
4462 v, tail = v[:l], v[l:]
4464 sub_offset = offset + tlen + llen
4466 for name, spec in self.specs.items():
4467 if spec.optional and (
4468 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4472 sub_decode_path = decode_path + (name,)
4474 value, v_tail = spec.decode(
4478 decode_path=sub_decode_path,
4486 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4487 if defined is not None:
4488 defined_by, defined_spec = defined
4489 if issubclass(value.__class__, SequenceOf):
4490 for i, _value in enumerate(value):
4491 sub_sub_decode_path = sub_decode_path + (
4493 DecodePathDefBy(defined_by),
4495 defined_value, defined_tail = defined_spec.decode(
4496 memoryview(bytes(_value)),
4498 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4499 if value.expled else (value.tlen + value.llen)
4502 decode_path=sub_sub_decode_path,
4505 if len(defined_tail) > 0:
4508 klass=self.__class__,
4509 decode_path=sub_sub_decode_path,
4512 _value.defined = (defined_by, defined_value)
4514 defined_value, defined_tail = defined_spec.decode(
4515 memoryview(bytes(value)),
4517 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4518 if value.expled else (value.tlen + value.llen)
4521 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4524 if len(defined_tail) > 0:
4527 klass=self.__class__,
4528 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4531 value.defined = (defined_by, defined_value)
4533 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4535 sub_offset += value_len
4537 if spec.default is not None and value == spec.default:
4538 if ctx.get("strict_default_existence", False):
4540 "DEFAULT value met",
4541 klass=self.__class__,
4542 decode_path=sub_decode_path,
4547 values[name] = value
4549 spec_defines = getattr(spec, "defines", ())
4550 if len(spec_defines) == 0:
4551 defines_by_path = ctx.get("defines_by_path", ())
4552 if len(defines_by_path) > 0:
4553 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4554 if spec_defines is not None and len(spec_defines) > 0:
4555 for rel_path, schema in spec_defines:
4556 defined = schema.get(value, None)
4557 if defined is not None:
4558 ctx.setdefault("defines", []).append((
4559 abs_decode_path(sub_decode_path[:-1], rel_path),
4563 if v[:EOC_LEN].tobytes() != EOC:
4566 klass=self.__class__,
4567 decode_path=decode_path,
4575 klass=self.__class__,
4576 decode_path=decode_path,
4579 obj = self.__class__(
4583 default=self.default,
4584 optional=self.optional,
4585 _decoded=(offset, llen, vlen),
4588 obj.lenindef = lenindef
4592 value = pp_console_row(next(self.pps()))
4594 for name in self.specs:
4595 _value = self._value.get(name)
4598 cols.append("%s: %s" % (name, repr(_value)))
4599 return "%s[%s]" % (value, "; ".join(cols))
4601 def pps(self, decode_path=()):
4603 asn1_type_name=self.asn1_type_name,
4604 obj_name=self.__class__.__name__,
4605 decode_path=decode_path,
4606 optional=self.optional,
4607 default=self == self.default,
4608 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4609 expl=None if self._expl is None else tag_decode(self._expl),
4614 expl_offset=self.expl_offset if self.expled else None,
4615 expl_tlen=self.expl_tlen if self.expled else None,
4616 expl_llen=self.expl_llen if self.expled else None,
4617 expl_vlen=self.expl_vlen if self.expled else None,
4618 expl_lenindef=self.expl_lenindef,
4619 lenindef=self.lenindef,
4621 for name in self.specs:
4622 value = self._value.get(name)
4625 yield value.pps(decode_path=decode_path + (name,))
4626 for pp in self.pps_lenindef(decode_path):
4630 class Set(Sequence):
4631 """``SET`` structure type
4633 Its usage is identical to :py:class:`pyderasn.Sequence`.
4636 tag_default = tag_encode(form=TagFormConstructed, num=17)
4637 asn1_type_name = "SET"
4640 raws = self._encoded_values()
4643 return b"".join((self.tag, len_encode(len(v)), v))
4645 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4647 t, tlen, lv = tag_strip(tlv)
4648 except DecodeError as err:
4649 raise err.__class__(
4651 klass=self.__class__,
4652 decode_path=decode_path,
4657 klass=self.__class__,
4658 decode_path=decode_path,
4665 l, llen, v = len_decode(lv)
4666 except LenIndefForm as err:
4667 if not ctx.get("bered", False):
4668 raise err.__class__(
4670 klass=self.__class__,
4671 decode_path=decode_path,
4674 l, llen, v = 0, 1, lv[1:]
4676 except DecodeError as err:
4677 raise err.__class__(
4679 klass=self.__class__,
4680 decode_path=decode_path,
4684 raise NotEnoughData(
4685 "encoded length is longer than data",
4686 klass=self.__class__,
4690 v, tail = v[:l], v[l:]
4692 sub_offset = offset + tlen + llen
4694 specs_items = self.specs.items
4696 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4698 for name, spec in specs_items():
4699 sub_decode_path = decode_path + (name,)
4705 decode_path=sub_decode_path,
4714 klass=self.__class__,
4715 decode_path=decode_path,
4718 value, v_tail = spec.decode(
4722 decode_path=sub_decode_path,
4725 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4726 sub_offset += value_len
4729 if spec.default is None or value != spec.default: # pragma: no cover
4730 # SeqMixing.test_encoded_default_accepted covers that place
4731 values[name] = value
4732 obj = self.__class__(
4736 default=self.default,
4737 optional=self.optional,
4738 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4742 if v[:EOC_LEN].tobytes() != EOC:
4745 klass=self.__class__,
4746 decode_path=decode_path,
4753 "not all values are ready",
4754 klass=self.__class__,
4755 decode_path=decode_path,
4761 class SequenceOf(Obj):
4762 """``SEQUENCE OF`` sequence type
4764 For that kind of type you must specify the object it will carry on
4765 (bounds are for example here, not required)::
4767 class Ints(SequenceOf):
4772 >>> ints.append(Integer(123))
4773 >>> ints.append(Integer(234))
4775 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4776 >>> [int(i) for i in ints]
4778 >>> ints.append(Integer(345))
4779 Traceback (most recent call last):
4780 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4783 >>> ints[1] = Integer(345)
4785 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4787 Also you can initialize sequence with preinitialized values:
4789 >>> ints = Ints([Integer(123), Integer(234)])
4791 __slots__ = ("spec", "_bound_min", "_bound_max")
4792 tag_default = tag_encode(form=TagFormConstructed, num=16)
4793 asn1_type_name = "SEQUENCE OF"
4806 super(SequenceOf, self).__init__(
4814 schema = getattr(self, "schema", None)
4816 raise ValueError("schema must be specified")
4818 self._bound_min, self._bound_max = getattr(
4822 ) if bounds is None else bounds
4824 if value is not None:
4825 self._value = self._value_sanitize(value)
4826 if default is not None:
4827 default_value = self._value_sanitize(default)
4828 default_obj = self.__class__(
4833 default_obj._value = default_value
4834 self.default = default_obj
4836 self._value = default_obj.copy()._value
4838 def _value_sanitize(self, value):
4839 if issubclass(value.__class__, SequenceOf):
4840 value = value._value
4841 elif hasattr(value, "__iter__"):
4844 raise InvalidValueType((self.__class__, iter))
4845 if not self._bound_min <= len(value) <= self._bound_max:
4846 raise BoundsError(self._bound_min, len(value), self._bound_max)
4848 if not isinstance(v, self.spec.__class__):
4849 raise InvalidValueType((self.spec.__class__,))
4854 return all(v.ready for v in self._value)
4857 obj = self.__class__(schema=self.spec)
4858 obj._bound_min = self._bound_min
4859 obj._bound_max = self._bound_max
4861 obj._expl = self._expl
4862 obj.default = self.default
4863 obj.optional = self.optional
4864 obj.offset = self.offset
4865 obj.llen = self.llen
4866 obj.vlen = self.vlen
4867 obj._value = [v.copy() for v in self._value]
4870 def __eq__(self, their):
4871 if isinstance(their, self.__class__):
4873 self.spec == their.spec and
4874 self.tag == their.tag and
4875 self._expl == their._expl and
4876 self._value == their._value
4878 if hasattr(their, "__iter__"):
4879 return self._value == list(their)
4891 return self.__class__(
4895 (self._bound_min, self._bound_max)
4896 if bounds is None else bounds
4898 impl=self.tag if impl is None else impl,
4899 expl=self._expl if expl is None else expl,
4900 default=self.default if default is None else default,
4901 optional=self.optional if optional is None else optional,
4904 def __contains__(self, key):
4905 return key in self._value
4907 def append(self, value):
4908 if not isinstance(value, self.spec.__class__):
4909 raise InvalidValueType((self.spec.__class__,))
4910 if len(self._value) + 1 > self._bound_max:
4913 len(self._value) + 1,
4916 self._value.append(value)
4919 self._assert_ready()
4920 return iter(self._value)
4923 self._assert_ready()
4924 return len(self._value)
4926 def __setitem__(self, key, value):
4927 if not isinstance(value, self.spec.__class__):
4928 raise InvalidValueType((self.spec.__class__,))
4929 self._value[key] = self.spec(value=value)
4931 def __getitem__(self, key):
4932 return self._value[key]
4934 def _encoded_values(self):
4935 return [v.encode() for v in self._value]
4938 v = b"".join(self._encoded_values())
4939 return b"".join((self.tag, len_encode(len(v)), v))
4941 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4943 t, tlen, lv = tag_strip(tlv)
4944 except DecodeError as err:
4945 raise err.__class__(
4947 klass=self.__class__,
4948 decode_path=decode_path,
4953 klass=self.__class__,
4954 decode_path=decode_path,
4961 l, llen, v = len_decode(lv)
4962 except LenIndefForm as err:
4963 if not ctx.get("bered", False):
4964 raise err.__class__(
4966 klass=self.__class__,
4967 decode_path=decode_path,
4970 l, llen, v = 0, 1, lv[1:]
4972 except DecodeError as err:
4973 raise err.__class__(
4975 klass=self.__class__,
4976 decode_path=decode_path,
4980 raise NotEnoughData(
4981 "encoded length is longer than data",
4982 klass=self.__class__,
4983 decode_path=decode_path,
4987 v, tail = v[:l], v[l:]
4989 sub_offset = offset + tlen + llen
4993 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4995 value, v_tail = spec.decode(
4999 decode_path=decode_path + (str(len(_value)),),
5002 value_len = value.expl_tlvlen if value.expled else value.tlvlen
5003 sub_offset += value_len
5006 _value.append(value)
5007 obj = self.__class__(
5010 bounds=(self._bound_min, self._bound_max),
5013 default=self.default,
5014 optional=self.optional,
5015 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5018 if v[:EOC_LEN].tobytes() != EOC:
5021 klass=self.__class__,
5022 decode_path=decode_path,
5031 pp_console_row(next(self.pps())),
5032 ", ".join(repr(v) for v in self._value),
5035 def pps(self, decode_path=()):
5037 asn1_type_name=self.asn1_type_name,
5038 obj_name=self.__class__.__name__,
5039 decode_path=decode_path,
5040 optional=self.optional,
5041 default=self == self.default,
5042 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5043 expl=None if self._expl is None else tag_decode(self._expl),
5048 expl_offset=self.expl_offset if self.expled else None,
5049 expl_tlen=self.expl_tlen if self.expled else None,
5050 expl_llen=self.expl_llen if self.expled else None,
5051 expl_vlen=self.expl_vlen if self.expled else None,
5052 expl_lenindef=self.expl_lenindef,
5053 lenindef=self.lenindef,
5055 for i, value in enumerate(self._value):
5056 yield value.pps(decode_path=decode_path + (str(i),))
5057 for pp in self.pps_lenindef(decode_path):
5061 class SetOf(SequenceOf):
5062 """``SET OF`` sequence type
5064 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5067 tag_default = tag_encode(form=TagFormConstructed, num=17)
5068 asn1_type_name = "SET OF"
5071 raws = self._encoded_values()
5074 return b"".join((self.tag, len_encode(len(v)), v))
5077 def obj_by_path(pypath): # pragma: no cover
5078 """Import object specified as string Python path
5080 Modules must be separated from classes/functions with ``:``.
5082 >>> obj_by_path("foo.bar:Baz")
5083 <class 'foo.bar.Baz'>
5084 >>> obj_by_path("foo.bar:Baz.boo")
5085 <classmethod 'foo.bar.Baz.boo'>
5087 mod, objs = pypath.rsplit(":", 1)
5088 from importlib import import_module
5089 obj = import_module(mod)
5090 for obj_name in objs.split("."):
5091 obj = getattr(obj, obj_name)
5095 def generic_decoder(): # pragma: no cover
5096 # All of this below is a big hack with self references
5097 choice = PrimitiveTypes()
5098 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5099 choice.specs["SetOf"] = SetOf(schema=choice)
5101 choice.specs["SequenceOf%d" % i] = SequenceOf(
5105 choice.specs["Any"] = Any()
5107 # Class name equals to type name, to omit it from output
5108 class SEQUENCEOF(SequenceOf):
5112 def pprint_any(obj, oids=None, with_colours=False):
5113 def _pprint_pps(pps):
5115 if hasattr(pp, "_fields"):
5116 if pp.asn1_type_name == Choice.asn1_type_name:
5118 pp_kwargs = pp._asdict()
5119 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5120 pp = _pp(**pp_kwargs)
5121 yield pp_console_row(
5126 with_colours=with_colours,
5128 for row in pp_console_blob(pp):
5131 for row in _pprint_pps(pp):
5133 return "\n".join(_pprint_pps(obj.pps()))
5134 return SEQUENCEOF(), pprint_any
5137 def main(): # pragma: no cover
5139 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5140 parser.add_argument(
5144 help="Skip that number of bytes from the beginning",
5146 parser.add_argument(
5148 help="Python path to dictionary with OIDs",
5150 parser.add_argument(
5152 help="Python path to schema definition to use",
5154 parser.add_argument(
5155 "--defines-by-path",
5156 help="Python path to decoder's defines_by_path",
5158 parser.add_argument(
5160 action='store_true',
5161 help="Disallow BER encoding",
5163 parser.add_argument(
5165 type=argparse.FileType("rb"),
5166 help="Path to DER file you want to decode",
5168 args = parser.parse_args()
5169 args.DERFile.seek(args.skip)
5170 der = memoryview(args.DERFile.read())
5171 args.DERFile.close()
5172 oids = obj_by_path(args.oids) if args.oids else {}
5174 schema = obj_by_path(args.schema)
5175 from functools import partial
5176 pprinter = partial(pprint, big_blobs=True)
5178 schema, pprinter = generic_decoder()
5179 ctx = {"bered": not args.nobered}
5180 if args.defines_by_path is not None:
5181 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5182 obj, tail = schema().decode(der, ctx=ctx)
5186 with_colours=True if environ.get("NO_COLOR") is None else False,
5189 print("\nTrailing data: %s" % hexenc(tail))
5192 if __name__ == "__main__":