3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
193 * ``expled`` -- to know if explicit tag is set
194 * ``expl_offset`` (it is lesser than ``offset``)
197 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
198 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
200 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
203 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
210 You can specify so called context keyword argument during ``decode()``
211 invocation. It is dictionary containing various options governing
214 Currently available context options:
216 * :ref:`allow_default_values <allow_default_values_ctx>`
217 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
218 * :ref:`bered <bered_ctx>`
219 * :ref:`defines_by_path <defines_by_path_ctx>`
226 All objects have ``pps()`` method, that is a generator of
227 :py:class:`pyderasn.PP` namedtuple, holding various raw information
228 about the object. If ``pps`` is called on sequences, then all underlying
229 ``PP`` will be yielded.
231 You can use :py:func:`pyderasn.pp_console_row` function, converting
232 those ``PP`` to human readable string. Actually exactly it is used for
233 all object ``repr``. But it is easy to write custom formatters.
235 >>> from pyderasn import pprint
236 >>> encoded = Integer(-12345).encode()
237 >>> obj, tail = Integer().decode(encoded)
238 >>> print(pprint(obj))
239 0 [1,1, 2] INTEGER -12345
246 ASN.1 structures often have ANY and OCTET STRING fields, that are
247 DEFINED BY some previously met ObjectIdentifier. This library provides
248 ability to specify mapping between some OID and field that must be
249 decoded with specific specification.
254 :py:class:`pyderasn.ObjectIdentifier` field inside
255 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
256 necessary for decoding structures. For example, CMS (:rfc:`5652`)
259 class ContentInfo(Sequence):
261 ("contentType", ContentType(defines=((("content",), {
262 id_digestedData: DigestedData(),
263 id_signedData: SignedData(),
265 ("content", Any(expl=tag_ctxc(0))),
268 ``contentType`` field tells that it defines that ``content`` must be
269 decoded with ``SignedData`` specification, if ``contentType`` equals to
270 ``id-signedData``. The same applies to ``DigestedData``. If
271 ``contentType`` contains unknown OID, then no automatic decoding is
274 You can specify multiple fields, that will be autodecoded -- that is why
275 ``defines`` kwarg is a sequence. You can specify defined field
276 relatively or absolutely to current decode path. For example ``defines``
277 for AlgorithmIdentifier of X.509's
278 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
282 id_ecPublicKey: ECParameters(),
283 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
285 (("..", "subjectPublicKey"), {
286 id_rsaEncryption: RSAPublicKey(),
287 id_GostR3410_2001: OctetString(),
291 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
292 autodecode its parameters inside SPKI's algorithm and its public key
295 Following types can be automatically decoded (DEFINED BY):
297 * :py:class:`pyderasn.Any`
298 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
299 * :py:class:`pyderasn.OctetString`
300 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
301 ``Any``/``BitString``/``OctetString``-s
303 When any of those fields is automatically decoded, then ``.defined``
304 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
305 was defined, ``value`` contains corresponding decoded value. For example
306 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
308 .. _defines_by_path_ctx:
310 defines_by_path context option
311 ______________________________
313 Sometimes you either can not or do not want to explicitly set *defines*
314 in the scheme. You can dynamically apply those definitions when calling
315 ``.decode()`` method.
317 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
318 value must be sequence of following tuples::
320 (decode_path, defines)
322 where ``decode_path`` is a tuple holding so-called decode path to the
323 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
324 ``defines``, holding exactly the same value as accepted in its keyword
327 For example, again for CMS, you want to automatically decode
328 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
329 structures it may hold. Also, automatically decode ``controlSequence``
332 content_info, tail = ContentInfo().decode(data, defines_by_path=(
335 ((("content",), {id_signedData: SignedData()}),),
340 DecodePathDefBy(id_signedData),
345 id_cct_PKIData: PKIData(),
346 id_cct_PKIResponse: PKIResponse(),
352 DecodePathDefBy(id_signedData),
355 DecodePathDefBy(id_cct_PKIResponse),
361 id_cmc_recipientNonce: RecipientNonce(),
362 id_cmc_senderNonce: SenderNonce(),
363 id_cmc_statusInfoV2: CMCStatusInfoV2(),
364 id_cmc_transactionId: TransactionId(),
369 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
370 First function is useful for path construction when some automatic
371 decoding is already done. ``any`` means literally any value it meet --
372 useful for SEQUENCE/SET OF-s.
379 By default PyDERASN accepts only DER encoded data. It always encodes to
380 DER. But you can optionally enable BER decoding with setting ``bered``
381 :ref:`context <ctx>` argument to True. Indefinite lengths and
382 constructed primitive types should be parsed successfully.
384 * If object is encoded in BER form (not the DER one), then ``bered``
385 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
386 STRING``, ``SEQUENCE``, ``SET`` can contain it.
387 * If object has an indefinite length encoding, then its ``lenindef``
388 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
389 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
391 * If object has an indefinite length encoded explicit tag, then
392 ``expl_lenindef`` is set to True.
394 EOC (end-of-contents) token's length is taken in advance in object's
397 .. _allow_expl_oob_ctx:
399 Allow explicit tag out-of-bound
400 -------------------------------
402 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
403 one value, more than one object. If you set ``allow_expl_oob`` context
404 option to True, then no error will be raised and that invalid encoding
405 will be silently further processed. But pay attention that offsets and
406 lengths will be invalid in that case.
410 This option should be used only for skipping some decode errors, just
411 to see the decoded structure somehow.
418 .. autoclass:: pyderasn.Boolean
423 .. autoclass:: pyderasn.Integer
428 .. autoclass:: pyderasn.BitString
433 .. autoclass:: pyderasn.OctetString
438 .. autoclass:: pyderasn.Null
443 .. autoclass:: pyderasn.ObjectIdentifier
448 .. autoclass:: pyderasn.Enumerated
452 .. autoclass:: pyderasn.CommonString
456 .. autoclass:: pyderasn.NumericString
460 .. autoclass:: pyderasn.UTCTime
461 :members: __init__, todatetime
465 .. autoclass:: pyderasn.GeneralizedTime
472 .. autoclass:: pyderasn.Choice
477 .. autoclass:: PrimitiveTypes
481 .. autoclass:: pyderasn.Any
489 .. autoclass:: pyderasn.Sequence
494 .. autoclass:: pyderasn.Set
499 .. autoclass:: pyderasn.SequenceOf
504 .. autoclass:: pyderasn.SetOf
510 .. autofunction:: pyderasn.abs_decode_path
511 .. autofunction:: pyderasn.hexenc
512 .. autofunction:: pyderasn.hexdec
513 .. autofunction:: pyderasn.tag_encode
514 .. autofunction:: pyderasn.tag_decode
515 .. autofunction:: pyderasn.tag_ctxp
516 .. autofunction:: pyderasn.tag_ctxc
517 .. autoclass:: pyderasn.Obj
518 .. autoclass:: pyderasn.DecodeError
520 .. autoclass:: pyderasn.NotEnoughData
521 .. autoclass:: pyderasn.LenIndefForm
522 .. autoclass:: pyderasn.TagMismatch
523 .. autoclass:: pyderasn.InvalidLength
524 .. autoclass:: pyderasn.InvalidOID
525 .. autoclass:: pyderasn.ObjUnknown
526 .. autoclass:: pyderasn.ObjNotReady
527 .. autoclass:: pyderasn.InvalidValueType
528 .. autoclass:: pyderasn.BoundsError
531 from codecs import getdecoder
532 from codecs import getencoder
533 from collections import namedtuple
534 from collections import OrderedDict
535 from datetime import datetime
536 from math import ceil
537 from os import environ
538 from string import digits
540 from six import add_metaclass
541 from six import binary_type
542 from six import byte2int
543 from six import indexbytes
544 from six import int2byte
545 from six import integer_types
546 from six import iterbytes
548 from six import string_types
549 from six import text_type
550 from six.moves import xrange as six_xrange
554 from termcolor import colored
556 def colored(what, *args):
600 "TagClassApplication",
604 "TagFormConstructed",
615 TagClassUniversal = 0
616 TagClassApplication = 1 << 6
617 TagClassContext = 1 << 7
618 TagClassPrivate = 1 << 6 | 1 << 7
620 TagFormConstructed = 1 << 5
623 TagClassApplication: "APPLICATION ",
624 TagClassPrivate: "PRIVATE ",
625 TagClassUniversal: "UNIV ",
629 LENINDEF = b"\x80" # length indefinite mark
630 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
633 ########################################################################
635 ########################################################################
637 class DecodeError(Exception):
638 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
640 :param str msg: reason of decode failing
641 :param klass: optional exact DecodeError inherited class (like
642 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
643 :py:exc:`InvalidLength`)
644 :param decode_path: tuple of strings. It contains human
645 readable names of the fields through which
646 decoding process has passed
647 :param int offset: binary offset where failure happened
649 super(DecodeError, self).__init__()
652 self.decode_path = decode_path
658 "" if self.klass is None else self.klass.__name__,
660 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
661 if len(self.decode_path) > 0 else ""
663 ("(at %d)" % self.offset) if self.offset > 0 else "",
669 return "%s(%s)" % (self.__class__.__name__, self)
672 class NotEnoughData(DecodeError):
676 class LenIndefForm(DecodeError):
680 class TagMismatch(DecodeError):
684 class InvalidLength(DecodeError):
688 class InvalidOID(DecodeError):
692 class ObjUnknown(ValueError):
693 def __init__(self, name):
694 super(ObjUnknown, self).__init__()
698 return "object is unknown: %s" % self.name
701 return "%s(%s)" % (self.__class__.__name__, self)
704 class ObjNotReady(ValueError):
705 def __init__(self, name):
706 super(ObjNotReady, self).__init__()
710 return "object is not ready: %s" % self.name
713 return "%s(%s)" % (self.__class__.__name__, self)
716 class InvalidValueType(ValueError):
717 def __init__(self, expected_types):
718 super(InvalidValueType, self).__init__()
719 self.expected_types = expected_types
722 return "invalid value type, expected: %s" % ", ".join(
723 [repr(t) for t in self.expected_types]
727 return "%s(%s)" % (self.__class__.__name__, self)
730 class BoundsError(ValueError):
731 def __init__(self, bound_min, value, bound_max):
732 super(BoundsError, self).__init__()
733 self.bound_min = bound_min
735 self.bound_max = bound_max
738 return "unsatisfied bounds: %s <= %s <= %s" % (
745 return "%s(%s)" % (self.__class__.__name__, self)
748 ########################################################################
750 ########################################################################
752 _hexdecoder = getdecoder("hex")
753 _hexencoder = getencoder("hex")
757 """Binary data to hexadecimal string convert
759 return _hexdecoder(data)[0]
763 """Hexadecimal string to binary data convert
765 return _hexencoder(data)[0].decode("ascii")
768 def int_bytes_len(num, byte_len=8):
771 return int(ceil(float(num.bit_length()) / byte_len))
774 def zero_ended_encode(num):
775 octets = bytearray(int_bytes_len(num, 7))
777 octets[i] = num & 0x7F
781 octets[i] = 0x80 | (num & 0x7F)
787 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
788 """Encode tag to binary form
790 :param int num: tag's number
791 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
792 :py:data:`pyderasn.TagClassContext`,
793 :py:data:`pyderasn.TagClassApplication`,
794 :py:data:`pyderasn.TagClassPrivate`)
795 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
796 :py:data:`pyderasn.TagFormConstructed`)
800 return int2byte(klass | form | num)
801 # [XX|X|11111][1.......][1.......] ... [0.......]
802 return int2byte(klass | form | 31) + zero_ended_encode(num)
806 """Decode tag from binary form
810 No validation is performed, assuming that it has already passed.
812 It returns tuple with three integers, as
813 :py:func:`pyderasn.tag_encode` accepts.
815 first_octet = byte2int(tag)
816 klass = first_octet & 0xC0
817 form = first_octet & 0x20
818 if first_octet & 0x1F < 0x1F:
819 return (klass, form, first_octet & 0x1F)
821 for octet in iterbytes(tag[1:]):
824 return (klass, form, num)
828 """Create CONTEXT PRIMITIVE tag
830 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
834 """Create CONTEXT CONSTRUCTED tag
836 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
840 """Take off tag from the data
842 :returns: (encoded tag, tag length, remaining data)
845 raise NotEnoughData("no data at all")
846 if byte2int(data) & 0x1F < 31:
847 return data[:1], 1, data[1:]
852 raise DecodeError("unfinished tag")
853 if indexbytes(data, i) & 0x80 == 0:
856 return data[:i], i, data[i:]
862 octets = bytearray(int_bytes_len(l) + 1)
863 octets[0] = 0x80 | (len(octets) - 1)
864 for i in six_xrange(len(octets) - 1, 0, -1):
870 def len_decode(data):
873 :returns: (decoded length, length's length, remaining data)
874 :raises LenIndefForm: if indefinite form encoding is met
877 raise NotEnoughData("no data at all")
878 first_octet = byte2int(data)
879 if first_octet & 0x80 == 0:
880 return first_octet, 1, data[1:]
881 octets_num = first_octet & 0x7F
882 if octets_num + 1 > len(data):
883 raise NotEnoughData("encoded length is longer than data")
886 if byte2int(data[1:]) == 0:
887 raise DecodeError("leading zeros")
889 for v in iterbytes(data[1:1 + octets_num]):
892 raise DecodeError("long form instead of short one")
893 return l, 1 + octets_num, data[1 + octets_num:]
896 ########################################################################
898 ########################################################################
900 class AutoAddSlots(type):
901 def __new__(mcs, name, bases, _dict):
902 _dict["__slots__"] = _dict.get("__slots__", ())
903 return type.__new__(mcs, name, bases, _dict)
906 @add_metaclass(AutoAddSlots)
908 """Common ASN.1 object class
910 All ASN.1 types are inherited from it. It has metaclass that
911 automatically adds ``__slots__`` to all inherited classes.
935 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
936 self._expl = getattr(self, "expl", None) if expl is None else expl
937 if self.tag != self.tag_default and self._expl is not None:
938 raise ValueError("implicit and explicit tags can not be set simultaneously")
939 if default is not None:
941 self.optional = optional
942 self.offset, self.llen, self.vlen = _decoded
944 self.expl_lenindef = False
945 self.lenindef = False
949 def ready(self): # pragma: no cover
950 """Is object ready to be encoded?
952 raise NotImplementedError()
954 def _assert_ready(self):
956 raise ObjNotReady(self.__class__.__name__)
960 """Is object decoded?
962 return (self.llen + self.vlen) > 0
964 def copy(self): # pragma: no cover
965 """Make a copy of object, safe to be mutated
967 raise NotImplementedError()
975 return self.tlen + self.llen + self.vlen
977 def __str__(self): # pragma: no cover
978 return self.__bytes__() if PY2 else self.__unicode__()
980 def __ne__(self, their):
981 return not(self == their)
983 def __gt__(self, their): # pragma: no cover
984 return not(self < their)
986 def __le__(self, their): # pragma: no cover
987 return (self == their) or (self < their)
989 def __ge__(self, their): # pragma: no cover
990 return (self == their) or (self > their)
992 def _encode(self): # pragma: no cover
993 raise NotImplementedError()
995 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
996 raise NotImplementedError()
1000 if self._expl is None:
1002 return b"".join((self._expl, len_encode(len(raw)), raw))
1015 :param data: either binary or memoryview
1016 :param int offset: initial data's offset
1017 :param bool leavemm: do we need to leave memoryview of remaining
1018 data as is, or convert it to bytes otherwise
1019 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1020 :param tag_only: decode only the tag, without length and contents
1021 (used only in Choice and Set structures, trying to
1022 determine if tag satisfies the scheme)
1023 :returns: (Obj, remaining data)
1027 tlv = memoryview(data)
1028 if self._expl is None:
1029 result = self._decode(
1032 decode_path=decode_path,
1041 t, tlen, lv = tag_strip(tlv)
1042 except DecodeError as err:
1043 raise err.__class__(
1045 klass=self.__class__,
1046 decode_path=decode_path,
1051 klass=self.__class__,
1052 decode_path=decode_path,
1056 l, llen, v = len_decode(lv)
1057 except LenIndefForm as err:
1058 if not ctx.get("bered", False):
1059 raise err.__class__(
1061 klass=self.__class__,
1062 decode_path=decode_path,
1066 offset += tlen + llen
1067 result = self._decode(
1070 decode_path=decode_path,
1077 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1078 if eoc_expected.tobytes() != EOC:
1081 klass=self.__class__,
1082 decode_path=decode_path,
1086 obj.expl_lenindef = True
1087 except DecodeError as err:
1088 raise err.__class__(
1090 klass=self.__class__,
1091 decode_path=decode_path,
1096 raise NotEnoughData(
1097 "encoded length is longer than data",
1098 klass=self.__class__,
1099 decode_path=decode_path,
1102 result = self._decode(
1104 offset=offset + tlen + llen,
1105 decode_path=decode_path,
1112 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1114 "explicit tag out-of-bound, longer than data",
1115 klass=self.__class__,
1116 decode_path=decode_path,
1119 return obj, (tail if leavemm else tail.tobytes())
1123 return self._expl is not None
1130 def expl_tlen(self):
1131 return len(self._expl)
1134 def expl_llen(self):
1135 if self.expl_lenindef:
1137 return len(len_encode(self.tlvlen))
1140 def expl_offset(self):
1141 return self.offset - self.expl_tlen - self.expl_llen
1144 def expl_vlen(self):
1148 def expl_tlvlen(self):
1149 return self.expl_tlen + self.expl_llen + self.expl_vlen
1152 def fulloffset(self):
1153 return self.expl_offset if self.expled else self.offset
1157 return self.expl_tlvlen if self.expled else self.tlvlen
1159 def pps_lenindef(self, decode_path):
1162 asn1_type_name="EOC",
1164 decode_path=decode_path,
1166 self.offset + self.tlvlen -
1167 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1174 if self.expl_lenindef:
1176 asn1_type_name="EOC",
1177 obj_name="EXPLICIT",
1178 decode_path=decode_path,
1179 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1187 class DecodePathDefBy(object):
1188 """DEFINED BY representation inside decode path
1190 __slots__ = ("defined_by",)
1192 def __init__(self, defined_by):
1193 self.defined_by = defined_by
1195 def __ne__(self, their):
1196 return not(self == their)
1198 def __eq__(self, their):
1199 if not isinstance(their, self.__class__):
1201 return self.defined_by == their.defined_by
1204 return "DEFINED BY " + str(self.defined_by)
1207 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1210 ########################################################################
1212 ########################################################################
1214 PP = namedtuple("PP", (
1239 asn1_type_name="unknown",
1256 expl_lenindef=False,
1284 def _colourize(what, colour, with_colours, attrs=("bold",)):
1285 return colored(what, colour, attrs=attrs) if with_colours else what
1294 with_decode_path=False,
1295 decode_path_len_decrease=0,
1302 " " if pp.expl_offset is None else
1303 ("-%d" % (pp.offset - pp.expl_offset))
1305 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1307 cols.append(_colourize(col, "red", with_colours, ()))
1308 col = "[%d,%d,%4d]%s" % (
1312 LENINDEF_PP_CHAR if pp.lenindef else " "
1314 col = _colourize(col, "green", with_colours, ())
1316 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1317 if decode_path_len > 0:
1318 cols.append(" ." * decode_path_len)
1319 ent = pp.decode_path[-1]
1320 if isinstance(ent, DecodePathDefBy):
1321 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1322 value = str(ent.defined_by)
1324 oids is not None and
1325 ent.defined_by.asn1_type_name ==
1326 ObjectIdentifier.asn1_type_name and
1329 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1331 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1333 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1334 if pp.expl is not None:
1335 klass, _, num = pp.expl
1336 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1337 cols.append(_colourize(col, "blue", with_colours))
1338 if pp.impl is not None:
1339 klass, _, num = pp.impl
1340 col = "[%s%d]" % (TagClassReprs[klass], num)
1341 cols.append(_colourize(col, "blue", with_colours))
1342 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1343 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1345 cols.append(_colourize("BER", "red", with_colours))
1346 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1347 if pp.value is not None:
1349 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1351 oids is not None and
1352 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1355 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1357 if isinstance(pp.blob, binary_type):
1358 cols.append(hexenc(pp.blob))
1359 elif isinstance(pp.blob, tuple):
1360 cols.append(", ".join(pp.blob))
1362 cols.append(_colourize("OPTIONAL", "red", with_colours))
1364 cols.append(_colourize("DEFAULT", "red", with_colours))
1365 if with_decode_path:
1366 cols.append(_colourize(
1367 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1371 return " ".join(cols)
1374 def pp_console_blob(pp, decode_path_len_decrease=0):
1375 cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
1376 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1377 if decode_path_len > 0:
1378 cols.append(" ." * (decode_path_len + 1))
1379 if isinstance(pp.blob, binary_type):
1380 blob = hexenc(pp.blob).upper()
1381 for i in range(0, len(blob), 32):
1382 chunk = blob[i:i + 32]
1383 yield " ".join(cols + [":".join(
1384 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1386 elif isinstance(pp.blob, tuple):
1387 yield " ".join(cols + [", ".join(pp.blob)])
1395 with_decode_path=False,
1396 decode_path_only=(),
1398 """Pretty print object
1400 :param Obj obj: object you want to pretty print
1401 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1402 from it is met, then its humand readable form is printed
1403 :param big_blobs: if large binary objects are met (like OctetString
1404 values), do we need to print them too, on separate
1406 :param with_colours: colourize output, if ``termcolor`` library
1408 :param with_decode_path: print decode path
1409 :param decode_path_only: print only that specified decode path
1411 def _pprint_pps(pps):
1413 if hasattr(pp, "_fields"):
1415 decode_path_only != () and
1417 str(p) for p in pp.decode_path[:len(decode_path_only)]
1418 ) != decode_path_only
1422 yield pp_console_row(
1427 with_colours=with_colours,
1428 with_decode_path=with_decode_path,
1429 decode_path_len_decrease=len(decode_path_only),
1431 for row in pp_console_blob(
1433 decode_path_len_decrease=len(decode_path_only),
1437 yield pp_console_row(
1442 with_colours=with_colours,
1443 with_decode_path=with_decode_path,
1444 decode_path_len_decrease=len(decode_path_only),
1447 for row in _pprint_pps(pp):
1449 return "\n".join(_pprint_pps(obj.pps()))
1452 ########################################################################
1453 # ASN.1 primitive types
1454 ########################################################################
1457 """``BOOLEAN`` boolean type
1459 >>> b = Boolean(True)
1461 >>> b == Boolean(True)
1467 tag_default = tag_encode(1)
1468 asn1_type_name = "BOOLEAN"
1480 :param value: set the value. Either boolean type, or
1481 :py:class:`pyderasn.Boolean` object
1482 :param bytes impl: override default tag with ``IMPLICIT`` one
1483 :param bytes expl: override default tag with ``EXPLICIT`` one
1484 :param default: set default value. Type same as in ``value``
1485 :param bool optional: is object ``OPTIONAL`` in sequence
1487 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1488 self._value = None if value is None else self._value_sanitize(value)
1489 if default is not None:
1490 default = self._value_sanitize(default)
1491 self.default = self.__class__(
1497 self._value = default
1499 def _value_sanitize(self, value):
1500 if issubclass(value.__class__, Boolean):
1502 if isinstance(value, bool):
1504 raise InvalidValueType((self.__class__, bool))
1508 return self._value is not None
1511 obj = self.__class__()
1512 obj._value = self._value
1514 obj._expl = self._expl
1515 obj.default = self.default
1516 obj.optional = self.optional
1517 obj.offset = self.offset
1518 obj.llen = self.llen
1519 obj.vlen = self.vlen
1522 def __nonzero__(self):
1523 self._assert_ready()
1527 self._assert_ready()
1530 def __eq__(self, their):
1531 if isinstance(their, bool):
1532 return self._value == their
1533 if not issubclass(their.__class__, Boolean):
1536 self._value == their._value and
1537 self.tag == their.tag and
1538 self._expl == their._expl
1549 return self.__class__(
1551 impl=self.tag if impl is None else impl,
1552 expl=self._expl if expl is None else expl,
1553 default=self.default if default is None else default,
1554 optional=self.optional if optional is None else optional,
1558 self._assert_ready()
1562 (b"\xFF" if self._value else b"\x00"),
1565 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1567 t, _, lv = tag_strip(tlv)
1568 except DecodeError as err:
1569 raise err.__class__(
1571 klass=self.__class__,
1572 decode_path=decode_path,
1577 klass=self.__class__,
1578 decode_path=decode_path,
1584 l, _, v = len_decode(lv)
1585 except DecodeError as err:
1586 raise err.__class__(
1588 klass=self.__class__,
1589 decode_path=decode_path,
1593 raise InvalidLength(
1594 "Boolean's length must be equal to 1",
1595 klass=self.__class__,
1596 decode_path=decode_path,
1600 raise NotEnoughData(
1601 "encoded length is longer than data",
1602 klass=self.__class__,
1603 decode_path=decode_path,
1606 first_octet = byte2int(v)
1608 if first_octet == 0:
1610 elif first_octet == 0xFF:
1612 elif ctx.get("bered", False):
1617 "unacceptable Boolean value",
1618 klass=self.__class__,
1619 decode_path=decode_path,
1622 obj = self.__class__(
1626 default=self.default,
1627 optional=self.optional,
1628 _decoded=(offset, 1, 1),
1634 return pp_console_row(next(self.pps()))
1636 def pps(self, decode_path=()):
1638 asn1_type_name=self.asn1_type_name,
1639 obj_name=self.__class__.__name__,
1640 decode_path=decode_path,
1641 value=str(self._value) if self.ready else None,
1642 optional=self.optional,
1643 default=self == self.default,
1644 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1645 expl=None if self._expl is None else tag_decode(self._expl),
1650 expl_offset=self.expl_offset if self.expled else None,
1651 expl_tlen=self.expl_tlen if self.expled else None,
1652 expl_llen=self.expl_llen if self.expled else None,
1653 expl_vlen=self.expl_vlen if self.expled else None,
1654 expl_lenindef=self.expl_lenindef,
1657 for pp in self.pps_lenindef(decode_path):
1662 """``INTEGER`` integer type
1664 >>> b = Integer(-123)
1666 >>> b == Integer(-123)
1671 >>> Integer(2, bounds=(1, 3))
1673 >>> Integer(5, bounds=(1, 3))
1674 Traceback (most recent call last):
1675 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1679 class Version(Integer):
1686 >>> v = Version("v1")
1693 {'v3': 2, 'v1': 0, 'v2': 1}
1695 __slots__ = ("specs", "_bound_min", "_bound_max")
1696 tag_default = tag_encode(2)
1697 asn1_type_name = "INTEGER"
1711 :param value: set the value. Either integer type, named value
1712 (if ``schema`` is specified in the class), or
1713 :py:class:`pyderasn.Integer` object
1714 :param bounds: set ``(MIN, MAX)`` value constraint.
1715 (-inf, +inf) by default
1716 :param bytes impl: override default tag with ``IMPLICIT`` one
1717 :param bytes expl: override default tag with ``EXPLICIT`` one
1718 :param default: set default value. Type same as in ``value``
1719 :param bool optional: is object ``OPTIONAL`` in sequence
1721 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1723 specs = getattr(self, "schema", {}) if _specs is None else _specs
1724 self.specs = specs if isinstance(specs, dict) else dict(specs)
1725 self._bound_min, self._bound_max = getattr(
1728 (float("-inf"), float("+inf")),
1729 ) if bounds is None else bounds
1730 if value is not None:
1731 self._value = self._value_sanitize(value)
1732 if default is not None:
1733 default = self._value_sanitize(default)
1734 self.default = self.__class__(
1740 if self._value is None:
1741 self._value = default
1743 def _value_sanitize(self, value):
1744 if issubclass(value.__class__, Integer):
1745 value = value._value
1746 elif isinstance(value, integer_types):
1748 elif isinstance(value, str):
1749 value = self.specs.get(value)
1751 raise ObjUnknown("integer value: %s" % value)
1753 raise InvalidValueType((self.__class__, int, str))
1754 if not self._bound_min <= value <= self._bound_max:
1755 raise BoundsError(self._bound_min, value, self._bound_max)
1760 return self._value is not None
1763 obj = self.__class__(_specs=self.specs)
1764 obj._value = self._value
1765 obj._bound_min = self._bound_min
1766 obj._bound_max = self._bound_max
1768 obj._expl = self._expl
1769 obj.default = self.default
1770 obj.optional = self.optional
1771 obj.offset = self.offset
1772 obj.llen = self.llen
1773 obj.vlen = self.vlen
1777 self._assert_ready()
1778 return int(self._value)
1781 self._assert_ready()
1784 bytes(self._expl or b"") +
1785 str(self._value).encode("ascii"),
1788 def __eq__(self, their):
1789 if isinstance(their, integer_types):
1790 return self._value == their
1791 if not issubclass(their.__class__, Integer):
1794 self._value == their._value and
1795 self.tag == their.tag and
1796 self._expl == their._expl
1799 def __lt__(self, their):
1800 return self._value < their._value
1804 for name, value in self.specs.items():
1805 if value == self._value:
1817 return self.__class__(
1820 (self._bound_min, self._bound_max)
1821 if bounds is None else bounds
1823 impl=self.tag if impl is None else impl,
1824 expl=self._expl if expl is None else expl,
1825 default=self.default if default is None else default,
1826 optional=self.optional if optional is None else optional,
1831 self._assert_ready()
1835 octets = bytearray([0])
1839 octets = bytearray()
1841 octets.append((value & 0xFF) ^ 0xFF)
1843 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1846 octets = bytearray()
1848 octets.append(value & 0xFF)
1850 if octets[-1] & 0x80 > 0:
1853 octets = bytes(octets)
1855 bytes_len = ceil(value.bit_length() / 8) or 1
1858 octets = value.to_bytes(
1863 except OverflowError:
1867 return b"".join((self.tag, len_encode(len(octets)), octets))
1869 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1871 t, _, lv = tag_strip(tlv)
1872 except DecodeError as err:
1873 raise err.__class__(
1875 klass=self.__class__,
1876 decode_path=decode_path,
1881 klass=self.__class__,
1882 decode_path=decode_path,
1888 l, llen, v = len_decode(lv)
1889 except DecodeError as err:
1890 raise err.__class__(
1892 klass=self.__class__,
1893 decode_path=decode_path,
1897 raise NotEnoughData(
1898 "encoded length is longer than data",
1899 klass=self.__class__,
1900 decode_path=decode_path,
1904 raise NotEnoughData(
1906 klass=self.__class__,
1907 decode_path=decode_path,
1910 v, tail = v[:l], v[l:]
1911 first_octet = byte2int(v)
1913 second_octet = byte2int(v[1:])
1915 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1916 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1919 "non normalized integer",
1920 klass=self.__class__,
1921 decode_path=decode_path,
1926 if first_octet & 0x80 > 0:
1927 octets = bytearray()
1928 for octet in bytearray(v):
1929 octets.append(octet ^ 0xFF)
1930 for octet in octets:
1931 value = (value << 8) | octet
1935 for octet in bytearray(v):
1936 value = (value << 8) | octet
1938 value = int.from_bytes(v, byteorder="big", signed=True)
1940 obj = self.__class__(
1942 bounds=(self._bound_min, self._bound_max),
1945 default=self.default,
1946 optional=self.optional,
1948 _decoded=(offset, llen, l),
1950 except BoundsError as err:
1953 klass=self.__class__,
1954 decode_path=decode_path,
1960 return pp_console_row(next(self.pps()))
1962 def pps(self, decode_path=()):
1964 asn1_type_name=self.asn1_type_name,
1965 obj_name=self.__class__.__name__,
1966 decode_path=decode_path,
1967 value=(self.named or str(self._value)) if self.ready else None,
1968 optional=self.optional,
1969 default=self == self.default,
1970 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1971 expl=None if self._expl is None else tag_decode(self._expl),
1976 expl_offset=self.expl_offset if self.expled else None,
1977 expl_tlen=self.expl_tlen if self.expled else None,
1978 expl_llen=self.expl_llen if self.expled else None,
1979 expl_vlen=self.expl_vlen if self.expled else None,
1980 expl_lenindef=self.expl_lenindef,
1982 for pp in self.pps_lenindef(decode_path):
1986 class BitString(Obj):
1987 """``BIT STRING`` bit string type
1989 >>> BitString(b"hello world")
1990 BIT STRING 88 bits 68656c6c6f20776f726c64
1993 >>> b == b"hello world"
1998 >>> BitString("'0A3B5F291CD'H")
1999 BIT STRING 44 bits 0a3b5f291cd0
2000 >>> b = BitString("'010110000000'B")
2001 BIT STRING 12 bits 5800
2004 >>> b[0], b[1], b[2], b[3]
2005 (False, True, False, True)
2009 [False, True, False, True, True, False, False, False, False, False, False, False]
2013 class KeyUsage(BitString):
2015 ("digitalSignature", 0),
2016 ("nonRepudiation", 1),
2017 ("keyEncipherment", 2),
2020 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2021 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2023 ['nonRepudiation', 'keyEncipherment']
2025 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2029 Pay attention that BIT STRING can be encoded both in primitive
2030 and constructed forms. Decoder always checks constructed form tag
2031 additionally to specified primitive one. If BER decoding is
2032 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2033 of DER restrictions.
2035 __slots__ = ("tag_constructed", "specs", "defined")
2036 tag_default = tag_encode(3)
2037 asn1_type_name = "BIT STRING"
2050 :param value: set the value. Either binary type, tuple of named
2051 values (if ``schema`` is specified in the class),
2052 string in ``'XXX...'B`` form, or
2053 :py:class:`pyderasn.BitString` object
2054 :param bytes impl: override default tag with ``IMPLICIT`` one
2055 :param bytes expl: override default tag with ``EXPLICIT`` one
2056 :param default: set default value. Type same as in ``value``
2057 :param bool optional: is object ``OPTIONAL`` in sequence
2059 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2060 specs = getattr(self, "schema", {}) if _specs is None else _specs
2061 self.specs = specs if isinstance(specs, dict) else dict(specs)
2062 self._value = None if value is None else self._value_sanitize(value)
2063 if default is not None:
2064 default = self._value_sanitize(default)
2065 self.default = self.__class__(
2071 self._value = default
2073 tag_klass, _, tag_num = tag_decode(self.tag)
2074 self.tag_constructed = tag_encode(
2076 form=TagFormConstructed,
2080 def _bits2octets(self, bits):
2081 if len(self.specs) > 0:
2082 bits = bits.rstrip("0")
2084 bits += "0" * ((8 - (bit_len % 8)) % 8)
2085 octets = bytearray(len(bits) // 8)
2086 for i in six_xrange(len(octets)):
2087 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2088 return bit_len, bytes(octets)
2090 def _value_sanitize(self, value):
2091 if issubclass(value.__class__, BitString):
2093 if isinstance(value, (string_types, binary_type)):
2095 isinstance(value, string_types) and
2096 value.startswith("'")
2098 if value.endswith("'B"):
2100 if not set(value) <= set(("0", "1")):
2101 raise ValueError("B's coding contains unacceptable chars")
2102 return self._bits2octets(value)
2103 elif value.endswith("'H"):
2107 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2109 if isinstance(value, binary_type):
2110 return (len(value) * 8, value)
2112 raise InvalidValueType((self.__class__, string_types, binary_type))
2113 if isinstance(value, tuple):
2116 isinstance(value[0], integer_types) and
2117 isinstance(value[1], binary_type)
2122 bit = self.specs.get(name)
2124 raise ObjUnknown("BitString value: %s" % name)
2127 return self._bits2octets("")
2129 return self._bits2octets("".join(
2130 ("1" if bit in bits else "0")
2131 for bit in six_xrange(max(bits) + 1)
2133 raise InvalidValueType((self.__class__, binary_type, string_types))
2137 return self._value is not None
2140 obj = self.__class__(_specs=self.specs)
2142 if value is not None:
2143 value = (value[0], value[1])
2146 obj._expl = self._expl
2147 obj.default = self.default
2148 obj.optional = self.optional
2149 obj.offset = self.offset
2150 obj.llen = self.llen
2151 obj.vlen = self.vlen
2155 self._assert_ready()
2156 for i in six_xrange(self._value[0]):
2161 self._assert_ready()
2162 return self._value[0]
2164 def __bytes__(self):
2165 self._assert_ready()
2166 return self._value[1]
2168 def __eq__(self, their):
2169 if isinstance(their, bytes):
2170 return self._value[1] == their
2171 if not issubclass(their.__class__, BitString):
2174 self._value == their._value and
2175 self.tag == their.tag and
2176 self._expl == their._expl
2181 return [name for name, bit in self.specs.items() if self[bit]]
2191 return self.__class__(
2193 impl=self.tag if impl is None else impl,
2194 expl=self._expl if expl is None else expl,
2195 default=self.default if default is None else default,
2196 optional=self.optional if optional is None else optional,
2200 def __getitem__(self, key):
2201 if isinstance(key, int):
2202 bit_len, octets = self._value
2206 byte2int(memoryview(octets)[key // 8:]) >>
2209 if isinstance(key, string_types):
2210 value = self.specs.get(key)
2212 raise ObjUnknown("BitString value: %s" % key)
2214 raise InvalidValueType((int, str))
2217 self._assert_ready()
2218 bit_len, octets = self._value
2221 len_encode(len(octets) + 1),
2222 int2byte((8 - bit_len % 8) % 8),
2226 def _decode_chunk(self, lv, offset, decode_path, ctx):
2228 l, llen, v = len_decode(lv)
2229 except DecodeError as err:
2230 raise err.__class__(
2232 klass=self.__class__,
2233 decode_path=decode_path,
2237 raise NotEnoughData(
2238 "encoded length is longer than data",
2239 klass=self.__class__,
2240 decode_path=decode_path,
2244 raise NotEnoughData(
2246 klass=self.__class__,
2247 decode_path=decode_path,
2250 pad_size = byte2int(v)
2251 if l == 1 and pad_size != 0:
2253 "invalid empty value",
2254 klass=self.__class__,
2255 decode_path=decode_path,
2261 klass=self.__class__,
2262 decode_path=decode_path,
2265 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2268 klass=self.__class__,
2269 decode_path=decode_path,
2272 v, tail = v[:l], v[l:]
2273 obj = self.__class__(
2274 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2277 default=self.default,
2278 optional=self.optional,
2280 _decoded=(offset, llen, l),
2284 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2286 t, tlen, lv = tag_strip(tlv)
2287 except DecodeError as err:
2288 raise err.__class__(
2290 klass=self.__class__,
2291 decode_path=decode_path,
2297 return self._decode_chunk(lv, offset, decode_path, ctx)
2298 if t == self.tag_constructed:
2299 if not ctx.get("bered", False):
2301 "unallowed BER constructed encoding",
2302 klass=self.__class__,
2303 decode_path=decode_path,
2310 l, llen, v = len_decode(lv)
2311 except LenIndefForm:
2312 llen, l, v = 1, 0, lv[1:]
2314 except DecodeError as err:
2315 raise err.__class__(
2317 klass=self.__class__,
2318 decode_path=decode_path,
2322 raise NotEnoughData(
2323 "encoded length is longer than data",
2324 klass=self.__class__,
2325 decode_path=decode_path,
2328 if not lenindef and l == 0:
2329 raise NotEnoughData(
2331 klass=self.__class__,
2332 decode_path=decode_path,
2336 sub_offset = offset + tlen + llen
2340 if v[:EOC_LEN].tobytes() == EOC:
2347 "chunk out of bounds",
2348 klass=self.__class__,
2349 decode_path=decode_path + (str(len(chunks) - 1),),
2350 offset=chunks[-1].offset,
2352 sub_decode_path = decode_path + (str(len(chunks)),)
2354 chunk, v_tail = BitString().decode(
2357 decode_path=sub_decode_path,
2363 "expected BitString encoded chunk",
2364 klass=self.__class__,
2365 decode_path=sub_decode_path,
2368 chunks.append(chunk)
2369 sub_offset += chunk.tlvlen
2370 vlen += chunk.tlvlen
2372 if len(chunks) == 0:
2375 klass=self.__class__,
2376 decode_path=decode_path,
2381 for chunk_i, chunk in enumerate(chunks[:-1]):
2382 if chunk.bit_len % 8 != 0:
2384 "BitString chunk is not multiple of 8 bits",
2385 klass=self.__class__,
2386 decode_path=decode_path + (str(chunk_i),),
2387 offset=chunk.offset,
2389 values.append(bytes(chunk))
2390 bit_len += chunk.bit_len
2391 chunk_last = chunks[-1]
2392 values.append(bytes(chunk_last))
2393 bit_len += chunk_last.bit_len
2394 obj = self.__class__(
2395 value=(bit_len, b"".join(values)),
2398 default=self.default,
2399 optional=self.optional,
2401 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2403 obj.lenindef = lenindef
2405 return obj, (v[EOC_LEN:] if lenindef else v)
2407 klass=self.__class__,
2408 decode_path=decode_path,
2413 return pp_console_row(next(self.pps()))
2415 def pps(self, decode_path=()):
2419 bit_len, blob = self._value
2420 value = "%d bits" % bit_len
2421 if len(self.specs) > 0:
2422 blob = tuple(self.named)
2424 asn1_type_name=self.asn1_type_name,
2425 obj_name=self.__class__.__name__,
2426 decode_path=decode_path,
2429 optional=self.optional,
2430 default=self == self.default,
2431 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2432 expl=None if self._expl is None else tag_decode(self._expl),
2437 expl_offset=self.expl_offset if self.expled else None,
2438 expl_tlen=self.expl_tlen if self.expled else None,
2439 expl_llen=self.expl_llen if self.expled else None,
2440 expl_vlen=self.expl_vlen if self.expled else None,
2441 expl_lenindef=self.expl_lenindef,
2442 lenindef=self.lenindef,
2445 defined_by, defined = self.defined or (None, None)
2446 if defined_by is not None:
2448 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2450 for pp in self.pps_lenindef(decode_path):
2454 class OctetString(Obj):
2455 """``OCTET STRING`` binary string type
2457 >>> s = OctetString(b"hello world")
2458 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2459 >>> s == OctetString(b"hello world")
2464 >>> OctetString(b"hello", bounds=(4, 4))
2465 Traceback (most recent call last):
2466 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2467 >>> OctetString(b"hell", bounds=(4, 4))
2468 OCTET STRING 4 bytes 68656c6c
2472 Pay attention that OCTET STRING can be encoded both in primitive
2473 and constructed forms. Decoder always checks constructed form tag
2474 additionally to specified primitive one. If BER decoding is
2475 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2476 of DER restrictions.
2478 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2479 tag_default = tag_encode(4)
2480 asn1_type_name = "OCTET STRING"
2493 :param value: set the value. Either binary type, or
2494 :py:class:`pyderasn.OctetString` object
2495 :param bounds: set ``(MIN, MAX)`` value size constraint.
2496 (-inf, +inf) by default
2497 :param bytes impl: override default tag with ``IMPLICIT`` one
2498 :param bytes expl: override default tag with ``EXPLICIT`` one
2499 :param default: set default value. Type same as in ``value``
2500 :param bool optional: is object ``OPTIONAL`` in sequence
2502 super(OctetString, self).__init__(
2510 self._bound_min, self._bound_max = getattr(
2514 ) if bounds is None else bounds
2515 if value is not None:
2516 self._value = self._value_sanitize(value)
2517 if default is not None:
2518 default = self._value_sanitize(default)
2519 self.default = self.__class__(
2524 if self._value is None:
2525 self._value = default
2527 tag_klass, _, tag_num = tag_decode(self.tag)
2528 self.tag_constructed = tag_encode(
2530 form=TagFormConstructed,
2534 def _value_sanitize(self, value):
2535 if issubclass(value.__class__, OctetString):
2536 value = value._value
2537 elif isinstance(value, binary_type):
2540 raise InvalidValueType((self.__class__, bytes))
2541 if not self._bound_min <= len(value) <= self._bound_max:
2542 raise BoundsError(self._bound_min, len(value), self._bound_max)
2547 return self._value is not None
2550 obj = self.__class__()
2551 obj._value = self._value
2552 obj._bound_min = self._bound_min
2553 obj._bound_max = self._bound_max
2555 obj._expl = self._expl
2556 obj.default = self.default
2557 obj.optional = self.optional
2558 obj.offset = self.offset
2559 obj.llen = self.llen
2560 obj.vlen = self.vlen
2563 def __bytes__(self):
2564 self._assert_ready()
2567 def __eq__(self, their):
2568 if isinstance(their, binary_type):
2569 return self._value == their
2570 if not issubclass(their.__class__, OctetString):
2573 self._value == their._value and
2574 self.tag == their.tag and
2575 self._expl == their._expl
2578 def __lt__(self, their):
2579 return self._value < their._value
2590 return self.__class__(
2593 (self._bound_min, self._bound_max)
2594 if bounds is None else bounds
2596 impl=self.tag if impl is None else impl,
2597 expl=self._expl if expl is None else expl,
2598 default=self.default if default is None else default,
2599 optional=self.optional if optional is None else optional,
2603 self._assert_ready()
2606 len_encode(len(self._value)),
2610 def _decode_chunk(self, lv, offset, decode_path, ctx):
2612 l, llen, v = len_decode(lv)
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,
2627 v, tail = v[:l], v[l:]
2629 obj = self.__class__(
2631 bounds=(self._bound_min, self._bound_max),
2634 default=self.default,
2635 optional=self.optional,
2636 _decoded=(offset, llen, l),
2638 except DecodeError as err:
2641 klass=self.__class__,
2642 decode_path=decode_path,
2645 except BoundsError as err:
2648 klass=self.__class__,
2649 decode_path=decode_path,
2654 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2656 t, tlen, lv = tag_strip(tlv)
2657 except DecodeError as err:
2658 raise err.__class__(
2660 klass=self.__class__,
2661 decode_path=decode_path,
2667 return self._decode_chunk(lv, offset, decode_path, ctx)
2668 if t == self.tag_constructed:
2669 if not ctx.get("bered", False):
2671 "unallowed BER constructed encoding",
2672 klass=self.__class__,
2673 decode_path=decode_path,
2680 l, llen, v = len_decode(lv)
2681 except LenIndefForm:
2682 llen, l, v = 1, 0, lv[1:]
2684 except DecodeError as err:
2685 raise err.__class__(
2687 klass=self.__class__,
2688 decode_path=decode_path,
2692 raise NotEnoughData(
2693 "encoded length is longer than data",
2694 klass=self.__class__,
2695 decode_path=decode_path,
2699 sub_offset = offset + tlen + llen
2703 if v[:EOC_LEN].tobytes() == EOC:
2710 "chunk out of bounds",
2711 klass=self.__class__,
2712 decode_path=decode_path + (str(len(chunks) - 1),),
2713 offset=chunks[-1].offset,
2715 sub_decode_path = decode_path + (str(len(chunks)),)
2717 chunk, v_tail = OctetString().decode(
2720 decode_path=sub_decode_path,
2726 "expected OctetString encoded chunk",
2727 klass=self.__class__,
2728 decode_path=sub_decode_path,
2731 chunks.append(chunk)
2732 sub_offset += chunk.tlvlen
2733 vlen += chunk.tlvlen
2736 obj = self.__class__(
2737 value=b"".join(bytes(chunk) for chunk in chunks),
2738 bounds=(self._bound_min, self._bound_max),
2741 default=self.default,
2742 optional=self.optional,
2743 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2745 except DecodeError as err:
2748 klass=self.__class__,
2749 decode_path=decode_path,
2752 except BoundsError as err:
2755 klass=self.__class__,
2756 decode_path=decode_path,
2759 obj.lenindef = lenindef
2761 return obj, (v[EOC_LEN:] if lenindef else v)
2763 klass=self.__class__,
2764 decode_path=decode_path,
2769 return pp_console_row(next(self.pps()))
2771 def pps(self, decode_path=()):
2773 asn1_type_name=self.asn1_type_name,
2774 obj_name=self.__class__.__name__,
2775 decode_path=decode_path,
2776 value=("%d bytes" % len(self._value)) if self.ready else None,
2777 blob=self._value if self.ready else None,
2778 optional=self.optional,
2779 default=self == self.default,
2780 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2781 expl=None if self._expl is None else tag_decode(self._expl),
2786 expl_offset=self.expl_offset if self.expled else None,
2787 expl_tlen=self.expl_tlen if self.expled else None,
2788 expl_llen=self.expl_llen if self.expled else None,
2789 expl_vlen=self.expl_vlen if self.expled else None,
2790 expl_lenindef=self.expl_lenindef,
2791 lenindef=self.lenindef,
2794 defined_by, defined = self.defined or (None, None)
2795 if defined_by is not None:
2797 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2799 for pp in self.pps_lenindef(decode_path):
2804 """``NULL`` null object
2812 tag_default = tag_encode(5)
2813 asn1_type_name = "NULL"
2817 value=None, # unused, but Sequence passes it
2824 :param bytes impl: override default tag with ``IMPLICIT`` one
2825 :param bytes expl: override default tag with ``EXPLICIT`` one
2826 :param bool optional: is object ``OPTIONAL`` in sequence
2828 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2836 obj = self.__class__()
2838 obj._expl = self._expl
2839 obj.default = self.default
2840 obj.optional = self.optional
2841 obj.offset = self.offset
2842 obj.llen = self.llen
2843 obj.vlen = self.vlen
2846 def __eq__(self, their):
2847 if not issubclass(their.__class__, Null):
2850 self.tag == their.tag and
2851 self._expl == their._expl
2861 return self.__class__(
2862 impl=self.tag if impl is None else impl,
2863 expl=self._expl if expl is None else expl,
2864 optional=self.optional if optional is None else optional,
2868 return self.tag + len_encode(0)
2870 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2872 t, _, lv = tag_strip(tlv)
2873 except DecodeError as err:
2874 raise err.__class__(
2876 klass=self.__class__,
2877 decode_path=decode_path,
2882 klass=self.__class__,
2883 decode_path=decode_path,
2889 l, _, v = len_decode(lv)
2890 except DecodeError as err:
2891 raise err.__class__(
2893 klass=self.__class__,
2894 decode_path=decode_path,
2898 raise InvalidLength(
2899 "Null must have zero length",
2900 klass=self.__class__,
2901 decode_path=decode_path,
2904 obj = self.__class__(
2907 optional=self.optional,
2908 _decoded=(offset, 1, 0),
2913 return pp_console_row(next(self.pps()))
2915 def pps(self, decode_path=()):
2917 asn1_type_name=self.asn1_type_name,
2918 obj_name=self.__class__.__name__,
2919 decode_path=decode_path,
2920 optional=self.optional,
2921 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2922 expl=None if self._expl is None else tag_decode(self._expl),
2927 expl_offset=self.expl_offset if self.expled else None,
2928 expl_tlen=self.expl_tlen if self.expled else None,
2929 expl_llen=self.expl_llen if self.expled else None,
2930 expl_vlen=self.expl_vlen if self.expled else None,
2931 expl_lenindef=self.expl_lenindef,
2933 for pp in self.pps_lenindef(decode_path):
2937 class ObjectIdentifier(Obj):
2938 """``OBJECT IDENTIFIER`` OID type
2940 >>> oid = ObjectIdentifier((1, 2, 3))
2941 OBJECT IDENTIFIER 1.2.3
2942 >>> oid == ObjectIdentifier("1.2.3")
2948 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2949 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2951 >>> str(ObjectIdentifier((3, 1)))
2952 Traceback (most recent call last):
2953 pyderasn.InvalidOID: unacceptable first arc value
2955 __slots__ = ("defines",)
2956 tag_default = tag_encode(6)
2957 asn1_type_name = "OBJECT IDENTIFIER"
2970 :param value: set the value. Either tuples of integers,
2971 string of "."-concatenated integers, or
2972 :py:class:`pyderasn.ObjectIdentifier` object
2973 :param defines: sequence of tuples. Each tuple has two elements.
2974 First one is relative to current one decode
2975 path, aiming to the field defined by that OID.
2976 Read about relative path in
2977 :py:func:`pyderasn.abs_decode_path`. Second
2978 tuple element is ``{OID: pyderasn.Obj()}``
2979 dictionary, mapping between current OID value
2980 and structure applied to defined field.
2981 :ref:`Read about DEFINED BY <definedby>`
2982 :param bytes impl: override default tag with ``IMPLICIT`` one
2983 :param bytes expl: override default tag with ``EXPLICIT`` one
2984 :param default: set default value. Type same as in ``value``
2985 :param bool optional: is object ``OPTIONAL`` in sequence
2987 super(ObjectIdentifier, self).__init__(
2995 if value is not None:
2996 self._value = self._value_sanitize(value)
2997 if default is not None:
2998 default = self._value_sanitize(default)
2999 self.default = self.__class__(
3004 if self._value is None:
3005 self._value = default
3006 self.defines = defines
3008 def __add__(self, their):
3009 if isinstance(their, self.__class__):
3010 return self.__class__(self._value + their._value)
3011 if isinstance(their, tuple):
3012 return self.__class__(self._value + their)
3013 raise InvalidValueType((self.__class__, tuple))
3015 def _value_sanitize(self, value):
3016 if issubclass(value.__class__, ObjectIdentifier):
3018 if isinstance(value, string_types):
3020 value = tuple(int(arc) for arc in value.split("."))
3022 raise InvalidOID("unacceptable arcs values")
3023 if isinstance(value, tuple):
3025 raise InvalidOID("less than 2 arcs")
3026 first_arc = value[0]
3027 if first_arc in (0, 1):
3028 if not (0 <= value[1] <= 39):
3029 raise InvalidOID("second arc is too wide")
3030 elif first_arc == 2:
3033 raise InvalidOID("unacceptable first arc value")
3035 raise InvalidValueType((self.__class__, str, tuple))
3039 return self._value is not None
3042 obj = self.__class__()
3043 obj._value = self._value
3044 obj.defines = self.defines
3046 obj._expl = self._expl
3047 obj.default = self.default
3048 obj.optional = self.optional
3049 obj.offset = self.offset
3050 obj.llen = self.llen
3051 obj.vlen = self.vlen
3055 self._assert_ready()
3056 return iter(self._value)
3059 return ".".join(str(arc) for arc in self._value or ())
3062 self._assert_ready()
3065 bytes(self._expl or b"") +
3066 str(self._value).encode("ascii"),
3069 def __eq__(self, their):
3070 if isinstance(their, tuple):
3071 return self._value == their
3072 if not issubclass(their.__class__, ObjectIdentifier):
3075 self.tag == their.tag and
3076 self._expl == their._expl and
3077 self._value == their._value
3080 def __lt__(self, their):
3081 return self._value < their._value
3092 return self.__class__(
3094 defines=self.defines if defines is None else defines,
3095 impl=self.tag if impl is None else impl,
3096 expl=self._expl if expl is None else expl,
3097 default=self.default if default is None else default,
3098 optional=self.optional if optional is None else optional,
3102 self._assert_ready()
3104 first_value = value[1]
3105 first_arc = value[0]
3108 elif first_arc == 1:
3110 elif first_arc == 2:
3112 else: # pragma: no cover
3113 raise RuntimeError("invalid arc is stored")
3114 octets = [zero_ended_encode(first_value)]
3115 for arc in value[2:]:
3116 octets.append(zero_ended_encode(arc))
3117 v = b"".join(octets)
3118 return b"".join((self.tag, len_encode(len(v)), v))
3120 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3122 t, _, lv = tag_strip(tlv)
3123 except DecodeError as err:
3124 raise err.__class__(
3126 klass=self.__class__,
3127 decode_path=decode_path,
3132 klass=self.__class__,
3133 decode_path=decode_path,
3139 l, llen, v = len_decode(lv)
3140 except DecodeError as err:
3141 raise err.__class__(
3143 klass=self.__class__,
3144 decode_path=decode_path,
3148 raise NotEnoughData(
3149 "encoded length is longer than data",
3150 klass=self.__class__,
3151 decode_path=decode_path,
3155 raise NotEnoughData(
3157 klass=self.__class__,
3158 decode_path=decode_path,
3161 v, tail = v[:l], v[l:]
3167 octet = indexbytes(v, i)
3168 arc = (arc << 7) | (octet & 0x7F)
3169 if octet & 0x80 == 0:
3177 klass=self.__class__,
3178 decode_path=decode_path,
3182 second_arc = arcs[0]
3183 if 0 <= second_arc <= 39:
3185 elif 40 <= second_arc <= 79:
3191 obj = self.__class__(
3192 value=tuple([first_arc, second_arc] + arcs[1:]),
3195 default=self.default,
3196 optional=self.optional,
3197 _decoded=(offset, llen, l),
3202 return pp_console_row(next(self.pps()))
3204 def pps(self, decode_path=()):
3206 asn1_type_name=self.asn1_type_name,
3207 obj_name=self.__class__.__name__,
3208 decode_path=decode_path,
3209 value=str(self) if self.ready else None,
3210 optional=self.optional,
3211 default=self == self.default,
3212 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3213 expl=None if self._expl is None else tag_decode(self._expl),
3218 expl_offset=self.expl_offset if self.expled else None,
3219 expl_tlen=self.expl_tlen if self.expled else None,
3220 expl_llen=self.expl_llen if self.expled else None,
3221 expl_vlen=self.expl_vlen if self.expled else None,
3222 expl_lenindef=self.expl_lenindef,
3224 for pp in self.pps_lenindef(decode_path):
3228 class Enumerated(Integer):
3229 """``ENUMERATED`` integer type
3231 This type is identical to :py:class:`pyderasn.Integer`, but requires
3232 schema to be specified and does not accept values missing from it.
3235 tag_default = tag_encode(10)
3236 asn1_type_name = "ENUMERATED"
3247 bounds=None, # dummy argument, workability for Integer.decode
3249 super(Enumerated, self).__init__(
3258 if len(self.specs) == 0:
3259 raise ValueError("schema must be specified")
3261 def _value_sanitize(self, value):
3262 if isinstance(value, self.__class__):
3263 value = value._value
3264 elif isinstance(value, integer_types):
3265 if value not in list(self.specs.values()):
3267 "unknown integer value: %s" % value,
3268 klass=self.__class__,
3270 elif isinstance(value, string_types):
3271 value = self.specs.get(value)
3273 raise ObjUnknown("integer value: %s" % value)
3275 raise InvalidValueType((self.__class__, int, str))
3279 obj = self.__class__(_specs=self.specs)
3280 obj._value = self._value
3281 obj._bound_min = self._bound_min
3282 obj._bound_max = self._bound_max
3284 obj._expl = self._expl
3285 obj.default = self.default
3286 obj.optional = self.optional
3287 obj.offset = self.offset
3288 obj.llen = self.llen
3289 obj.vlen = self.vlen
3301 return self.__class__(
3303 impl=self.tag if impl is None else impl,
3304 expl=self._expl if expl is None else expl,
3305 default=self.default if default is None else default,
3306 optional=self.optional if optional is None else optional,
3311 class CommonString(OctetString):
3312 """Common class for all strings
3314 Everything resembles :py:class:`pyderasn.OctetString`, except
3315 ability to deal with unicode text strings.
3317 >>> hexenc("привет мир".encode("utf-8"))
3318 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3319 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3321 >>> s = UTF8String("привет мир")
3322 UTF8String UTF8String привет мир
3324 'привет мир'
3325 >>> hexenc(bytes(s))
3326 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3328 >>> PrintableString("привет мир")
3329 Traceback (most recent call last):
3330 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3332 >>> BMPString("ада", bounds=(2, 2))
3333 Traceback (most recent call last):
3334 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3335 >>> s = BMPString("ад", bounds=(2, 2))
3338 >>> hexenc(bytes(s))
3346 * - :py:class:`pyderasn.UTF8String`
3348 * - :py:class:`pyderasn.NumericString`
3350 * - :py:class:`pyderasn.PrintableString`
3352 * - :py:class:`pyderasn.TeletexString`
3354 * - :py:class:`pyderasn.T61String`
3356 * - :py:class:`pyderasn.VideotexString`
3358 * - :py:class:`pyderasn.IA5String`
3360 * - :py:class:`pyderasn.GraphicString`
3362 * - :py:class:`pyderasn.VisibleString`
3364 * - :py:class:`pyderasn.ISO646String`
3366 * - :py:class:`pyderasn.GeneralString`
3368 * - :py:class:`pyderasn.UniversalString`
3370 * - :py:class:`pyderasn.BMPString`
3373 __slots__ = ("encoding",)
3375 def _value_sanitize(self, value):
3377 value_decoded = None
3378 if isinstance(value, self.__class__):
3379 value_raw = value._value
3380 elif isinstance(value, text_type):
3381 value_decoded = value
3382 elif isinstance(value, binary_type):
3385 raise InvalidValueType((self.__class__, text_type, binary_type))
3388 value_decoded.encode(self.encoding)
3389 if value_raw is None else value_raw
3392 value_raw.decode(self.encoding)
3393 if value_decoded is None else value_decoded
3395 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3396 raise DecodeError(str(err))
3397 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3405 def __eq__(self, their):
3406 if isinstance(their, binary_type):
3407 return self._value == their
3408 if isinstance(their, text_type):
3409 return self._value == their.encode(self.encoding)
3410 if not isinstance(their, self.__class__):
3413 self._value == their._value and
3414 self.tag == their.tag and
3415 self._expl == their._expl
3418 def __unicode__(self):
3420 return self._value.decode(self.encoding)
3421 return text_type(self._value)
3424 return pp_console_row(next(self.pps(no_unicode=PY2)))
3426 def pps(self, decode_path=(), no_unicode=False):
3429 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3431 asn1_type_name=self.asn1_type_name,
3432 obj_name=self.__class__.__name__,
3433 decode_path=decode_path,
3435 optional=self.optional,
3436 default=self == self.default,
3437 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3438 expl=None if self._expl is None else tag_decode(self._expl),
3443 expl_offset=self.expl_offset if self.expled else None,
3444 expl_tlen=self.expl_tlen if self.expled else None,
3445 expl_llen=self.expl_llen if self.expled else None,
3446 expl_vlen=self.expl_vlen if self.expled else None,
3447 expl_lenindef=self.expl_lenindef,
3449 for pp in self.pps_lenindef(decode_path):
3453 class UTF8String(CommonString):
3455 tag_default = tag_encode(12)
3457 asn1_type_name = "UTF8String"
3460 class NumericString(CommonString):
3463 Its value is properly sanitized: only ASCII digits can be stored.
3466 tag_default = tag_encode(18)
3468 asn1_type_name = "NumericString"
3469 allowable_chars = set(digits.encode("ascii"))
3471 def _value_sanitize(self, value):
3472 value = super(NumericString, self)._value_sanitize(value)
3473 if not set(value) <= self.allowable_chars:
3474 raise DecodeError("non-numeric value")
3478 class PrintableString(CommonString):
3480 tag_default = tag_encode(19)
3482 asn1_type_name = "PrintableString"
3485 class TeletexString(CommonString):
3487 tag_default = tag_encode(20)
3489 asn1_type_name = "TeletexString"
3492 class T61String(TeletexString):
3494 asn1_type_name = "T61String"
3497 class VideotexString(CommonString):
3499 tag_default = tag_encode(21)
3500 encoding = "iso-8859-1"
3501 asn1_type_name = "VideotexString"
3504 class IA5String(CommonString):
3506 tag_default = tag_encode(22)
3508 asn1_type_name = "IA5"
3511 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3512 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3513 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3516 class UTCTime(CommonString):
3517 """``UTCTime`` datetime type
3519 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3520 UTCTime UTCTime 2017-09-30T22:07:50
3526 datetime.datetime(2017, 9, 30, 22, 7, 50)
3527 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3528 datetime.datetime(1957, 9, 30, 22, 7, 50)
3531 tag_default = tag_encode(23)
3533 asn1_type_name = "UTCTime"
3535 fmt = "%y%m%d%H%M%SZ"
3545 bounds=None, # dummy argument, workability for OctetString.decode
3548 :param value: set the value. Either datetime type, or
3549 :py:class:`pyderasn.UTCTime` object
3550 :param bytes impl: override default tag with ``IMPLICIT`` one
3551 :param bytes expl: override default tag with ``EXPLICIT`` one
3552 :param default: set default value. Type same as in ``value``
3553 :param bool optional: is object ``OPTIONAL`` in sequence
3555 super(UTCTime, self).__init__(
3563 if value is not None:
3564 self._value = self._value_sanitize(value)
3565 if default is not None:
3566 default = self._value_sanitize(default)
3567 self.default = self.__class__(
3572 if self._value is None:
3573 self._value = default
3575 def _value_sanitize(self, value):
3576 if isinstance(value, self.__class__):
3578 if isinstance(value, datetime):
3579 return value.strftime(self.fmt).encode("ascii")
3580 if isinstance(value, binary_type):
3582 value_decoded = value.decode("ascii")
3583 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3584 raise DecodeError("invalid UTCTime encoding")
3585 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3587 datetime.strptime(value_decoded, self.fmt)
3588 except (TypeError, ValueError):
3589 raise DecodeError("invalid UTCTime format")
3592 raise DecodeError("invalid UTCTime length")
3593 raise InvalidValueType((self.__class__, datetime))
3595 def __eq__(self, their):
3596 if isinstance(their, binary_type):
3597 return self._value == their
3598 if isinstance(their, datetime):
3599 return self.todatetime() == their
3600 if not isinstance(their, self.__class__):
3603 self._value == their._value and
3604 self.tag == their.tag and
3605 self._expl == their._expl
3608 def todatetime(self):
3609 """Convert to datetime
3613 Pay attention that UTCTime can not hold full year, so all years
3614 having < 50 years are treated as 20xx, 19xx otherwise, according
3615 to X.509 recomendation.
3617 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3618 year = value.year % 100
3620 year=(2000 + year) if year < 50 else (1900 + year),
3624 minute=value.minute,
3625 second=value.second,
3629 return pp_console_row(next(self.pps()))
3631 def pps(self, decode_path=()):
3633 asn1_type_name=self.asn1_type_name,
3634 obj_name=self.__class__.__name__,
3635 decode_path=decode_path,
3636 value=self.todatetime().isoformat() if self.ready else None,
3637 optional=self.optional,
3638 default=self == self.default,
3639 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3640 expl=None if self._expl is None else tag_decode(self._expl),
3645 expl_offset=self.expl_offset if self.expled else None,
3646 expl_tlen=self.expl_tlen if self.expled else None,
3647 expl_llen=self.expl_llen if self.expled else None,
3648 expl_vlen=self.expl_vlen if self.expled else None,
3649 expl_lenindef=self.expl_lenindef,
3651 for pp in self.pps_lenindef(decode_path):
3655 class GeneralizedTime(UTCTime):
3656 """``GeneralizedTime`` datetime type
3658 This type is similar to :py:class:`pyderasn.UTCTime`.
3660 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3661 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3663 '20170930220750.000123Z'
3664 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3665 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3668 tag_default = tag_encode(24)
3669 asn1_type_name = "GeneralizedTime"
3671 fmt = "%Y%m%d%H%M%SZ"
3672 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3674 def _value_sanitize(self, value):
3675 if isinstance(value, self.__class__):
3677 if isinstance(value, datetime):
3678 return value.strftime(
3679 self.fmt_ms if value.microsecond > 0 else self.fmt
3681 if isinstance(value, binary_type):
3683 value_decoded = value.decode("ascii")
3684 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3685 raise DecodeError("invalid GeneralizedTime encoding")
3686 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3688 datetime.strptime(value_decoded, self.fmt)
3689 except (TypeError, ValueError):
3691 "invalid GeneralizedTime (without ms) format",
3694 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3696 datetime.strptime(value_decoded, self.fmt_ms)
3697 except (TypeError, ValueError):
3699 "invalid GeneralizedTime (with ms) format",
3704 "invalid GeneralizedTime length",
3705 klass=self.__class__,
3707 raise InvalidValueType((self.__class__, datetime))
3709 def todatetime(self):
3710 value = self._value.decode("ascii")
3711 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3712 return datetime.strptime(value, self.fmt)
3713 return datetime.strptime(value, self.fmt_ms)
3716 class GraphicString(CommonString):
3718 tag_default = tag_encode(25)
3719 encoding = "iso-8859-1"
3720 asn1_type_name = "GraphicString"
3723 class VisibleString(CommonString):
3725 tag_default = tag_encode(26)
3727 asn1_type_name = "VisibleString"
3730 class ISO646String(VisibleString):
3732 asn1_type_name = "ISO646String"
3735 class GeneralString(CommonString):
3737 tag_default = tag_encode(27)
3738 encoding = "iso-8859-1"
3739 asn1_type_name = "GeneralString"
3742 class UniversalString(CommonString):
3744 tag_default = tag_encode(28)
3745 encoding = "utf-32-be"
3746 asn1_type_name = "UniversalString"
3749 class BMPString(CommonString):
3751 tag_default = tag_encode(30)
3752 encoding = "utf-16-be"
3753 asn1_type_name = "BMPString"
3757 """``CHOICE`` special type
3761 class GeneralName(Choice):
3763 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3764 ("dNSName", IA5String(impl=tag_ctxp(2))),
3767 >>> gn = GeneralName()
3769 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3770 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3771 >>> gn["dNSName"] = IA5String("bar.baz")
3772 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3773 >>> gn["rfc822Name"]
3776 [2] IA5String IA5 bar.baz
3779 >>> gn.value == gn["dNSName"]
3782 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3784 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3785 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3787 __slots__ = ("specs",)
3789 asn1_type_name = "CHOICE"
3802 :param value: set the value. Either ``(choice, value)`` tuple, or
3803 :py:class:`pyderasn.Choice` object
3804 :param bytes impl: can not be set, do **not** use it
3805 :param bytes expl: override default tag with ``EXPLICIT`` one
3806 :param default: set default value. Type same as in ``value``
3807 :param bool optional: is object ``OPTIONAL`` in sequence
3809 if impl is not None:
3810 raise ValueError("no implicit tag allowed for CHOICE")
3811 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3813 schema = getattr(self, "schema", ())
3814 if len(schema) == 0:
3815 raise ValueError("schema must be specified")
3817 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3820 if value is not None:
3821 self._value = self._value_sanitize(value)
3822 if default is not None:
3823 default_value = self._value_sanitize(default)
3824 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3825 default_obj.specs = self.specs
3826 default_obj._value = default_value
3827 self.default = default_obj
3829 self._value = default_obj.copy()._value
3831 def _value_sanitize(self, value):
3832 if isinstance(value, self.__class__):
3834 if isinstance(value, tuple) and len(value) == 2:
3836 spec = self.specs.get(choice)
3838 raise ObjUnknown(choice)
3839 if not isinstance(obj, spec.__class__):
3840 raise InvalidValueType((spec,))
3841 return (choice, spec(obj))
3842 raise InvalidValueType((self.__class__, tuple))
3846 return self._value is not None and self._value[1].ready
3849 obj = self.__class__(schema=self.specs)
3850 obj._expl = self._expl
3851 obj.default = self.default
3852 obj.optional = self.optional
3853 obj.offset = self.offset
3854 obj.llen = self.llen
3855 obj.vlen = self.vlen
3857 if value is not None:
3858 obj._value = (value[0], value[1].copy())
3861 def __eq__(self, their):
3862 if isinstance(their, tuple) and len(their) == 2:
3863 return self._value == their
3864 if not isinstance(their, self.__class__):
3867 self.specs == their.specs and
3868 self._value == their._value
3878 return self.__class__(
3881 expl=self._expl if expl is None else expl,
3882 default=self.default if default is None else default,
3883 optional=self.optional if optional is None else optional,
3888 self._assert_ready()
3889 return self._value[0]
3893 self._assert_ready()
3894 return self._value[1]
3896 def __getitem__(self, key):
3897 if key not in self.specs:
3898 raise ObjUnknown(key)
3899 if self._value is None:
3901 choice, value = self._value
3906 def __setitem__(self, key, value):
3907 spec = self.specs.get(key)
3909 raise ObjUnknown(key)
3910 if not isinstance(value, spec.__class__):
3911 raise InvalidValueType((spec.__class__,))
3912 self._value = (key, spec(value))
3920 return self._value[1].decoded if self.ready else False
3923 self._assert_ready()
3924 return self._value[1].encode()
3926 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3927 for choice, spec in self.specs.items():
3928 sub_decode_path = decode_path + (choice,)
3934 decode_path=sub_decode_path,
3943 klass=self.__class__,
3944 decode_path=decode_path,
3949 value, tail = spec.decode(
3953 decode_path=sub_decode_path,
3956 obj = self.__class__(
3959 default=self.default,
3960 optional=self.optional,
3961 _decoded=(offset, 0, value.fulllen),
3963 obj._value = (choice, value)
3967 value = pp_console_row(next(self.pps()))
3969 value = "%s[%r]" % (value, self.value)
3972 def pps(self, decode_path=()):
3974 asn1_type_name=self.asn1_type_name,
3975 obj_name=self.__class__.__name__,
3976 decode_path=decode_path,
3977 value=self.choice if self.ready else None,
3978 optional=self.optional,
3979 default=self == self.default,
3980 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3981 expl=None if self._expl is None else tag_decode(self._expl),
3986 expl_lenindef=self.expl_lenindef,
3989 yield self.value.pps(decode_path=decode_path + (self.choice,))
3990 for pp in self.pps_lenindef(decode_path):
3994 class PrimitiveTypes(Choice):
3995 """Predefined ``CHOICE`` for all generic primitive types
3997 It could be useful for general decoding of some unspecified values:
3999 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4000 OCTET STRING 3 bytes 666f6f
4001 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4005 schema = tuple((klass.__name__, klass()) for klass in (
4030 """``ANY`` special type
4032 >>> Any(Integer(-123))
4034 >>> a = Any(OctetString(b"hello world").encode())
4035 ANY 040b68656c6c6f20776f726c64
4036 >>> hexenc(bytes(a))
4037 b'0x040x0bhello world'
4039 __slots__ = ("defined",)
4040 tag_default = tag_encode(0)
4041 asn1_type_name = "ANY"
4051 :param value: set the value. Either any kind of pyderasn's
4052 **ready** object, or bytes. Pay attention that
4053 **no** validation is performed is raw binary value
4055 :param bytes expl: override default tag with ``EXPLICIT`` one
4056 :param bool optional: is object ``OPTIONAL`` in sequence
4058 super(Any, self).__init__(None, expl, None, optional, _decoded)
4059 self._value = None if value is None else self._value_sanitize(value)
4062 def _value_sanitize(self, value):
4063 if isinstance(value, self.__class__):
4065 if isinstance(value, Obj):
4066 return value.encode()
4067 if isinstance(value, binary_type):
4069 raise InvalidValueType((self.__class__, Obj, binary_type))
4073 return self._value is not None
4076 obj = self.__class__()
4077 obj._value = self._value
4079 obj._expl = self._expl
4080 obj.optional = self.optional
4081 obj.offset = self.offset
4082 obj.llen = self.llen
4083 obj.vlen = self.vlen
4086 def __eq__(self, their):
4087 if isinstance(their, binary_type):
4088 return self._value == their
4089 if issubclass(their.__class__, Any):
4090 return self._value == their._value
4099 return self.__class__(
4101 expl=self._expl if expl is None else expl,
4102 optional=self.optional if optional is None else optional,
4105 def __bytes__(self):
4106 self._assert_ready()
4114 self._assert_ready()
4117 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4119 t, tlen, lv = tag_strip(tlv)
4120 except DecodeError as err:
4121 raise err.__class__(
4123 klass=self.__class__,
4124 decode_path=decode_path,
4128 l, llen, v = len_decode(lv)
4129 except LenIndefForm as err:
4130 if not ctx.get("bered", False):
4131 raise err.__class__(
4133 klass=self.__class__,
4134 decode_path=decode_path,
4137 llen, vlen, v = 1, 0, lv[1:]
4138 sub_offset = offset + tlen + llen
4140 while v[:EOC_LEN].tobytes() != EOC:
4141 chunk, v = Any().decode(
4144 decode_path=decode_path + (str(chunk_i),),
4148 vlen += chunk.tlvlen
4149 sub_offset += chunk.tlvlen
4151 tlvlen = tlen + llen + vlen + EOC_LEN
4152 obj = self.__class__(
4153 value=tlv[:tlvlen].tobytes(),
4155 optional=self.optional,
4156 _decoded=(offset, 0, tlvlen),
4160 return obj, v[EOC_LEN:]
4161 except DecodeError as err:
4162 raise err.__class__(
4164 klass=self.__class__,
4165 decode_path=decode_path,
4169 raise NotEnoughData(
4170 "encoded length is longer than data",
4171 klass=self.__class__,
4172 decode_path=decode_path,
4175 tlvlen = tlen + llen + l
4176 v, tail = tlv[:tlvlen], v[l:]
4177 obj = self.__class__(
4180 optional=self.optional,
4181 _decoded=(offset, 0, tlvlen),
4187 return pp_console_row(next(self.pps()))
4189 def pps(self, decode_path=()):
4191 asn1_type_name=self.asn1_type_name,
4192 obj_name=self.__class__.__name__,
4193 decode_path=decode_path,
4194 blob=self._value if self.ready else None,
4195 optional=self.optional,
4196 default=self == self.default,
4197 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4198 expl=None if self._expl is None else tag_decode(self._expl),
4203 expl_offset=self.expl_offset if self.expled else None,
4204 expl_tlen=self.expl_tlen if self.expled else None,
4205 expl_llen=self.expl_llen if self.expled else None,
4206 expl_vlen=self.expl_vlen if self.expled else None,
4207 expl_lenindef=self.expl_lenindef,
4208 lenindef=self.lenindef,
4210 defined_by, defined = self.defined or (None, None)
4211 if defined_by is not None:
4213 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4215 for pp in self.pps_lenindef(decode_path):
4219 ########################################################################
4220 # ASN.1 constructed types
4221 ########################################################################
4223 def get_def_by_path(defines_by_path, sub_decode_path):
4224 """Get define by decode path
4226 for path, define in defines_by_path:
4227 if len(path) != len(sub_decode_path):
4229 for p1, p2 in zip(path, sub_decode_path):
4230 if (p1 != any) and (p1 != p2):
4236 def abs_decode_path(decode_path, rel_path):
4237 """Create an absolute decode path from current and relative ones
4239 :param decode_path: current decode path, starting point.
4241 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4242 If first tuple's element is "/", then treat it as
4243 an absolute path, ignoring ``decode_path`` as
4244 starting point. Also this tuple can contain ".."
4245 elements, stripping the leading element from
4248 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4249 ("foo", "bar", "baz", "whatever")
4250 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4252 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4255 if rel_path[0] == "/":
4257 if rel_path[0] == "..":
4258 return abs_decode_path(decode_path[:-1], rel_path[1:])
4259 return decode_path + rel_path
4262 class Sequence(Obj):
4263 """``SEQUENCE`` structure type
4265 You have to make specification of sequence::
4267 class Extension(Sequence):
4269 ("extnID", ObjectIdentifier()),
4270 ("critical", Boolean(default=False)),
4271 ("extnValue", OctetString()),
4274 Then, you can work with it as with dictionary.
4276 >>> ext = Extension()
4277 >>> Extension().specs
4279 ('extnID', OBJECT IDENTIFIER),
4280 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4281 ('extnValue', OCTET STRING),
4283 >>> ext["extnID"] = "1.2.3"
4284 Traceback (most recent call last):
4285 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4286 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4288 You can determine if sequence is ready to be encoded:
4293 Traceback (most recent call last):
4294 pyderasn.ObjNotReady: object is not ready: extnValue
4295 >>> ext["extnValue"] = OctetString(b"foobar")
4299 Value you want to assign, must have the same **type** as in
4300 corresponding specification, but it can have different tags,
4301 optional/default attributes -- they will be taken from specification
4304 class TBSCertificate(Sequence):
4306 ("version", Version(expl=tag_ctxc(0), default="v1")),
4309 >>> tbs = TBSCertificate()
4310 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4312 Assign ``None`` to remove value from sequence.
4314 You can set values in Sequence during its initialization:
4316 >>> AlgorithmIdentifier((
4317 ("algorithm", ObjectIdentifier("1.2.3")),
4318 ("parameters", Any(Null()))
4320 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4322 You can determine if value exists/set in the sequence and take its value:
4324 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4327 OBJECT IDENTIFIER 1.2.3
4329 But pay attention that if value has default, then it won't be (not
4330 in) in the sequence (because ``DEFAULT`` must not be encoded in
4331 DER), but you can read its value:
4333 >>> "critical" in ext, ext["critical"]
4334 (False, BOOLEAN False)
4335 >>> ext["critical"] = Boolean(True)
4336 >>> "critical" in ext, ext["critical"]
4337 (True, BOOLEAN True)
4339 All defaulted values are always optional.
4341 .. _allow_default_values_ctx:
4343 DER prohibits default value encoding and will raise an error if
4344 default value is unexpectedly met during decode.
4345 If :ref:`bered <bered_ctx>` context option is set, then no error
4346 will be raised, but ``bered`` attribute set. You can disable strict
4347 defaulted values existence validation by setting
4348 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4350 Two sequences are equal if they have equal specification (schema),
4351 implicit/explicit tagging and the same values.
4353 __slots__ = ("specs",)
4354 tag_default = tag_encode(form=TagFormConstructed, num=16)
4355 asn1_type_name = "SEQUENCE"
4367 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4369 schema = getattr(self, "schema", ())
4371 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4374 if value is not None:
4375 if issubclass(value.__class__, Sequence):
4376 self._value = value._value
4377 elif hasattr(value, "__iter__"):
4378 for seq_key, seq_value in value:
4379 self[seq_key] = seq_value
4381 raise InvalidValueType((Sequence,))
4382 if default is not None:
4383 if not issubclass(default.__class__, Sequence):
4384 raise InvalidValueType((Sequence,))
4385 default_value = default._value
4386 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4387 default_obj.specs = self.specs
4388 default_obj._value = default_value
4389 self.default = default_obj
4391 self._value = default_obj.copy()._value
4395 for name, spec in self.specs.items():
4396 value = self._value.get(name)
4407 obj = self.__class__(schema=self.specs)
4409 obj._expl = self._expl
4410 obj.default = self.default
4411 obj.optional = self.optional
4412 obj.offset = self.offset
4413 obj.llen = self.llen
4414 obj.vlen = self.vlen
4415 obj._value = {k: v.copy() for k, v in self._value.items()}
4418 def __eq__(self, their):
4419 if not isinstance(their, self.__class__):
4422 self.specs == their.specs and
4423 self.tag == their.tag and
4424 self._expl == their._expl and
4425 self._value == their._value
4436 return self.__class__(
4439 impl=self.tag if impl is None else impl,
4440 expl=self._expl if expl is None else expl,
4441 default=self.default if default is None else default,
4442 optional=self.optional if optional is None else optional,
4445 def __contains__(self, key):
4446 return key in self._value
4448 def __setitem__(self, key, value):
4449 spec = self.specs.get(key)
4451 raise ObjUnknown(key)
4453 self._value.pop(key, None)
4455 if not isinstance(value, spec.__class__):
4456 raise InvalidValueType((spec.__class__,))
4457 value = spec(value=value)
4458 if spec.default is not None and value == spec.default:
4459 self._value.pop(key, None)
4461 self._value[key] = value
4463 def __getitem__(self, key):
4464 value = self._value.get(key)
4465 if value is not None:
4467 spec = self.specs.get(key)
4469 raise ObjUnknown(key)
4470 if spec.default is not None:
4474 def _encoded_values(self):
4476 for name, spec in self.specs.items():
4477 value = self._value.get(name)
4481 raise ObjNotReady(name)
4482 raws.append(value.encode())
4486 v = b"".join(self._encoded_values())
4487 return b"".join((self.tag, len_encode(len(v)), v))
4489 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4491 t, tlen, lv = tag_strip(tlv)
4492 except DecodeError as err:
4493 raise err.__class__(
4495 klass=self.__class__,
4496 decode_path=decode_path,
4501 klass=self.__class__,
4502 decode_path=decode_path,
4508 ctx_bered = ctx.get("bered", False)
4510 l, llen, v = len_decode(lv)
4511 except LenIndefForm as err:
4513 raise err.__class__(
4515 klass=self.__class__,
4516 decode_path=decode_path,
4519 l, llen, v = 0, 1, lv[1:]
4521 except DecodeError as err:
4522 raise err.__class__(
4524 klass=self.__class__,
4525 decode_path=decode_path,
4529 raise NotEnoughData(
4530 "encoded length is longer than data",
4531 klass=self.__class__,
4532 decode_path=decode_path,
4536 v, tail = v[:l], v[l:]
4538 sub_offset = offset + tlen + llen
4541 ctx_allow_default_values = ctx.get("allow_default_values", False)
4542 for name, spec in self.specs.items():
4543 if spec.optional and (
4544 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4548 sub_decode_path = decode_path + (name,)
4550 value, v_tail = spec.decode(
4554 decode_path=sub_decode_path,
4562 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4563 if defined is not None:
4564 defined_by, defined_spec = defined
4565 if issubclass(value.__class__, SequenceOf):
4566 for i, _value in enumerate(value):
4567 sub_sub_decode_path = sub_decode_path + (
4569 DecodePathDefBy(defined_by),
4571 defined_value, defined_tail = defined_spec.decode(
4572 memoryview(bytes(_value)),
4574 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4575 if value.expled else (value.tlen + value.llen)
4578 decode_path=sub_sub_decode_path,
4581 if len(defined_tail) > 0:
4584 klass=self.__class__,
4585 decode_path=sub_sub_decode_path,
4588 _value.defined = (defined_by, defined_value)
4590 defined_value, defined_tail = defined_spec.decode(
4591 memoryview(bytes(value)),
4593 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4594 if value.expled else (value.tlen + value.llen)
4597 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4600 if len(defined_tail) > 0:
4603 klass=self.__class__,
4604 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4607 value.defined = (defined_by, defined_value)
4609 value_len = value.fulllen
4611 sub_offset += value_len
4613 if spec.default is not None and value == spec.default:
4614 if ctx_bered or ctx_allow_default_values:
4618 "DEFAULT value met",
4619 klass=self.__class__,
4620 decode_path=sub_decode_path,
4623 values[name] = value
4625 spec_defines = getattr(spec, "defines", ())
4626 if len(spec_defines) == 0:
4627 defines_by_path = ctx.get("defines_by_path", ())
4628 if len(defines_by_path) > 0:
4629 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4630 if spec_defines is not None and len(spec_defines) > 0:
4631 for rel_path, schema in spec_defines:
4632 defined = schema.get(value, None)
4633 if defined is not None:
4634 ctx.setdefault("_defines", []).append((
4635 abs_decode_path(sub_decode_path[:-1], rel_path),
4639 if v[:EOC_LEN].tobytes() != EOC:
4642 klass=self.__class__,
4643 decode_path=decode_path,
4651 klass=self.__class__,
4652 decode_path=decode_path,
4655 obj = self.__class__(
4659 default=self.default,
4660 optional=self.optional,
4661 _decoded=(offset, llen, vlen),
4664 obj.lenindef = lenindef
4669 value = pp_console_row(next(self.pps()))
4671 for name in self.specs:
4672 _value = self._value.get(name)
4675 cols.append("%s: %s" % (name, repr(_value)))
4676 return "%s[%s]" % (value, "; ".join(cols))
4678 def pps(self, decode_path=()):
4680 asn1_type_name=self.asn1_type_name,
4681 obj_name=self.__class__.__name__,
4682 decode_path=decode_path,
4683 optional=self.optional,
4684 default=self == self.default,
4685 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4686 expl=None if self._expl is None else tag_decode(self._expl),
4691 expl_offset=self.expl_offset if self.expled else None,
4692 expl_tlen=self.expl_tlen if self.expled else None,
4693 expl_llen=self.expl_llen if self.expled else None,
4694 expl_vlen=self.expl_vlen if self.expled else None,
4695 expl_lenindef=self.expl_lenindef,
4696 lenindef=self.lenindef,
4698 for name in self.specs:
4699 value = self._value.get(name)
4702 yield value.pps(decode_path=decode_path + (name,))
4703 for pp in self.pps_lenindef(decode_path):
4707 class Set(Sequence):
4708 """``SET`` structure type
4710 Its usage is identical to :py:class:`pyderasn.Sequence`.
4713 tag_default = tag_encode(form=TagFormConstructed, num=17)
4714 asn1_type_name = "SET"
4717 raws = self._encoded_values()
4720 return b"".join((self.tag, len_encode(len(v)), v))
4722 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4724 t, tlen, lv = tag_strip(tlv)
4725 except DecodeError as err:
4726 raise err.__class__(
4728 klass=self.__class__,
4729 decode_path=decode_path,
4734 klass=self.__class__,
4735 decode_path=decode_path,
4741 ctx_bered = ctx.get("bered", False)
4743 l, llen, v = len_decode(lv)
4744 except LenIndefForm as err:
4746 raise err.__class__(
4748 klass=self.__class__,
4749 decode_path=decode_path,
4752 l, llen, v = 0, 1, lv[1:]
4754 except DecodeError as err:
4755 raise err.__class__(
4757 klass=self.__class__,
4758 decode_path=decode_path,
4762 raise NotEnoughData(
4763 "encoded length is longer than data",
4764 klass=self.__class__,
4768 v, tail = v[:l], v[l:]
4770 sub_offset = offset + tlen + llen
4773 ctx_allow_default_values = ctx.get("allow_default_values", False)
4774 specs_items = self.specs.items
4776 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4778 for name, spec in specs_items():
4779 sub_decode_path = decode_path + (name,)
4785 decode_path=sub_decode_path,
4794 klass=self.__class__,
4795 decode_path=decode_path,
4798 value, v_tail = spec.decode(
4802 decode_path=sub_decode_path,
4805 value_len = value.fulllen
4806 sub_offset += value_len
4809 if spec.default is None or value != spec.default:
4811 elif ctx_bered or ctx_allow_default_values:
4815 "DEFAULT value met",
4816 klass=self.__class__,
4817 decode_path=sub_decode_path,
4820 values[name] = value
4821 obj = self.__class__(
4825 default=self.default,
4826 optional=self.optional,
4827 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4832 if v[:EOC_LEN].tobytes() != EOC:
4835 klass=self.__class__,
4836 decode_path=decode_path,
4843 "not all values are ready",
4844 klass=self.__class__,
4845 decode_path=decode_path,
4851 class SequenceOf(Obj):
4852 """``SEQUENCE OF`` sequence type
4854 For that kind of type you must specify the object it will carry on
4855 (bounds are for example here, not required)::
4857 class Ints(SequenceOf):
4862 >>> ints.append(Integer(123))
4863 >>> ints.append(Integer(234))
4865 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4866 >>> [int(i) for i in ints]
4868 >>> ints.append(Integer(345))
4869 Traceback (most recent call last):
4870 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4873 >>> ints[1] = Integer(345)
4875 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4877 Also you can initialize sequence with preinitialized values:
4879 >>> ints = Ints([Integer(123), Integer(234)])
4881 __slots__ = ("spec", "_bound_min", "_bound_max")
4882 tag_default = tag_encode(form=TagFormConstructed, num=16)
4883 asn1_type_name = "SEQUENCE OF"
4896 super(SequenceOf, self).__init__(
4904 schema = getattr(self, "schema", None)
4906 raise ValueError("schema must be specified")
4908 self._bound_min, self._bound_max = getattr(
4912 ) if bounds is None else bounds
4914 if value is not None:
4915 self._value = self._value_sanitize(value)
4916 if default is not None:
4917 default_value = self._value_sanitize(default)
4918 default_obj = self.__class__(
4923 default_obj._value = default_value
4924 self.default = default_obj
4926 self._value = default_obj.copy()._value
4928 def _value_sanitize(self, value):
4929 if issubclass(value.__class__, SequenceOf):
4930 value = value._value
4931 elif hasattr(value, "__iter__"):
4934 raise InvalidValueType((self.__class__, iter))
4935 if not self._bound_min <= len(value) <= self._bound_max:
4936 raise BoundsError(self._bound_min, len(value), self._bound_max)
4938 if not isinstance(v, self.spec.__class__):
4939 raise InvalidValueType((self.spec.__class__,))
4944 return all(v.ready for v in self._value)
4947 obj = self.__class__(schema=self.spec)
4948 obj._bound_min = self._bound_min
4949 obj._bound_max = self._bound_max
4951 obj._expl = self._expl
4952 obj.default = self.default
4953 obj.optional = self.optional
4954 obj.offset = self.offset
4955 obj.llen = self.llen
4956 obj.vlen = self.vlen
4957 obj._value = [v.copy() for v in self._value]
4960 def __eq__(self, their):
4961 if isinstance(their, self.__class__):
4963 self.spec == their.spec and
4964 self.tag == their.tag and
4965 self._expl == their._expl and
4966 self._value == their._value
4968 if hasattr(their, "__iter__"):
4969 return self._value == list(their)
4981 return self.__class__(
4985 (self._bound_min, self._bound_max)
4986 if bounds is None else bounds
4988 impl=self.tag if impl is None else impl,
4989 expl=self._expl if expl is None else expl,
4990 default=self.default if default is None else default,
4991 optional=self.optional if optional is None else optional,
4994 def __contains__(self, key):
4995 return key in self._value
4997 def append(self, value):
4998 if not isinstance(value, self.spec.__class__):
4999 raise InvalidValueType((self.spec.__class__,))
5000 if len(self._value) + 1 > self._bound_max:
5003 len(self._value) + 1,
5006 self._value.append(value)
5009 self._assert_ready()
5010 return iter(self._value)
5013 self._assert_ready()
5014 return len(self._value)
5016 def __setitem__(self, key, value):
5017 if not isinstance(value, self.spec.__class__):
5018 raise InvalidValueType((self.spec.__class__,))
5019 self._value[key] = self.spec(value=value)
5021 def __getitem__(self, key):
5022 return self._value[key]
5024 def _encoded_values(self):
5025 return [v.encode() for v in self._value]
5028 v = b"".join(self._encoded_values())
5029 return b"".join((self.tag, len_encode(len(v)), v))
5031 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5033 t, tlen, lv = tag_strip(tlv)
5034 except DecodeError as err:
5035 raise err.__class__(
5037 klass=self.__class__,
5038 decode_path=decode_path,
5043 klass=self.__class__,
5044 decode_path=decode_path,
5051 l, llen, v = len_decode(lv)
5052 except LenIndefForm as err:
5053 if not ctx.get("bered", False):
5054 raise err.__class__(
5056 klass=self.__class__,
5057 decode_path=decode_path,
5060 l, llen, v = 0, 1, lv[1:]
5062 except DecodeError as err:
5063 raise err.__class__(
5065 klass=self.__class__,
5066 decode_path=decode_path,
5070 raise NotEnoughData(
5071 "encoded length is longer than data",
5072 klass=self.__class__,
5073 decode_path=decode_path,
5077 v, tail = v[:l], v[l:]
5079 sub_offset = offset + tlen + llen
5083 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5085 value, v_tail = spec.decode(
5089 decode_path=decode_path + (str(len(_value)),),
5092 value_len = value.fulllen
5093 sub_offset += value_len
5096 _value.append(value)
5098 obj = self.__class__(
5101 bounds=(self._bound_min, self._bound_max),
5104 default=self.default,
5105 optional=self.optional,
5106 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5108 except BoundsError as err:
5111 klass=self.__class__,
5112 decode_path=decode_path,
5116 if v[:EOC_LEN].tobytes() != EOC:
5119 klass=self.__class__,
5120 decode_path=decode_path,
5129 pp_console_row(next(self.pps())),
5130 ", ".join(repr(v) for v in self._value),
5133 def pps(self, decode_path=()):
5135 asn1_type_name=self.asn1_type_name,
5136 obj_name=self.__class__.__name__,
5137 decode_path=decode_path,
5138 optional=self.optional,
5139 default=self == self.default,
5140 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5141 expl=None if self._expl is None else tag_decode(self._expl),
5146 expl_offset=self.expl_offset if self.expled else None,
5147 expl_tlen=self.expl_tlen if self.expled else None,
5148 expl_llen=self.expl_llen if self.expled else None,
5149 expl_vlen=self.expl_vlen if self.expled else None,
5150 expl_lenindef=self.expl_lenindef,
5151 lenindef=self.lenindef,
5153 for i, value in enumerate(self._value):
5154 yield value.pps(decode_path=decode_path + (str(i),))
5155 for pp in self.pps_lenindef(decode_path):
5159 class SetOf(SequenceOf):
5160 """``SET OF`` sequence type
5162 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5165 tag_default = tag_encode(form=TagFormConstructed, num=17)
5166 asn1_type_name = "SET OF"
5169 raws = self._encoded_values()
5172 return b"".join((self.tag, len_encode(len(v)), v))
5175 def obj_by_path(pypath): # pragma: no cover
5176 """Import object specified as string Python path
5178 Modules must be separated from classes/functions with ``:``.
5180 >>> obj_by_path("foo.bar:Baz")
5181 <class 'foo.bar.Baz'>
5182 >>> obj_by_path("foo.bar:Baz.boo")
5183 <classmethod 'foo.bar.Baz.boo'>
5185 mod, objs = pypath.rsplit(":", 1)
5186 from importlib import import_module
5187 obj = import_module(mod)
5188 for obj_name in objs.split("."):
5189 obj = getattr(obj, obj_name)
5193 def generic_decoder(): # pragma: no cover
5194 # All of this below is a big hack with self references
5195 choice = PrimitiveTypes()
5196 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5197 choice.specs["SetOf"] = SetOf(schema=choice)
5199 choice.specs["SequenceOf%d" % i] = SequenceOf(
5203 choice.specs["Any"] = Any()
5205 # Class name equals to type name, to omit it from output
5206 class SEQUENCEOF(SequenceOf):
5214 with_decode_path=False,
5215 decode_path_only=(),
5217 def _pprint_pps(pps):
5219 if hasattr(pp, "_fields"):
5221 decode_path_only != () and
5222 pp.decode_path[:len(decode_path_only)] != decode_path_only
5225 if pp.asn1_type_name == Choice.asn1_type_name:
5227 pp_kwargs = pp._asdict()
5228 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5229 pp = _pp(**pp_kwargs)
5230 yield pp_console_row(
5235 with_colours=with_colours,
5236 with_decode_path=with_decode_path,
5237 decode_path_len_decrease=len(decode_path_only),
5239 for row in pp_console_blob(
5241 decode_path_len_decrease=len(decode_path_only),
5245 for row in _pprint_pps(pp):
5247 return "\n".join(_pprint_pps(obj.pps()))
5248 return SEQUENCEOF(), pprint_any
5251 def main(): # pragma: no cover
5253 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5254 parser.add_argument(
5258 help="Skip that number of bytes from the beginning",
5260 parser.add_argument(
5262 help="Python path to dictionary with OIDs",
5264 parser.add_argument(
5266 help="Python path to schema definition to use",
5268 parser.add_argument(
5269 "--defines-by-path",
5270 help="Python path to decoder's defines_by_path",
5272 parser.add_argument(
5274 action="store_true",
5275 help="Disallow BER encoding",
5277 parser.add_argument(
5278 "--print-decode-path",
5279 action="store_true",
5280 help="Print decode paths",
5282 parser.add_argument(
5283 "--decode-path-only",
5284 help="Print only specified decode path",
5286 parser.add_argument(
5288 action="store_true",
5289 help="Allow explicit tag out-of-bound",
5291 parser.add_argument(
5293 type=argparse.FileType("rb"),
5294 help="Path to DER file you want to decode",
5296 args = parser.parse_args()
5297 args.DERFile.seek(args.skip)
5298 der = memoryview(args.DERFile.read())
5299 args.DERFile.close()
5300 oids = obj_by_path(args.oids) if args.oids else {}
5302 schema = obj_by_path(args.schema)
5303 from functools import partial
5304 pprinter = partial(pprint, big_blobs=True)
5306 schema, pprinter = generic_decoder()
5308 "bered": not args.nobered,
5309 "allow_expl_oob": args.allow_expl_oob,
5311 if args.defines_by_path is not None:
5312 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5313 obj, tail = schema().decode(der, ctx=ctx)
5317 with_colours=True if environ.get("NO_COLOR") is None else False,
5318 with_decode_path=args.print_decode_path,
5320 () if args.decode_path_only is None else
5321 tuple(args.decode_path_only.split(":"))
5325 print("\nTrailing data: %s" % hexenc(tail))
5328 if __name__ == "__main__":