3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2019 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:`allow_unordered_set <allow_unordered_set_ctx>`
219 * :ref:`bered <bered_ctx>`
220 * :ref:`defines_by_path <defines_by_path_ctx>`
227 All objects have ``pps()`` method, that is a generator of
228 :py:class:`pyderasn.PP` namedtuple, holding various raw information
229 about the object. If ``pps`` is called on sequences, then all underlying
230 ``PP`` will be yielded.
232 You can use :py:func:`pyderasn.pp_console_row` function, converting
233 those ``PP`` to human readable string. Actually exactly it is used for
234 all object ``repr``. But it is easy to write custom formatters.
236 >>> from pyderasn import pprint
237 >>> encoded = Integer(-12345).encode()
238 >>> obj, tail = Integer().decode(encoded)
239 >>> print(pprint(obj))
240 0 [1,1, 2] INTEGER -12345
247 ASN.1 structures often have ANY and OCTET STRING fields, that are
248 DEFINED BY some previously met ObjectIdentifier. This library provides
249 ability to specify mapping between some OID and field that must be
250 decoded with specific specification.
255 :py:class:`pyderasn.ObjectIdentifier` field inside
256 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
257 necessary for decoding structures. For example, CMS (:rfc:`5652`)
260 class ContentInfo(Sequence):
262 ("contentType", ContentType(defines=((("content",), {
263 id_digestedData: DigestedData(),
264 id_signedData: SignedData(),
266 ("content", Any(expl=tag_ctxc(0))),
269 ``contentType`` field tells that it defines that ``content`` must be
270 decoded with ``SignedData`` specification, if ``contentType`` equals to
271 ``id-signedData``. The same applies to ``DigestedData``. If
272 ``contentType`` contains unknown OID, then no automatic decoding is
275 You can specify multiple fields, that will be autodecoded -- that is why
276 ``defines`` kwarg is a sequence. You can specify defined field
277 relatively or absolutely to current decode path. For example ``defines``
278 for AlgorithmIdentifier of X.509's
279 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
283 id_ecPublicKey: ECParameters(),
284 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
286 (("..", "subjectPublicKey"), {
287 id_rsaEncryption: RSAPublicKey(),
288 id_GostR3410_2001: OctetString(),
292 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
293 autodecode its parameters inside SPKI's algorithm and its public key
296 Following types can be automatically decoded (DEFINED BY):
298 * :py:class:`pyderasn.Any`
299 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
300 * :py:class:`pyderasn.OctetString`
301 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
302 ``Any``/``BitString``/``OctetString``-s
304 When any of those fields is automatically decoded, then ``.defined``
305 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
306 was defined, ``value`` contains corresponding decoded value. For example
307 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
309 .. _defines_by_path_ctx:
311 defines_by_path context option
312 ______________________________
314 Sometimes you either can not or do not want to explicitly set *defines*
315 in the scheme. You can dynamically apply those definitions when calling
316 ``.decode()`` method.
318 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
319 value must be sequence of following tuples::
321 (decode_path, defines)
323 where ``decode_path`` is a tuple holding so-called decode path to the
324 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
325 ``defines``, holding exactly the same value as accepted in its keyword
328 For example, again for CMS, you want to automatically decode
329 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
330 structures it may hold. Also, automatically decode ``controlSequence``
333 content_info, tail = ContentInfo().decode(data, defines_by_path=(
336 ((("content",), {id_signedData: SignedData()}),),
341 DecodePathDefBy(id_signedData),
346 id_cct_PKIData: PKIData(),
347 id_cct_PKIResponse: PKIResponse(),
353 DecodePathDefBy(id_signedData),
356 DecodePathDefBy(id_cct_PKIResponse),
362 id_cmc_recipientNonce: RecipientNonce(),
363 id_cmc_senderNonce: SenderNonce(),
364 id_cmc_statusInfoV2: CMCStatusInfoV2(),
365 id_cmc_transactionId: TransactionId(),
370 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
371 First function is useful for path construction when some automatic
372 decoding is already done. ``any`` means literally any value it meet --
373 useful for SEQUENCE/SET OF-s.
380 By default PyDERASN accepts only DER encoded data. It always encodes to
381 DER. But you can optionally enable BER decoding with setting ``bered``
382 :ref:`context <ctx>` argument to True. Indefinite lengths and
383 constructed primitive types should be parsed successfully.
385 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
386 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
387 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``
389 * If object has an indefinite length encoding, then its ``lenindef``
390 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
391 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
393 * If object has an indefinite length encoded explicit tag, then
394 ``expl_lenindef`` is set to True.
395 * If object has either any of BER-related encoding (explicit tag
396 indefinite length, object's indefinite length, BER-encoding) or any
397 underlying component has that kind of encoding, then ``bered``
398 attribute is set to True. For example SignedData CMS can have
399 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
400 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
402 EOC (end-of-contents) token's length is taken in advance in object's
405 .. _allow_expl_oob_ctx:
407 Allow explicit tag out-of-bound
408 -------------------------------
410 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
411 one value, more than one object. If you set ``allow_expl_oob`` context
412 option to True, then no error will be raised and that invalid encoding
413 will be silently further processed. But pay attention that offsets and
414 lengths will be invalid in that case.
418 This option should be used only for skipping some decode errors, just
419 to see the decoded structure somehow.
426 .. autoclass:: pyderasn.Boolean
431 .. autoclass:: pyderasn.Integer
436 .. autoclass:: pyderasn.BitString
441 .. autoclass:: pyderasn.OctetString
446 .. autoclass:: pyderasn.Null
451 .. autoclass:: pyderasn.ObjectIdentifier
456 .. autoclass:: pyderasn.Enumerated
460 .. autoclass:: pyderasn.CommonString
464 .. autoclass:: pyderasn.NumericString
468 .. autoclass:: pyderasn.UTCTime
469 :members: __init__, todatetime
473 .. autoclass:: pyderasn.GeneralizedTime
480 .. autoclass:: pyderasn.Choice
485 .. autoclass:: PrimitiveTypes
489 .. autoclass:: pyderasn.Any
497 .. autoclass:: pyderasn.Sequence
502 .. autoclass:: pyderasn.Set
507 .. autoclass:: pyderasn.SequenceOf
512 .. autoclass:: pyderasn.SetOf
518 .. autofunction:: pyderasn.abs_decode_path
519 .. autofunction:: pyderasn.colonize_hex
520 .. autofunction:: pyderasn.hexenc
521 .. autofunction:: pyderasn.hexdec
522 .. autofunction:: pyderasn.tag_encode
523 .. autofunction:: pyderasn.tag_decode
524 .. autofunction:: pyderasn.tag_ctxp
525 .. autofunction:: pyderasn.tag_ctxc
526 .. autoclass:: pyderasn.Obj
527 .. autoclass:: pyderasn.DecodeError
529 .. autoclass:: pyderasn.NotEnoughData
530 .. autoclass:: pyderasn.LenIndefForm
531 .. autoclass:: pyderasn.TagMismatch
532 .. autoclass:: pyderasn.InvalidLength
533 .. autoclass:: pyderasn.InvalidOID
534 .. autoclass:: pyderasn.ObjUnknown
535 .. autoclass:: pyderasn.ObjNotReady
536 .. autoclass:: pyderasn.InvalidValueType
537 .. autoclass:: pyderasn.BoundsError
540 from codecs import getdecoder
541 from codecs import getencoder
542 from collections import namedtuple
543 from collections import OrderedDict
544 from copy import copy
545 from datetime import datetime
546 from math import ceil
547 from os import environ
548 from string import ascii_letters
549 from string import digits
551 from six import add_metaclass
552 from six import binary_type
553 from six import byte2int
554 from six import indexbytes
555 from six import int2byte
556 from six import integer_types
557 from six import iterbytes
558 from six import iteritems
559 from six import itervalues
561 from six import string_types
562 from six import text_type
563 from six import unichr as six_unichr
564 from six.moves import xrange as six_xrange
568 from termcolor import colored
569 except ImportError: # pragma: no cover
570 def colored(what, *args):
614 "TagClassApplication",
618 "TagFormConstructed",
629 TagClassUniversal = 0
630 TagClassApplication = 1 << 6
631 TagClassContext = 1 << 7
632 TagClassPrivate = 1 << 6 | 1 << 7
634 TagFormConstructed = 1 << 5
637 TagClassApplication: "APPLICATION ",
638 TagClassPrivate: "PRIVATE ",
639 TagClassUniversal: "UNIV ",
643 LENINDEF = b"\x80" # length indefinite mark
644 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
647 ########################################################################
649 ########################################################################
651 class ASN1Error(ValueError):
655 class DecodeError(ASN1Error):
656 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
658 :param str msg: reason of decode failing
659 :param klass: optional exact DecodeError inherited class (like
660 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
661 :py:exc:`InvalidLength`)
662 :param decode_path: tuple of strings. It contains human
663 readable names of the fields through which
664 decoding process has passed
665 :param int offset: binary offset where failure happened
667 super(DecodeError, self).__init__()
670 self.decode_path = decode_path
676 "" if self.klass is None else self.klass.__name__,
678 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
679 if len(self.decode_path) > 0 else ""
681 ("(at %d)" % self.offset) if self.offset > 0 else "",
687 return "%s(%s)" % (self.__class__.__name__, self)
690 class NotEnoughData(DecodeError):
694 class LenIndefForm(DecodeError):
698 class TagMismatch(DecodeError):
702 class InvalidLength(DecodeError):
706 class InvalidOID(DecodeError):
710 class ObjUnknown(ASN1Error):
711 def __init__(self, name):
712 super(ObjUnknown, self).__init__()
716 return "object is unknown: %s" % self.name
719 return "%s(%s)" % (self.__class__.__name__, self)
722 class ObjNotReady(ASN1Error):
723 def __init__(self, name):
724 super(ObjNotReady, self).__init__()
728 return "object is not ready: %s" % self.name
731 return "%s(%s)" % (self.__class__.__name__, self)
734 class InvalidValueType(ASN1Error):
735 def __init__(self, expected_types):
736 super(InvalidValueType, self).__init__()
737 self.expected_types = expected_types
740 return "invalid value type, expected: %s" % ", ".join(
741 [repr(t) for t in self.expected_types]
745 return "%s(%s)" % (self.__class__.__name__, self)
748 class BoundsError(ASN1Error):
749 def __init__(self, bound_min, value, bound_max):
750 super(BoundsError, self).__init__()
751 self.bound_min = bound_min
753 self.bound_max = bound_max
756 return "unsatisfied bounds: %s <= %s <= %s" % (
763 return "%s(%s)" % (self.__class__.__name__, self)
766 ########################################################################
768 ########################################################################
770 _hexdecoder = getdecoder("hex")
771 _hexencoder = getencoder("hex")
775 """Binary data to hexadecimal string convert
777 return _hexdecoder(data)[0]
781 """Hexadecimal string to binary data convert
783 return _hexencoder(data)[0].decode("ascii")
786 def int_bytes_len(num, byte_len=8):
789 return int(ceil(float(num.bit_length()) / byte_len))
792 def zero_ended_encode(num):
793 octets = bytearray(int_bytes_len(num, 7))
795 octets[i] = num & 0x7F
799 octets[i] = 0x80 | (num & 0x7F)
805 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
806 """Encode tag to binary form
808 :param int num: tag's number
809 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
810 :py:data:`pyderasn.TagClassContext`,
811 :py:data:`pyderasn.TagClassApplication`,
812 :py:data:`pyderasn.TagClassPrivate`)
813 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
814 :py:data:`pyderasn.TagFormConstructed`)
818 return int2byte(klass | form | num)
819 # [XX|X|11111][1.......][1.......] ... [0.......]
820 return int2byte(klass | form | 31) + zero_ended_encode(num)
824 """Decode tag from binary form
828 No validation is performed, assuming that it has already passed.
830 It returns tuple with three integers, as
831 :py:func:`pyderasn.tag_encode` accepts.
833 first_octet = byte2int(tag)
834 klass = first_octet & 0xC0
835 form = first_octet & 0x20
836 if first_octet & 0x1F < 0x1F:
837 return (klass, form, first_octet & 0x1F)
839 for octet in iterbytes(tag[1:]):
842 return (klass, form, num)
846 """Create CONTEXT PRIMITIVE tag
848 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
852 """Create CONTEXT CONSTRUCTED tag
854 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
858 """Take off tag from the data
860 :returns: (encoded tag, tag length, remaining data)
863 raise NotEnoughData("no data at all")
864 if byte2int(data) & 0x1F < 31:
865 return data[:1], 1, data[1:]
870 raise DecodeError("unfinished tag")
871 if indexbytes(data, i) & 0x80 == 0:
874 return data[:i], i, data[i:]
880 octets = bytearray(int_bytes_len(l) + 1)
881 octets[0] = 0x80 | (len(octets) - 1)
882 for i in six_xrange(len(octets) - 1, 0, -1):
888 def len_decode(data):
891 :returns: (decoded length, length's length, remaining data)
892 :raises LenIndefForm: if indefinite form encoding is met
895 raise NotEnoughData("no data at all")
896 first_octet = byte2int(data)
897 if first_octet & 0x80 == 0:
898 return first_octet, 1, data[1:]
899 octets_num = first_octet & 0x7F
900 if octets_num + 1 > len(data):
901 raise NotEnoughData("encoded length is longer than data")
904 if byte2int(data[1:]) == 0:
905 raise DecodeError("leading zeros")
907 for v in iterbytes(data[1:1 + octets_num]):
910 raise DecodeError("long form instead of short one")
911 return l, 1 + octets_num, data[1 + octets_num:]
914 ########################################################################
916 ########################################################################
918 class AutoAddSlots(type):
919 def __new__(mcs, name, bases, _dict):
920 _dict["__slots__"] = _dict.get("__slots__", ())
921 return type.__new__(mcs, name, bases, _dict)
924 @add_metaclass(AutoAddSlots)
926 """Common ASN.1 object class
928 All ASN.1 types are inherited from it. It has metaclass that
929 automatically adds ``__slots__`` to all inherited classes.
953 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
954 self._expl = getattr(self, "expl", None) if expl is None else expl
955 if self.tag != self.tag_default and self._expl is not None:
956 raise ValueError("implicit and explicit tags can not be set simultaneously")
957 if default is not None:
959 self.optional = optional
960 self.offset, self.llen, self.vlen = _decoded
962 self.expl_lenindef = False
963 self.lenindef = False
964 self.ber_encoded = False
967 def ready(self): # pragma: no cover
968 """Is object ready to be encoded?
970 raise NotImplementedError()
972 def _assert_ready(self):
974 raise ObjNotReady(self.__class__.__name__)
978 """Is either object or any elements inside is BER encoded?
980 return self.expl_lenindef or self.lenindef or self.ber_encoded
984 """Is object decoded?
986 return (self.llen + self.vlen) > 0
988 def copy(self): # pragma: no cover
989 """Make a copy of object, safe to be mutated
991 raise NotImplementedError()
999 return self.tlen + self.llen + self.vlen
1001 def __str__(self): # pragma: no cover
1002 return self.__bytes__() if PY2 else self.__unicode__()
1004 def __ne__(self, their):
1005 return not(self == their)
1007 def __gt__(self, their): # pragma: no cover
1008 return not(self < their)
1010 def __le__(self, their): # pragma: no cover
1011 return (self == their) or (self < their)
1013 def __ge__(self, their): # pragma: no cover
1014 return (self == their) or (self > their)
1016 def _encode(self): # pragma: no cover
1017 raise NotImplementedError()
1019 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1020 raise NotImplementedError()
1023 raw = self._encode()
1024 if self._expl is None:
1026 return b"".join((self._expl, len_encode(len(raw)), raw))
1036 _ctx_immutable=True,
1040 :param data: either binary or memoryview
1041 :param int offset: initial data's offset
1042 :param bool leavemm: do we need to leave memoryview of remaining
1043 data as is, or convert it to bytes otherwise
1044 :param ctx: optional :ref:`context <ctx>` governing decoding process
1045 :param tag_only: decode only the tag, without length and contents
1046 (used only in Choice and Set structures, trying to
1047 determine if tag satisfies the scheme)
1048 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1049 :returns: (Obj, remaining data)
1053 elif _ctx_immutable:
1055 tlv = memoryview(data)
1056 if self._expl is None:
1057 result = self._decode(
1060 decode_path=decode_path,
1069 t, tlen, lv = tag_strip(tlv)
1070 except DecodeError as err:
1071 raise err.__class__(
1073 klass=self.__class__,
1074 decode_path=decode_path,
1079 klass=self.__class__,
1080 decode_path=decode_path,
1084 l, llen, v = len_decode(lv)
1085 except LenIndefForm as err:
1086 if not ctx.get("bered", False):
1087 raise err.__class__(
1089 klass=self.__class__,
1090 decode_path=decode_path,
1094 offset += tlen + llen
1095 result = self._decode(
1098 decode_path=decode_path,
1102 if tag_only: # pragma: no cover
1105 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1106 if eoc_expected.tobytes() != EOC:
1109 klass=self.__class__,
1110 decode_path=decode_path,
1114 obj.expl_lenindef = True
1115 except DecodeError as err:
1116 raise err.__class__(
1118 klass=self.__class__,
1119 decode_path=decode_path,
1124 raise NotEnoughData(
1125 "encoded length is longer than data",
1126 klass=self.__class__,
1127 decode_path=decode_path,
1130 result = self._decode(
1132 offset=offset + tlen + llen,
1133 decode_path=decode_path,
1137 if tag_only: # pragma: no cover
1140 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1142 "explicit tag out-of-bound, longer than data",
1143 klass=self.__class__,
1144 decode_path=decode_path,
1147 return obj, (tail if leavemm else tail.tobytes())
1151 return self._expl is not None
1158 def expl_tlen(self):
1159 return len(self._expl)
1162 def expl_llen(self):
1163 if self.expl_lenindef:
1165 return len(len_encode(self.tlvlen))
1168 def expl_offset(self):
1169 return self.offset - self.expl_tlen - self.expl_llen
1172 def expl_vlen(self):
1176 def expl_tlvlen(self):
1177 return self.expl_tlen + self.expl_llen + self.expl_vlen
1180 def fulloffset(self):
1181 return self.expl_offset if self.expled else self.offset
1185 return self.expl_tlvlen if self.expled else self.tlvlen
1187 def pps_lenindef(self, decode_path):
1188 if self.lenindef and not (
1189 getattr(self, "defined", None) is not None and
1190 self.defined[1].lenindef
1193 asn1_type_name="EOC",
1195 decode_path=decode_path,
1197 self.offset + self.tlvlen -
1198 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1206 if self.expl_lenindef:
1208 asn1_type_name="EOC",
1209 obj_name="EXPLICIT",
1210 decode_path=decode_path,
1211 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1220 class DecodePathDefBy(object):
1221 """DEFINED BY representation inside decode path
1223 __slots__ = ("defined_by",)
1225 def __init__(self, defined_by):
1226 self.defined_by = defined_by
1228 def __ne__(self, their):
1229 return not(self == their)
1231 def __eq__(self, their):
1232 if not isinstance(their, self.__class__):
1234 return self.defined_by == their.defined_by
1237 return "DEFINED BY " + str(self.defined_by)
1240 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1243 ########################################################################
1245 ########################################################################
1247 PP = namedtuple("PP", (
1275 asn1_type_name="unknown",
1292 expl_lenindef=False,
1323 def _colourize(what, colour, with_colours, attrs=("bold",)):
1324 return colored(what, colour, attrs=attrs) if with_colours else what
1327 def colonize_hex(hexed):
1328 """Separate hexadecimal string with colons
1330 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1339 with_decode_path=False,
1340 decode_path_len_decrease=0,
1347 " " if pp.expl_offset is None else
1348 ("-%d" % (pp.offset - pp.expl_offset))
1350 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1352 col = _colourize(col, "red", with_colours, ())
1353 col += _colourize("B", "red", with_colours) if pp.bered else " "
1355 col = "[%d,%d,%4d]%s" % (
1359 LENINDEF_PP_CHAR if pp.lenindef else " "
1361 col = _colourize(col, "green", with_colours, ())
1363 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1364 if decode_path_len > 0:
1365 cols.append(" ." * decode_path_len)
1366 ent = pp.decode_path[-1]
1367 if isinstance(ent, DecodePathDefBy):
1368 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1369 value = str(ent.defined_by)
1371 oids is not None and
1372 ent.defined_by.asn1_type_name ==
1373 ObjectIdentifier.asn1_type_name and
1376 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1378 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1380 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1381 if pp.expl is not None:
1382 klass, _, num = pp.expl
1383 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1384 cols.append(_colourize(col, "blue", with_colours))
1385 if pp.impl is not None:
1386 klass, _, num = pp.impl
1387 col = "[%s%d]" % (TagClassReprs[klass], num)
1388 cols.append(_colourize(col, "blue", with_colours))
1389 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1390 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1392 cols.append(_colourize("BER", "red", with_colours))
1393 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1394 if pp.value is not None:
1396 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1398 oids is not None and
1399 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1402 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1403 if pp.asn1_type_name == Integer.asn1_type_name:
1404 hex_repr = hex(int(pp.obj._value))[2:].upper()
1405 if len(hex_repr) % 2 != 0:
1406 hex_repr = "0" + hex_repr
1407 cols.append(_colourize(
1408 "(%s)" % colonize_hex(hex_repr),
1413 if isinstance(pp.blob, binary_type):
1414 cols.append(hexenc(pp.blob))
1415 elif isinstance(pp.blob, tuple):
1416 cols.append(", ".join(pp.blob))
1418 cols.append(_colourize("OPTIONAL", "red", with_colours))
1420 cols.append(_colourize("DEFAULT", "red", with_colours))
1421 if with_decode_path:
1422 cols.append(_colourize(
1423 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1427 return " ".join(cols)
1430 def pp_console_blob(pp, decode_path_len_decrease=0):
1431 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1432 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1433 if decode_path_len > 0:
1434 cols.append(" ." * (decode_path_len + 1))
1435 if isinstance(pp.blob, binary_type):
1436 blob = hexenc(pp.blob).upper()
1437 for i in six_xrange(0, len(blob), 32):
1438 chunk = blob[i:i + 32]
1439 yield " ".join(cols + [colonize_hex(chunk)])
1440 elif isinstance(pp.blob, tuple):
1441 yield " ".join(cols + [", ".join(pp.blob)])
1449 with_decode_path=False,
1450 decode_path_only=(),
1452 """Pretty print object
1454 :param Obj obj: object you want to pretty print
1455 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1456 from it is met, then its humand readable form is printed
1457 :param big_blobs: if large binary objects are met (like OctetString
1458 values), do we need to print them too, on separate
1460 :param with_colours: colourize output, if ``termcolor`` library
1462 :param with_decode_path: print decode path
1463 :param decode_path_only: print only that specified decode path
1465 def _pprint_pps(pps):
1467 if hasattr(pp, "_fields"):
1469 decode_path_only != () and
1471 str(p) for p in pp.decode_path[:len(decode_path_only)]
1472 ) != decode_path_only
1476 yield pp_console_row(
1481 with_colours=with_colours,
1482 with_decode_path=with_decode_path,
1483 decode_path_len_decrease=len(decode_path_only),
1485 for row in pp_console_blob(
1487 decode_path_len_decrease=len(decode_path_only),
1491 yield pp_console_row(
1496 with_colours=with_colours,
1497 with_decode_path=with_decode_path,
1498 decode_path_len_decrease=len(decode_path_only),
1501 for row in _pprint_pps(pp):
1503 return "\n".join(_pprint_pps(obj.pps()))
1506 ########################################################################
1507 # ASN.1 primitive types
1508 ########################################################################
1511 """``BOOLEAN`` boolean type
1513 >>> b = Boolean(True)
1515 >>> b == Boolean(True)
1521 tag_default = tag_encode(1)
1522 asn1_type_name = "BOOLEAN"
1534 :param value: set the value. Either boolean type, or
1535 :py:class:`pyderasn.Boolean` object
1536 :param bytes impl: override default tag with ``IMPLICIT`` one
1537 :param bytes expl: override default tag with ``EXPLICIT`` one
1538 :param default: set default value. Type same as in ``value``
1539 :param bool optional: is object ``OPTIONAL`` in sequence
1541 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1542 self._value = None if value is None else self._value_sanitize(value)
1543 if default is not None:
1544 default = self._value_sanitize(default)
1545 self.default = self.__class__(
1551 self._value = default
1553 def _value_sanitize(self, value):
1554 if issubclass(value.__class__, Boolean):
1556 if isinstance(value, bool):
1558 raise InvalidValueType((self.__class__, bool))
1562 return self._value is not None
1565 obj = self.__class__()
1566 obj._value = self._value
1568 obj._expl = self._expl
1569 obj.default = self.default
1570 obj.optional = self.optional
1571 obj.offset = self.offset
1572 obj.llen = self.llen
1573 obj.vlen = self.vlen
1574 obj.expl_lenindef = self.expl_lenindef
1575 obj.lenindef = self.lenindef
1576 obj.ber_encoded = self.ber_encoded
1579 def __nonzero__(self):
1580 self._assert_ready()
1584 self._assert_ready()
1587 def __eq__(self, their):
1588 if isinstance(their, bool):
1589 return self._value == their
1590 if not issubclass(their.__class__, Boolean):
1593 self._value == their._value and
1594 self.tag == their.tag and
1595 self._expl == their._expl
1606 return self.__class__(
1608 impl=self.tag if impl is None else impl,
1609 expl=self._expl if expl is None else expl,
1610 default=self.default if default is None else default,
1611 optional=self.optional if optional is None else optional,
1615 self._assert_ready()
1619 (b"\xFF" if self._value else b"\x00"),
1622 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1624 t, _, lv = tag_strip(tlv)
1625 except DecodeError as err:
1626 raise err.__class__(
1628 klass=self.__class__,
1629 decode_path=decode_path,
1634 klass=self.__class__,
1635 decode_path=decode_path,
1641 l, _, v = len_decode(lv)
1642 except DecodeError as err:
1643 raise err.__class__(
1645 klass=self.__class__,
1646 decode_path=decode_path,
1650 raise InvalidLength(
1651 "Boolean's length must be equal to 1",
1652 klass=self.__class__,
1653 decode_path=decode_path,
1657 raise NotEnoughData(
1658 "encoded length is longer than data",
1659 klass=self.__class__,
1660 decode_path=decode_path,
1663 first_octet = byte2int(v)
1665 if first_octet == 0:
1667 elif first_octet == 0xFF:
1669 elif ctx.get("bered", False):
1674 "unacceptable Boolean value",
1675 klass=self.__class__,
1676 decode_path=decode_path,
1679 obj = self.__class__(
1683 default=self.default,
1684 optional=self.optional,
1685 _decoded=(offset, 1, 1),
1687 obj.ber_encoded = ber_encoded
1691 return pp_console_row(next(self.pps()))
1693 def pps(self, decode_path=()):
1696 asn1_type_name=self.asn1_type_name,
1697 obj_name=self.__class__.__name__,
1698 decode_path=decode_path,
1699 value=str(self._value) if self.ready else None,
1700 optional=self.optional,
1701 default=self == self.default,
1702 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1703 expl=None if self._expl is None else tag_decode(self._expl),
1708 expl_offset=self.expl_offset if self.expled else None,
1709 expl_tlen=self.expl_tlen if self.expled else None,
1710 expl_llen=self.expl_llen if self.expled else None,
1711 expl_vlen=self.expl_vlen if self.expled else None,
1712 expl_lenindef=self.expl_lenindef,
1713 ber_encoded=self.ber_encoded,
1716 for pp in self.pps_lenindef(decode_path):
1721 """``INTEGER`` integer type
1723 >>> b = Integer(-123)
1725 >>> b == Integer(-123)
1730 >>> Integer(2, bounds=(1, 3))
1732 >>> Integer(5, bounds=(1, 3))
1733 Traceback (most recent call last):
1734 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1738 class Version(Integer):
1745 >>> v = Version("v1")
1752 {'v3': 2, 'v1': 0, 'v2': 1}
1754 __slots__ = ("specs", "_bound_min", "_bound_max")
1755 tag_default = tag_encode(2)
1756 asn1_type_name = "INTEGER"
1770 :param value: set the value. Either integer type, named value
1771 (if ``schema`` is specified in the class), or
1772 :py:class:`pyderasn.Integer` object
1773 :param bounds: set ``(MIN, MAX)`` value constraint.
1774 (-inf, +inf) by default
1775 :param bytes impl: override default tag with ``IMPLICIT`` one
1776 :param bytes expl: override default tag with ``EXPLICIT`` one
1777 :param default: set default value. Type same as in ``value``
1778 :param bool optional: is object ``OPTIONAL`` in sequence
1780 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1782 specs = getattr(self, "schema", {}) if _specs is None else _specs
1783 self.specs = specs if isinstance(specs, dict) else dict(specs)
1784 self._bound_min, self._bound_max = getattr(
1787 (float("-inf"), float("+inf")),
1788 ) if bounds is None else bounds
1789 if value is not None:
1790 self._value = self._value_sanitize(value)
1791 if default is not None:
1792 default = self._value_sanitize(default)
1793 self.default = self.__class__(
1799 if self._value is None:
1800 self._value = default
1802 def _value_sanitize(self, value):
1803 if issubclass(value.__class__, Integer):
1804 value = value._value
1805 elif isinstance(value, integer_types):
1807 elif isinstance(value, str):
1808 value = self.specs.get(value)
1810 raise ObjUnknown("integer value: %s" % value)
1812 raise InvalidValueType((self.__class__, int, str))
1813 if not self._bound_min <= value <= self._bound_max:
1814 raise BoundsError(self._bound_min, value, self._bound_max)
1819 return self._value is not None
1822 obj = self.__class__(_specs=self.specs)
1823 obj._value = self._value
1824 obj._bound_min = self._bound_min
1825 obj._bound_max = self._bound_max
1827 obj._expl = self._expl
1828 obj.default = self.default
1829 obj.optional = self.optional
1830 obj.offset = self.offset
1831 obj.llen = self.llen
1832 obj.vlen = self.vlen
1833 obj.expl_lenindef = self.expl_lenindef
1834 obj.lenindef = self.lenindef
1835 obj.ber_encoded = self.ber_encoded
1839 self._assert_ready()
1840 return int(self._value)
1843 self._assert_ready()
1846 bytes(self._expl or b"") +
1847 str(self._value).encode("ascii"),
1850 def __eq__(self, their):
1851 if isinstance(their, integer_types):
1852 return self._value == their
1853 if not issubclass(their.__class__, Integer):
1856 self._value == their._value and
1857 self.tag == their.tag and
1858 self._expl == their._expl
1861 def __lt__(self, their):
1862 return self._value < their._value
1866 for name, value in iteritems(self.specs):
1867 if value == self._value:
1879 return self.__class__(
1882 (self._bound_min, self._bound_max)
1883 if bounds is None else bounds
1885 impl=self.tag if impl is None else impl,
1886 expl=self._expl if expl is None else expl,
1887 default=self.default if default is None else default,
1888 optional=self.optional if optional is None else optional,
1893 self._assert_ready()
1897 octets = bytearray([0])
1901 octets = bytearray()
1903 octets.append((value & 0xFF) ^ 0xFF)
1905 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1908 octets = bytearray()
1910 octets.append(value & 0xFF)
1912 if octets[-1] & 0x80 > 0:
1915 octets = bytes(octets)
1917 bytes_len = ceil(value.bit_length() / 8) or 1
1920 octets = value.to_bytes(
1925 except OverflowError:
1929 return b"".join((self.tag, len_encode(len(octets)), octets))
1931 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1933 t, _, lv = tag_strip(tlv)
1934 except DecodeError as err:
1935 raise err.__class__(
1937 klass=self.__class__,
1938 decode_path=decode_path,
1943 klass=self.__class__,
1944 decode_path=decode_path,
1950 l, llen, v = len_decode(lv)
1951 except DecodeError as err:
1952 raise err.__class__(
1954 klass=self.__class__,
1955 decode_path=decode_path,
1959 raise NotEnoughData(
1960 "encoded length is longer than data",
1961 klass=self.__class__,
1962 decode_path=decode_path,
1966 raise NotEnoughData(
1968 klass=self.__class__,
1969 decode_path=decode_path,
1972 v, tail = v[:l], v[l:]
1973 first_octet = byte2int(v)
1975 second_octet = byte2int(v[1:])
1977 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1978 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1981 "non normalized integer",
1982 klass=self.__class__,
1983 decode_path=decode_path,
1988 if first_octet & 0x80 > 0:
1989 octets = bytearray()
1990 for octet in bytearray(v):
1991 octets.append(octet ^ 0xFF)
1992 for octet in octets:
1993 value = (value << 8) | octet
1997 for octet in bytearray(v):
1998 value = (value << 8) | octet
2000 value = int.from_bytes(v, byteorder="big", signed=True)
2002 obj = self.__class__(
2004 bounds=(self._bound_min, self._bound_max),
2007 default=self.default,
2008 optional=self.optional,
2010 _decoded=(offset, llen, l),
2012 except BoundsError as err:
2015 klass=self.__class__,
2016 decode_path=decode_path,
2022 return pp_console_row(next(self.pps()))
2024 def pps(self, decode_path=()):
2027 asn1_type_name=self.asn1_type_name,
2028 obj_name=self.__class__.__name__,
2029 decode_path=decode_path,
2030 value=(self.named or str(self._value)) if self.ready else None,
2031 optional=self.optional,
2032 default=self == self.default,
2033 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2034 expl=None if self._expl is None else tag_decode(self._expl),
2039 expl_offset=self.expl_offset if self.expled else None,
2040 expl_tlen=self.expl_tlen if self.expled else None,
2041 expl_llen=self.expl_llen if self.expled else None,
2042 expl_vlen=self.expl_vlen if self.expled else None,
2043 expl_lenindef=self.expl_lenindef,
2046 for pp in self.pps_lenindef(decode_path):
2050 class BitString(Obj):
2051 """``BIT STRING`` bit string type
2053 >>> BitString(b"hello world")
2054 BIT STRING 88 bits 68656c6c6f20776f726c64
2057 >>> b == b"hello world"
2062 >>> BitString("'0A3B5F291CD'H")
2063 BIT STRING 44 bits 0a3b5f291cd0
2064 >>> b = BitString("'010110000000'B")
2065 BIT STRING 12 bits 5800
2068 >>> b[0], b[1], b[2], b[3]
2069 (False, True, False, True)
2073 [False, True, False, True, True, False, False, False, False, False, False, False]
2077 class KeyUsage(BitString):
2079 ("digitalSignature", 0),
2080 ("nonRepudiation", 1),
2081 ("keyEncipherment", 2),
2084 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2085 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2087 ['nonRepudiation', 'keyEncipherment']
2089 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2093 Pay attention that BIT STRING can be encoded both in primitive
2094 and constructed forms. Decoder always checks constructed form tag
2095 additionally to specified primitive one. If BER decoding is
2096 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2097 of DER restrictions.
2099 __slots__ = ("tag_constructed", "specs", "defined")
2100 tag_default = tag_encode(3)
2101 asn1_type_name = "BIT STRING"
2114 :param value: set the value. Either binary type, tuple of named
2115 values (if ``schema`` is specified in the class),
2116 string in ``'XXX...'B`` form, or
2117 :py:class:`pyderasn.BitString` object
2118 :param bytes impl: override default tag with ``IMPLICIT`` one
2119 :param bytes expl: override default tag with ``EXPLICIT`` one
2120 :param default: set default value. Type same as in ``value``
2121 :param bool optional: is object ``OPTIONAL`` in sequence
2123 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2124 specs = getattr(self, "schema", {}) if _specs is None else _specs
2125 self.specs = specs if isinstance(specs, dict) else dict(specs)
2126 self._value = None if value is None else self._value_sanitize(value)
2127 if default is not None:
2128 default = self._value_sanitize(default)
2129 self.default = self.__class__(
2135 self._value = default
2137 tag_klass, _, tag_num = tag_decode(self.tag)
2138 self.tag_constructed = tag_encode(
2140 form=TagFormConstructed,
2144 def _bits2octets(self, bits):
2145 if len(self.specs) > 0:
2146 bits = bits.rstrip("0")
2148 bits += "0" * ((8 - (bit_len % 8)) % 8)
2149 octets = bytearray(len(bits) // 8)
2150 for i in six_xrange(len(octets)):
2151 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2152 return bit_len, bytes(octets)
2154 def _value_sanitize(self, value):
2155 if issubclass(value.__class__, BitString):
2157 if isinstance(value, (string_types, binary_type)):
2159 isinstance(value, string_types) and
2160 value.startswith("'")
2162 if value.endswith("'B"):
2164 if not set(value) <= set(("0", "1")):
2165 raise ValueError("B's coding contains unacceptable chars")
2166 return self._bits2octets(value)
2167 elif value.endswith("'H"):
2171 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2173 if isinstance(value, binary_type):
2174 return (len(value) * 8, value)
2176 raise InvalidValueType((self.__class__, string_types, binary_type))
2177 if isinstance(value, tuple):
2180 isinstance(value[0], integer_types) and
2181 isinstance(value[1], binary_type)
2186 bit = self.specs.get(name)
2188 raise ObjUnknown("BitString value: %s" % name)
2191 return self._bits2octets("")
2193 return self._bits2octets("".join(
2194 ("1" if bit in bits else "0")
2195 for bit in six_xrange(max(bits) + 1)
2197 raise InvalidValueType((self.__class__, binary_type, string_types))
2201 return self._value is not None
2204 obj = self.__class__(_specs=self.specs)
2206 if value is not None:
2207 value = (value[0], value[1])
2210 obj._expl = self._expl
2211 obj.default = self.default
2212 obj.optional = self.optional
2213 obj.offset = self.offset
2214 obj.llen = self.llen
2215 obj.vlen = self.vlen
2216 obj.expl_lenindef = self.expl_lenindef
2217 obj.lenindef = self.lenindef
2218 obj.ber_encoded = self.ber_encoded
2222 self._assert_ready()
2223 for i in six_xrange(self._value[0]):
2228 self._assert_ready()
2229 return self._value[0]
2231 def __bytes__(self):
2232 self._assert_ready()
2233 return self._value[1]
2235 def __eq__(self, their):
2236 if isinstance(their, bytes):
2237 return self._value[1] == their
2238 if not issubclass(their.__class__, BitString):
2241 self._value == their._value and
2242 self.tag == their.tag and
2243 self._expl == their._expl
2248 return [name for name, bit in iteritems(self.specs) if self[bit]]
2258 return self.__class__(
2260 impl=self.tag if impl is None else impl,
2261 expl=self._expl if expl is None else expl,
2262 default=self.default if default is None else default,
2263 optional=self.optional if optional is None else optional,
2267 def __getitem__(self, key):
2268 if isinstance(key, int):
2269 bit_len, octets = self._value
2273 byte2int(memoryview(octets)[key // 8:]) >>
2276 if isinstance(key, string_types):
2277 value = self.specs.get(key)
2279 raise ObjUnknown("BitString value: %s" % key)
2281 raise InvalidValueType((int, str))
2284 self._assert_ready()
2285 bit_len, octets = self._value
2288 len_encode(len(octets) + 1),
2289 int2byte((8 - bit_len % 8) % 8),
2293 def _decode_chunk(self, lv, offset, decode_path, ctx):
2295 l, llen, v = len_decode(lv)
2296 except DecodeError as err:
2297 raise err.__class__(
2299 klass=self.__class__,
2300 decode_path=decode_path,
2304 raise NotEnoughData(
2305 "encoded length is longer than data",
2306 klass=self.__class__,
2307 decode_path=decode_path,
2311 raise NotEnoughData(
2313 klass=self.__class__,
2314 decode_path=decode_path,
2317 pad_size = byte2int(v)
2318 if l == 1 and pad_size != 0:
2320 "invalid empty value",
2321 klass=self.__class__,
2322 decode_path=decode_path,
2328 klass=self.__class__,
2329 decode_path=decode_path,
2332 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2335 klass=self.__class__,
2336 decode_path=decode_path,
2339 v, tail = v[:l], v[l:]
2340 obj = self.__class__(
2341 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2344 default=self.default,
2345 optional=self.optional,
2347 _decoded=(offset, llen, l),
2351 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2353 t, tlen, lv = tag_strip(tlv)
2354 except DecodeError as err:
2355 raise err.__class__(
2357 klass=self.__class__,
2358 decode_path=decode_path,
2362 if tag_only: # pragma: no cover
2364 return self._decode_chunk(lv, offset, decode_path, ctx)
2365 if t == self.tag_constructed:
2366 if not ctx.get("bered", False):
2368 "unallowed BER constructed encoding",
2369 klass=self.__class__,
2370 decode_path=decode_path,
2373 if tag_only: # pragma: no cover
2377 l, llen, v = len_decode(lv)
2378 except LenIndefForm:
2379 llen, l, v = 1, 0, lv[1:]
2381 except DecodeError as err:
2382 raise err.__class__(
2384 klass=self.__class__,
2385 decode_path=decode_path,
2389 raise NotEnoughData(
2390 "encoded length is longer than data",
2391 klass=self.__class__,
2392 decode_path=decode_path,
2395 if not lenindef and l == 0:
2396 raise NotEnoughData(
2398 klass=self.__class__,
2399 decode_path=decode_path,
2403 sub_offset = offset + tlen + llen
2407 if v[:EOC_LEN].tobytes() == EOC:
2414 "chunk out of bounds",
2415 klass=self.__class__,
2416 decode_path=decode_path + (str(len(chunks) - 1),),
2417 offset=chunks[-1].offset,
2419 sub_decode_path = decode_path + (str(len(chunks)),)
2421 chunk, v_tail = BitString().decode(
2424 decode_path=sub_decode_path,
2427 _ctx_immutable=False,
2431 "expected BitString encoded chunk",
2432 klass=self.__class__,
2433 decode_path=sub_decode_path,
2436 chunks.append(chunk)
2437 sub_offset += chunk.tlvlen
2438 vlen += chunk.tlvlen
2440 if len(chunks) == 0:
2443 klass=self.__class__,
2444 decode_path=decode_path,
2449 for chunk_i, chunk in enumerate(chunks[:-1]):
2450 if chunk.bit_len % 8 != 0:
2452 "BitString chunk is not multiple of 8 bits",
2453 klass=self.__class__,
2454 decode_path=decode_path + (str(chunk_i),),
2455 offset=chunk.offset,
2457 values.append(bytes(chunk))
2458 bit_len += chunk.bit_len
2459 chunk_last = chunks[-1]
2460 values.append(bytes(chunk_last))
2461 bit_len += chunk_last.bit_len
2462 obj = self.__class__(
2463 value=(bit_len, b"".join(values)),
2466 default=self.default,
2467 optional=self.optional,
2469 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2471 obj.lenindef = lenindef
2472 obj.ber_encoded = True
2473 return obj, (v[EOC_LEN:] if lenindef else v)
2475 klass=self.__class__,
2476 decode_path=decode_path,
2481 return pp_console_row(next(self.pps()))
2483 def pps(self, decode_path=()):
2487 bit_len, blob = self._value
2488 value = "%d bits" % bit_len
2489 if len(self.specs) > 0:
2490 blob = tuple(self.named)
2493 asn1_type_name=self.asn1_type_name,
2494 obj_name=self.__class__.__name__,
2495 decode_path=decode_path,
2498 optional=self.optional,
2499 default=self == self.default,
2500 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2501 expl=None if self._expl is None else tag_decode(self._expl),
2506 expl_offset=self.expl_offset if self.expled else None,
2507 expl_tlen=self.expl_tlen if self.expled else None,
2508 expl_llen=self.expl_llen if self.expled else None,
2509 expl_vlen=self.expl_vlen if self.expled else None,
2510 expl_lenindef=self.expl_lenindef,
2511 lenindef=self.lenindef,
2512 ber_encoded=self.ber_encoded,
2515 defined_by, defined = self.defined or (None, None)
2516 if defined_by is not None:
2518 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2520 for pp in self.pps_lenindef(decode_path):
2524 class OctetString(Obj):
2525 """``OCTET STRING`` binary string type
2527 >>> s = OctetString(b"hello world")
2528 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2529 >>> s == OctetString(b"hello world")
2534 >>> OctetString(b"hello", bounds=(4, 4))
2535 Traceback (most recent call last):
2536 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2537 >>> OctetString(b"hell", bounds=(4, 4))
2538 OCTET STRING 4 bytes 68656c6c
2542 Pay attention that OCTET STRING can be encoded both in primitive
2543 and constructed forms. Decoder always checks constructed form tag
2544 additionally to specified primitive one. If BER decoding is
2545 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2546 of DER restrictions.
2548 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2549 tag_default = tag_encode(4)
2550 asn1_type_name = "OCTET STRING"
2563 :param value: set the value. Either binary type, or
2564 :py:class:`pyderasn.OctetString` object
2565 :param bounds: set ``(MIN, MAX)`` value size constraint.
2566 (-inf, +inf) by default
2567 :param bytes impl: override default tag with ``IMPLICIT`` one
2568 :param bytes expl: override default tag with ``EXPLICIT`` one
2569 :param default: set default value. Type same as in ``value``
2570 :param bool optional: is object ``OPTIONAL`` in sequence
2572 super(OctetString, self).__init__(
2580 self._bound_min, self._bound_max = getattr(
2584 ) if bounds is None else bounds
2585 if value is not None:
2586 self._value = self._value_sanitize(value)
2587 if default is not None:
2588 default = self._value_sanitize(default)
2589 self.default = self.__class__(
2594 if self._value is None:
2595 self._value = default
2597 tag_klass, _, tag_num = tag_decode(self.tag)
2598 self.tag_constructed = tag_encode(
2600 form=TagFormConstructed,
2604 def _value_sanitize(self, value):
2605 if issubclass(value.__class__, OctetString):
2606 value = value._value
2607 elif isinstance(value, binary_type):
2610 raise InvalidValueType((self.__class__, bytes))
2611 if not self._bound_min <= len(value) <= self._bound_max:
2612 raise BoundsError(self._bound_min, len(value), self._bound_max)
2617 return self._value is not None
2620 obj = self.__class__()
2621 obj._value = self._value
2622 obj._bound_min = self._bound_min
2623 obj._bound_max = self._bound_max
2625 obj._expl = self._expl
2626 obj.default = self.default
2627 obj.optional = self.optional
2628 obj.offset = self.offset
2629 obj.llen = self.llen
2630 obj.vlen = self.vlen
2631 obj.expl_lenindef = self.expl_lenindef
2632 obj.lenindef = self.lenindef
2633 obj.ber_encoded = self.ber_encoded
2636 def __bytes__(self):
2637 self._assert_ready()
2640 def __eq__(self, their):
2641 if isinstance(their, binary_type):
2642 return self._value == their
2643 if not issubclass(their.__class__, OctetString):
2646 self._value == their._value and
2647 self.tag == their.tag and
2648 self._expl == their._expl
2651 def __lt__(self, their):
2652 return self._value < their._value
2663 return self.__class__(
2666 (self._bound_min, self._bound_max)
2667 if bounds is None else bounds
2669 impl=self.tag if impl is None else impl,
2670 expl=self._expl if expl is None else expl,
2671 default=self.default if default is None else default,
2672 optional=self.optional if optional is None else optional,
2676 self._assert_ready()
2679 len_encode(len(self._value)),
2683 def _decode_chunk(self, lv, offset, decode_path, ctx):
2685 l, llen, v = len_decode(lv)
2686 except DecodeError as err:
2687 raise err.__class__(
2689 klass=self.__class__,
2690 decode_path=decode_path,
2694 raise NotEnoughData(
2695 "encoded length is longer than data",
2696 klass=self.__class__,
2697 decode_path=decode_path,
2700 v, tail = v[:l], v[l:]
2702 obj = self.__class__(
2704 bounds=(self._bound_min, self._bound_max),
2707 default=self.default,
2708 optional=self.optional,
2709 _decoded=(offset, llen, l),
2711 except DecodeError as err:
2714 klass=self.__class__,
2715 decode_path=decode_path,
2718 except BoundsError as err:
2721 klass=self.__class__,
2722 decode_path=decode_path,
2727 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2729 t, tlen, lv = tag_strip(tlv)
2730 except DecodeError as err:
2731 raise err.__class__(
2733 klass=self.__class__,
2734 decode_path=decode_path,
2740 return self._decode_chunk(lv, offset, decode_path, ctx)
2741 if t == self.tag_constructed:
2742 if not ctx.get("bered", False):
2744 "unallowed BER constructed encoding",
2745 klass=self.__class__,
2746 decode_path=decode_path,
2753 l, llen, v = len_decode(lv)
2754 except LenIndefForm:
2755 llen, l, v = 1, 0, lv[1:]
2757 except DecodeError as err:
2758 raise err.__class__(
2760 klass=self.__class__,
2761 decode_path=decode_path,
2765 raise NotEnoughData(
2766 "encoded length is longer than data",
2767 klass=self.__class__,
2768 decode_path=decode_path,
2772 sub_offset = offset + tlen + llen
2776 if v[:EOC_LEN].tobytes() == EOC:
2783 "chunk out of bounds",
2784 klass=self.__class__,
2785 decode_path=decode_path + (str(len(chunks) - 1),),
2786 offset=chunks[-1].offset,
2788 sub_decode_path = decode_path + (str(len(chunks)),)
2790 chunk, v_tail = OctetString().decode(
2793 decode_path=sub_decode_path,
2796 _ctx_immutable=False,
2800 "expected OctetString encoded chunk",
2801 klass=self.__class__,
2802 decode_path=sub_decode_path,
2805 chunks.append(chunk)
2806 sub_offset += chunk.tlvlen
2807 vlen += chunk.tlvlen
2810 obj = self.__class__(
2811 value=b"".join(bytes(chunk) for chunk in chunks),
2812 bounds=(self._bound_min, self._bound_max),
2815 default=self.default,
2816 optional=self.optional,
2817 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2819 except DecodeError as err:
2822 klass=self.__class__,
2823 decode_path=decode_path,
2826 except BoundsError as err:
2829 klass=self.__class__,
2830 decode_path=decode_path,
2833 obj.lenindef = lenindef
2834 obj.ber_encoded = True
2835 return obj, (v[EOC_LEN:] if lenindef else v)
2837 klass=self.__class__,
2838 decode_path=decode_path,
2843 return pp_console_row(next(self.pps()))
2845 def pps(self, decode_path=()):
2848 asn1_type_name=self.asn1_type_name,
2849 obj_name=self.__class__.__name__,
2850 decode_path=decode_path,
2851 value=("%d bytes" % len(self._value)) if self.ready else None,
2852 blob=self._value if self.ready else None,
2853 optional=self.optional,
2854 default=self == self.default,
2855 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2856 expl=None if self._expl is None else tag_decode(self._expl),
2861 expl_offset=self.expl_offset if self.expled else None,
2862 expl_tlen=self.expl_tlen if self.expled else None,
2863 expl_llen=self.expl_llen if self.expled else None,
2864 expl_vlen=self.expl_vlen if self.expled else None,
2865 expl_lenindef=self.expl_lenindef,
2866 lenindef=self.lenindef,
2867 ber_encoded=self.ber_encoded,
2870 defined_by, defined = self.defined or (None, None)
2871 if defined_by is not None:
2873 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2875 for pp in self.pps_lenindef(decode_path):
2880 """``NULL`` null object
2888 tag_default = tag_encode(5)
2889 asn1_type_name = "NULL"
2893 value=None, # unused, but Sequence passes it
2900 :param bytes impl: override default tag with ``IMPLICIT`` one
2901 :param bytes expl: override default tag with ``EXPLICIT`` one
2902 :param bool optional: is object ``OPTIONAL`` in sequence
2904 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2912 obj = self.__class__()
2914 obj._expl = self._expl
2915 obj.default = self.default
2916 obj.optional = self.optional
2917 obj.offset = self.offset
2918 obj.llen = self.llen
2919 obj.vlen = self.vlen
2920 obj.expl_lenindef = self.expl_lenindef
2921 obj.lenindef = self.lenindef
2922 obj.ber_encoded = self.ber_encoded
2925 def __eq__(self, their):
2926 if not issubclass(their.__class__, Null):
2929 self.tag == their.tag and
2930 self._expl == their._expl
2940 return self.__class__(
2941 impl=self.tag if impl is None else impl,
2942 expl=self._expl if expl is None else expl,
2943 optional=self.optional if optional is None else optional,
2947 return self.tag + len_encode(0)
2949 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2951 t, _, lv = tag_strip(tlv)
2952 except DecodeError as err:
2953 raise err.__class__(
2955 klass=self.__class__,
2956 decode_path=decode_path,
2961 klass=self.__class__,
2962 decode_path=decode_path,
2965 if tag_only: # pragma: no cover
2968 l, _, v = len_decode(lv)
2969 except DecodeError as err:
2970 raise err.__class__(
2972 klass=self.__class__,
2973 decode_path=decode_path,
2977 raise InvalidLength(
2978 "Null must have zero length",
2979 klass=self.__class__,
2980 decode_path=decode_path,
2983 obj = self.__class__(
2986 optional=self.optional,
2987 _decoded=(offset, 1, 0),
2992 return pp_console_row(next(self.pps()))
2994 def pps(self, decode_path=()):
2997 asn1_type_name=self.asn1_type_name,
2998 obj_name=self.__class__.__name__,
2999 decode_path=decode_path,
3000 optional=self.optional,
3001 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3002 expl=None if self._expl is None else tag_decode(self._expl),
3007 expl_offset=self.expl_offset if self.expled else None,
3008 expl_tlen=self.expl_tlen if self.expled else None,
3009 expl_llen=self.expl_llen if self.expled else None,
3010 expl_vlen=self.expl_vlen if self.expled else None,
3011 expl_lenindef=self.expl_lenindef,
3014 for pp in self.pps_lenindef(decode_path):
3018 class ObjectIdentifier(Obj):
3019 """``OBJECT IDENTIFIER`` OID type
3021 >>> oid = ObjectIdentifier((1, 2, 3))
3022 OBJECT IDENTIFIER 1.2.3
3023 >>> oid == ObjectIdentifier("1.2.3")
3029 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3030 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3032 >>> str(ObjectIdentifier((3, 1)))
3033 Traceback (most recent call last):
3034 pyderasn.InvalidOID: unacceptable first arc value
3036 __slots__ = ("defines",)
3037 tag_default = tag_encode(6)
3038 asn1_type_name = "OBJECT IDENTIFIER"
3051 :param value: set the value. Either tuples of integers,
3052 string of "."-concatenated integers, or
3053 :py:class:`pyderasn.ObjectIdentifier` object
3054 :param defines: sequence of tuples. Each tuple has two elements.
3055 First one is relative to current one decode
3056 path, aiming to the field defined by that OID.
3057 Read about relative path in
3058 :py:func:`pyderasn.abs_decode_path`. Second
3059 tuple element is ``{OID: pyderasn.Obj()}``
3060 dictionary, mapping between current OID value
3061 and structure applied to defined field.
3062 :ref:`Read about DEFINED BY <definedby>`
3063 :param bytes impl: override default tag with ``IMPLICIT`` one
3064 :param bytes expl: override default tag with ``EXPLICIT`` one
3065 :param default: set default value. Type same as in ``value``
3066 :param bool optional: is object ``OPTIONAL`` in sequence
3068 super(ObjectIdentifier, self).__init__(
3076 if value is not None:
3077 self._value = self._value_sanitize(value)
3078 if default is not None:
3079 default = self._value_sanitize(default)
3080 self.default = self.__class__(
3085 if self._value is None:
3086 self._value = default
3087 self.defines = defines
3089 def __add__(self, their):
3090 if isinstance(their, self.__class__):
3091 return self.__class__(self._value + their._value)
3092 if isinstance(their, tuple):
3093 return self.__class__(self._value + their)
3094 raise InvalidValueType((self.__class__, tuple))
3096 def _value_sanitize(self, value):
3097 if issubclass(value.__class__, ObjectIdentifier):
3099 if isinstance(value, string_types):
3101 value = tuple(int(arc) for arc in value.split("."))
3103 raise InvalidOID("unacceptable arcs values")
3104 if isinstance(value, tuple):
3106 raise InvalidOID("less than 2 arcs")
3107 first_arc = value[0]
3108 if first_arc in (0, 1):
3109 if not (0 <= value[1] <= 39):
3110 raise InvalidOID("second arc is too wide")
3111 elif first_arc == 2:
3114 raise InvalidOID("unacceptable first arc value")
3116 raise InvalidValueType((self.__class__, str, tuple))
3120 return self._value is not None
3123 obj = self.__class__()
3124 obj._value = self._value
3125 obj.defines = self.defines
3127 obj._expl = self._expl
3128 obj.default = self.default
3129 obj.optional = self.optional
3130 obj.offset = self.offset
3131 obj.llen = self.llen
3132 obj.vlen = self.vlen
3133 obj.expl_lenindef = self.expl_lenindef
3134 obj.lenindef = self.lenindef
3135 obj.ber_encoded = self.ber_encoded
3139 self._assert_ready()
3140 return iter(self._value)
3143 return ".".join(str(arc) for arc in self._value or ())
3146 self._assert_ready()
3149 bytes(self._expl or b"") +
3150 str(self._value).encode("ascii"),
3153 def __eq__(self, their):
3154 if isinstance(their, tuple):
3155 return self._value == their
3156 if not issubclass(their.__class__, ObjectIdentifier):
3159 self.tag == their.tag and
3160 self._expl == their._expl and
3161 self._value == their._value
3164 def __lt__(self, their):
3165 return self._value < their._value
3176 return self.__class__(
3178 defines=self.defines if defines is None else defines,
3179 impl=self.tag if impl is None else impl,
3180 expl=self._expl if expl is None else expl,
3181 default=self.default if default is None else default,
3182 optional=self.optional if optional is None else optional,
3186 self._assert_ready()
3188 first_value = value[1]
3189 first_arc = value[0]
3192 elif first_arc == 1:
3194 elif first_arc == 2:
3196 else: # pragma: no cover
3197 raise RuntimeError("invalid arc is stored")
3198 octets = [zero_ended_encode(first_value)]
3199 for arc in value[2:]:
3200 octets.append(zero_ended_encode(arc))
3201 v = b"".join(octets)
3202 return b"".join((self.tag, len_encode(len(v)), v))
3204 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3206 t, _, lv = tag_strip(tlv)
3207 except DecodeError as err:
3208 raise err.__class__(
3210 klass=self.__class__,
3211 decode_path=decode_path,
3216 klass=self.__class__,
3217 decode_path=decode_path,
3220 if tag_only: # pragma: no cover
3223 l, llen, v = len_decode(lv)
3224 except DecodeError as err:
3225 raise err.__class__(
3227 klass=self.__class__,
3228 decode_path=decode_path,
3232 raise NotEnoughData(
3233 "encoded length is longer than data",
3234 klass=self.__class__,
3235 decode_path=decode_path,
3239 raise NotEnoughData(
3241 klass=self.__class__,
3242 decode_path=decode_path,
3245 v, tail = v[:l], v[l:]
3252 octet = indexbytes(v, i)
3253 if i == 0 and octet == 0x80:
3254 if ctx.get("bered", False):
3257 raise DecodeError("non normalized arc encoding")
3258 arc = (arc << 7) | (octet & 0x7F)
3259 if octet & 0x80 == 0:
3267 klass=self.__class__,
3268 decode_path=decode_path,
3272 second_arc = arcs[0]
3273 if 0 <= second_arc <= 39:
3275 elif 40 <= second_arc <= 79:
3281 obj = self.__class__(
3282 value=tuple([first_arc, second_arc] + arcs[1:]),
3285 default=self.default,
3286 optional=self.optional,
3287 _decoded=(offset, llen, l),
3290 obj.ber_encoded = True
3294 return pp_console_row(next(self.pps()))
3296 def pps(self, decode_path=()):
3299 asn1_type_name=self.asn1_type_name,
3300 obj_name=self.__class__.__name__,
3301 decode_path=decode_path,
3302 value=str(self) if self.ready else None,
3303 optional=self.optional,
3304 default=self == self.default,
3305 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3306 expl=None if self._expl is None else tag_decode(self._expl),
3311 expl_offset=self.expl_offset if self.expled else None,
3312 expl_tlen=self.expl_tlen if self.expled else None,
3313 expl_llen=self.expl_llen if self.expled else None,
3314 expl_vlen=self.expl_vlen if self.expled else None,
3315 expl_lenindef=self.expl_lenindef,
3316 ber_encoded=self.ber_encoded,
3319 for pp in self.pps_lenindef(decode_path):
3323 class Enumerated(Integer):
3324 """``ENUMERATED`` integer type
3326 This type is identical to :py:class:`pyderasn.Integer`, but requires
3327 schema to be specified and does not accept values missing from it.
3330 tag_default = tag_encode(10)
3331 asn1_type_name = "ENUMERATED"
3342 bounds=None, # dummy argument, workability for Integer.decode
3344 super(Enumerated, self).__init__(
3353 if len(self.specs) == 0:
3354 raise ValueError("schema must be specified")
3356 def _value_sanitize(self, value):
3357 if isinstance(value, self.__class__):
3358 value = value._value
3359 elif isinstance(value, integer_types):
3360 for _value in itervalues(self.specs):
3365 "unknown integer value: %s" % value,
3366 klass=self.__class__,
3368 elif isinstance(value, string_types):
3369 value = self.specs.get(value)
3371 raise ObjUnknown("integer value: %s" % value)
3373 raise InvalidValueType((self.__class__, int, str))
3377 obj = self.__class__(_specs=self.specs)
3378 obj._value = self._value
3379 obj._bound_min = self._bound_min
3380 obj._bound_max = self._bound_max
3382 obj._expl = self._expl
3383 obj.default = self.default
3384 obj.optional = self.optional
3385 obj.offset = self.offset
3386 obj.llen = self.llen
3387 obj.vlen = self.vlen
3388 obj.expl_lenindef = self.expl_lenindef
3389 obj.lenindef = self.lenindef
3390 obj.ber_encoded = self.ber_encoded
3402 return self.__class__(
3404 impl=self.tag if impl is None else impl,
3405 expl=self._expl if expl is None else expl,
3406 default=self.default if default is None else default,
3407 optional=self.optional if optional is None else optional,
3412 class CommonString(OctetString):
3413 """Common class for all strings
3415 Everything resembles :py:class:`pyderasn.OctetString`, except
3416 ability to deal with unicode text strings.
3418 >>> hexenc("привет мир".encode("utf-8"))
3419 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3420 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3422 >>> s = UTF8String("привет мир")
3423 UTF8String UTF8String привет мир
3425 'привет мир'
3426 >>> hexenc(bytes(s))
3427 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3429 >>> PrintableString("привет мир")
3430 Traceback (most recent call last):
3431 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3433 >>> BMPString("ада", bounds=(2, 2))
3434 Traceback (most recent call last):
3435 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3436 >>> s = BMPString("ад", bounds=(2, 2))
3439 >>> hexenc(bytes(s))
3447 * - :py:class:`pyderasn.UTF8String`
3449 * - :py:class:`pyderasn.NumericString`
3451 * - :py:class:`pyderasn.PrintableString`
3453 * - :py:class:`pyderasn.TeletexString`
3455 * - :py:class:`pyderasn.T61String`
3457 * - :py:class:`pyderasn.VideotexString`
3459 * - :py:class:`pyderasn.IA5String`
3461 * - :py:class:`pyderasn.GraphicString`
3463 * - :py:class:`pyderasn.VisibleString`
3465 * - :py:class:`pyderasn.ISO646String`
3467 * - :py:class:`pyderasn.GeneralString`
3469 * - :py:class:`pyderasn.UniversalString`
3471 * - :py:class:`pyderasn.BMPString`
3474 __slots__ = ("encoding",)
3476 def _value_sanitize(self, value):
3478 value_decoded = None
3479 if isinstance(value, self.__class__):
3480 value_raw = value._value
3481 elif isinstance(value, text_type):
3482 value_decoded = value
3483 elif isinstance(value, binary_type):
3486 raise InvalidValueType((self.__class__, text_type, binary_type))
3489 value_decoded.encode(self.encoding)
3490 if value_raw is None else value_raw
3493 value_raw.decode(self.encoding)
3494 if value_decoded is None else value_decoded
3496 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3497 raise DecodeError(str(err))
3498 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3506 def __eq__(self, their):
3507 if isinstance(their, binary_type):
3508 return self._value == their
3509 if isinstance(their, text_type):
3510 return self._value == their.encode(self.encoding)
3511 if not isinstance(their, self.__class__):
3514 self._value == their._value and
3515 self.tag == their.tag and
3516 self._expl == their._expl
3519 def __unicode__(self):
3521 return self._value.decode(self.encoding)
3522 return text_type(self._value)
3525 return pp_console_row(next(self.pps(no_unicode=PY2)))
3527 def pps(self, decode_path=(), no_unicode=False):
3530 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3533 asn1_type_name=self.asn1_type_name,
3534 obj_name=self.__class__.__name__,
3535 decode_path=decode_path,
3537 optional=self.optional,
3538 default=self == self.default,
3539 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3540 expl=None if self._expl is None else tag_decode(self._expl),
3545 expl_offset=self.expl_offset if self.expled else None,
3546 expl_tlen=self.expl_tlen if self.expled else None,
3547 expl_llen=self.expl_llen if self.expled else None,
3548 expl_vlen=self.expl_vlen if self.expled else None,
3549 expl_lenindef=self.expl_lenindef,
3550 ber_encoded=self.ber_encoded,
3553 for pp in self.pps_lenindef(decode_path):
3557 class UTF8String(CommonString):
3559 tag_default = tag_encode(12)
3561 asn1_type_name = "UTF8String"
3564 class AllowableCharsMixin(object):
3566 def allowable_chars(self):
3568 return self._allowable_chars
3569 return set(six_unichr(c) for c in self._allowable_chars)
3572 class NumericString(AllowableCharsMixin, CommonString):
3575 Its value is properly sanitized: only ASCII digits with spaces can
3578 >>> NumericString().allowable_chars
3579 set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3582 tag_default = tag_encode(18)
3584 asn1_type_name = "NumericString"
3585 _allowable_chars = set(digits.encode("ascii") + b" ")
3587 def _value_sanitize(self, value):
3588 value = super(NumericString, self)._value_sanitize(value)
3589 if not set(value) <= self._allowable_chars:
3590 raise DecodeError("non-numeric value")
3594 class PrintableString(AllowableCharsMixin, CommonString):
3597 Its value is properly sanitized: see X.680 41.4 table 10.
3599 >>> PrintableString().allowable_chars
3600 >>> set([' ', "'", ..., 'z'])
3603 tag_default = tag_encode(19)
3605 asn1_type_name = "PrintableString"
3606 _allowable_chars = set(
3607 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3610 def _value_sanitize(self, value):
3611 value = super(PrintableString, self)._value_sanitize(value)
3612 if not set(value) <= self._allowable_chars:
3613 raise DecodeError("non-printable value")
3617 class TeletexString(CommonString):
3619 tag_default = tag_encode(20)
3621 asn1_type_name = "TeletexString"
3624 class T61String(TeletexString):
3626 asn1_type_name = "T61String"
3629 class VideotexString(CommonString):
3631 tag_default = tag_encode(21)
3632 encoding = "iso-8859-1"
3633 asn1_type_name = "VideotexString"
3636 class IA5String(CommonString):
3638 tag_default = tag_encode(22)
3640 asn1_type_name = "IA5"
3643 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3644 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3645 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3648 class UTCTime(CommonString):
3649 """``UTCTime`` datetime type
3651 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3652 UTCTime UTCTime 2017-09-30T22:07:50
3658 datetime.datetime(2017, 9, 30, 22, 7, 50)
3659 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3660 datetime.datetime(1957, 9, 30, 22, 7, 50)
3663 tag_default = tag_encode(23)
3665 asn1_type_name = "UTCTime"
3667 fmt = "%y%m%d%H%M%SZ"
3677 bounds=None, # dummy argument, workability for OctetString.decode
3680 :param value: set the value. Either datetime type, or
3681 :py:class:`pyderasn.UTCTime` object
3682 :param bytes impl: override default tag with ``IMPLICIT`` one
3683 :param bytes expl: override default tag with ``EXPLICIT`` one
3684 :param default: set default value. Type same as in ``value``
3685 :param bool optional: is object ``OPTIONAL`` in sequence
3687 super(UTCTime, self).__init__(
3695 if value is not None:
3696 self._value = self._value_sanitize(value)
3697 if default is not None:
3698 default = self._value_sanitize(default)
3699 self.default = self.__class__(
3704 if self._value is None:
3705 self._value = default
3707 def _value_sanitize(self, value):
3708 if isinstance(value, self.__class__):
3710 if isinstance(value, datetime):
3711 return value.strftime(self.fmt).encode("ascii")
3712 if isinstance(value, binary_type):
3714 value_decoded = value.decode("ascii")
3715 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3716 raise DecodeError("invalid UTCTime encoding")
3717 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3719 datetime.strptime(value_decoded, self.fmt)
3720 except (TypeError, ValueError):
3721 raise DecodeError("invalid UTCTime format")
3724 raise DecodeError("invalid UTCTime length")
3725 raise InvalidValueType((self.__class__, datetime))
3727 def __eq__(self, their):
3728 if isinstance(their, binary_type):
3729 return self._value == their
3730 if isinstance(their, datetime):
3731 return self.todatetime() == their
3732 if not isinstance(their, self.__class__):
3735 self._value == their._value and
3736 self.tag == their.tag and
3737 self._expl == their._expl
3740 def todatetime(self):
3741 """Convert to datetime
3745 Pay attention that UTCTime can not hold full year, so all years
3746 having < 50 years are treated as 20xx, 19xx otherwise, according
3747 to X.509 recomendation.
3749 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3750 year = value.year % 100
3752 year=(2000 + year) if year < 50 else (1900 + year),
3756 minute=value.minute,
3757 second=value.second,
3761 return pp_console_row(next(self.pps()))
3763 def pps(self, decode_path=()):
3766 asn1_type_name=self.asn1_type_name,
3767 obj_name=self.__class__.__name__,
3768 decode_path=decode_path,
3769 value=self.todatetime().isoformat() if self.ready else None,
3770 optional=self.optional,
3771 default=self == self.default,
3772 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3773 expl=None if self._expl is None else tag_decode(self._expl),
3778 expl_offset=self.expl_offset if self.expled else None,
3779 expl_tlen=self.expl_tlen if self.expled else None,
3780 expl_llen=self.expl_llen if self.expled else None,
3781 expl_vlen=self.expl_vlen if self.expled else None,
3782 expl_lenindef=self.expl_lenindef,
3783 ber_encoded=self.ber_encoded,
3786 for pp in self.pps_lenindef(decode_path):
3790 class GeneralizedTime(UTCTime):
3791 """``GeneralizedTime`` datetime type
3793 This type is similar to :py:class:`pyderasn.UTCTime`.
3795 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3796 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3798 '20170930220750.000123Z'
3799 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3800 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3803 tag_default = tag_encode(24)
3804 asn1_type_name = "GeneralizedTime"
3806 fmt = "%Y%m%d%H%M%SZ"
3807 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3809 def _value_sanitize(self, value):
3810 if isinstance(value, self.__class__):
3812 if isinstance(value, datetime):
3813 return value.strftime(
3814 self.fmt_ms if value.microsecond > 0 else self.fmt
3816 if isinstance(value, binary_type):
3818 value_decoded = value.decode("ascii")
3819 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3820 raise DecodeError("invalid GeneralizedTime encoding")
3821 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3823 datetime.strptime(value_decoded, self.fmt)
3824 except (TypeError, ValueError):
3826 "invalid GeneralizedTime (without ms) format",
3829 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3831 datetime.strptime(value_decoded, self.fmt_ms)
3832 except (TypeError, ValueError):
3834 "invalid GeneralizedTime (with ms) format",
3839 "invalid GeneralizedTime length",
3840 klass=self.__class__,
3842 raise InvalidValueType((self.__class__, datetime))
3844 def todatetime(self):
3845 value = self._value.decode("ascii")
3846 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3847 return datetime.strptime(value, self.fmt)
3848 return datetime.strptime(value, self.fmt_ms)
3851 class GraphicString(CommonString):
3853 tag_default = tag_encode(25)
3854 encoding = "iso-8859-1"
3855 asn1_type_name = "GraphicString"
3858 class VisibleString(CommonString):
3860 tag_default = tag_encode(26)
3862 asn1_type_name = "VisibleString"
3865 class ISO646String(VisibleString):
3867 asn1_type_name = "ISO646String"
3870 class GeneralString(CommonString):
3872 tag_default = tag_encode(27)
3873 encoding = "iso-8859-1"
3874 asn1_type_name = "GeneralString"
3877 class UniversalString(CommonString):
3879 tag_default = tag_encode(28)
3880 encoding = "utf-32-be"
3881 asn1_type_name = "UniversalString"
3884 class BMPString(CommonString):
3886 tag_default = tag_encode(30)
3887 encoding = "utf-16-be"
3888 asn1_type_name = "BMPString"
3892 """``CHOICE`` special type
3896 class GeneralName(Choice):
3898 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3899 ("dNSName", IA5String(impl=tag_ctxp(2))),
3902 >>> gn = GeneralName()
3904 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3905 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3906 >>> gn["dNSName"] = IA5String("bar.baz")
3907 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3908 >>> gn["rfc822Name"]
3911 [2] IA5String IA5 bar.baz
3914 >>> gn.value == gn["dNSName"]
3917 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3919 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3920 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3922 __slots__ = ("specs",)
3924 asn1_type_name = "CHOICE"
3937 :param value: set the value. Either ``(choice, value)`` tuple, or
3938 :py:class:`pyderasn.Choice` object
3939 :param bytes impl: can not be set, do **not** use it
3940 :param bytes expl: override default tag with ``EXPLICIT`` one
3941 :param default: set default value. Type same as in ``value``
3942 :param bool optional: is object ``OPTIONAL`` in sequence
3944 if impl is not None:
3945 raise ValueError("no implicit tag allowed for CHOICE")
3946 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3948 schema = getattr(self, "schema", ())
3949 if len(schema) == 0:
3950 raise ValueError("schema must be specified")
3952 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3955 if value is not None:
3956 self._value = self._value_sanitize(value)
3957 if default is not None:
3958 default_value = self._value_sanitize(default)
3959 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3960 default_obj.specs = self.specs
3961 default_obj._value = default_value
3962 self.default = default_obj
3964 self._value = default_obj.copy()._value
3966 def _value_sanitize(self, value):
3967 if isinstance(value, self.__class__):
3969 if isinstance(value, tuple) and len(value) == 2:
3971 spec = self.specs.get(choice)
3973 raise ObjUnknown(choice)
3974 if not isinstance(obj, spec.__class__):
3975 raise InvalidValueType((spec,))
3976 return (choice, spec(obj))
3977 raise InvalidValueType((self.__class__, tuple))
3981 return self._value is not None and self._value[1].ready
3985 return self.expl_lenindef or (
3986 (self._value is not None) and
3987 self._value[1].bered
3991 obj = self.__class__(schema=self.specs)
3992 obj._expl = self._expl
3993 obj.default = self.default
3994 obj.optional = self.optional
3995 obj.offset = self.offset
3996 obj.llen = self.llen
3997 obj.vlen = self.vlen
3998 obj.expl_lenindef = self.expl_lenindef
3999 obj.lenindef = self.lenindef
4000 obj.ber_encoded = self.ber_encoded
4002 if value is not None:
4003 obj._value = (value[0], value[1].copy())
4006 def __eq__(self, their):
4007 if isinstance(their, tuple) and len(their) == 2:
4008 return self._value == their
4009 if not isinstance(their, self.__class__):
4012 self.specs == their.specs and
4013 self._value == their._value
4023 return self.__class__(
4026 expl=self._expl if expl is None else expl,
4027 default=self.default if default is None else default,
4028 optional=self.optional if optional is None else optional,
4033 self._assert_ready()
4034 return self._value[0]
4038 self._assert_ready()
4039 return self._value[1]
4041 def __getitem__(self, key):
4042 if key not in self.specs:
4043 raise ObjUnknown(key)
4044 if self._value is None:
4046 choice, value = self._value
4051 def __setitem__(self, key, value):
4052 spec = self.specs.get(key)
4054 raise ObjUnknown(key)
4055 if not isinstance(value, spec.__class__):
4056 raise InvalidValueType((spec.__class__,))
4057 self._value = (key, spec(value))
4065 return self._value[1].decoded if self.ready else False
4068 self._assert_ready()
4069 return self._value[1].encode()
4071 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4072 for choice, spec in iteritems(self.specs):
4073 sub_decode_path = decode_path + (choice,)
4079 decode_path=sub_decode_path,
4082 _ctx_immutable=False,
4089 klass=self.__class__,
4090 decode_path=decode_path,
4093 if tag_only: # pragma: no cover
4095 value, tail = spec.decode(
4099 decode_path=sub_decode_path,
4101 _ctx_immutable=False,
4103 obj = self.__class__(
4106 default=self.default,
4107 optional=self.optional,
4108 _decoded=(offset, 0, value.fulllen),
4110 obj._value = (choice, value)
4114 value = pp_console_row(next(self.pps()))
4116 value = "%s[%r]" % (value, self.value)
4119 def pps(self, decode_path=()):
4122 asn1_type_name=self.asn1_type_name,
4123 obj_name=self.__class__.__name__,
4124 decode_path=decode_path,
4125 value=self.choice if self.ready else None,
4126 optional=self.optional,
4127 default=self == self.default,
4128 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4129 expl=None if self._expl is None else tag_decode(self._expl),
4134 expl_lenindef=self.expl_lenindef,
4138 yield self.value.pps(decode_path=decode_path + (self.choice,))
4139 for pp in self.pps_lenindef(decode_path):
4143 class PrimitiveTypes(Choice):
4144 """Predefined ``CHOICE`` for all generic primitive types
4146 It could be useful for general decoding of some unspecified values:
4148 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4149 OCTET STRING 3 bytes 666f6f
4150 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4154 schema = tuple((klass.__name__, klass()) for klass in (
4179 """``ANY`` special type
4181 >>> Any(Integer(-123))
4183 >>> a = Any(OctetString(b"hello world").encode())
4184 ANY 040b68656c6c6f20776f726c64
4185 >>> hexenc(bytes(a))
4186 b'0x040x0bhello world'
4188 __slots__ = ("defined",)
4189 tag_default = tag_encode(0)
4190 asn1_type_name = "ANY"
4200 :param value: set the value. Either any kind of pyderasn's
4201 **ready** object, or bytes. Pay attention that
4202 **no** validation is performed is raw binary value
4204 :param bytes expl: override default tag with ``EXPLICIT`` one
4205 :param bool optional: is object ``OPTIONAL`` in sequence
4207 super(Any, self).__init__(None, expl, None, optional, _decoded)
4208 self._value = None if value is None else self._value_sanitize(value)
4211 def _value_sanitize(self, value):
4212 if isinstance(value, self.__class__):
4214 if isinstance(value, Obj):
4215 return value.encode()
4216 if isinstance(value, binary_type):
4218 raise InvalidValueType((self.__class__, Obj, binary_type))
4222 return self._value is not None
4226 if self.expl_lenindef or self.lenindef:
4228 if self.defined is None:
4230 return self.defined[1].bered
4233 obj = self.__class__()
4234 obj._value = self._value
4236 obj._expl = self._expl
4237 obj.optional = self.optional
4238 obj.offset = self.offset
4239 obj.llen = self.llen
4240 obj.vlen = self.vlen
4241 obj.expl_lenindef = self.expl_lenindef
4242 obj.lenindef = self.lenindef
4243 obj.ber_encoded = self.ber_encoded
4246 def __eq__(self, their):
4247 if isinstance(their, binary_type):
4248 return self._value == their
4249 if issubclass(their.__class__, Any):
4250 return self._value == their._value
4259 return self.__class__(
4261 expl=self._expl if expl is None else expl,
4262 optional=self.optional if optional is None else optional,
4265 def __bytes__(self):
4266 self._assert_ready()
4274 self._assert_ready()
4277 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4279 t, tlen, lv = tag_strip(tlv)
4280 except DecodeError as err:
4281 raise err.__class__(
4283 klass=self.__class__,
4284 decode_path=decode_path,
4288 l, llen, v = len_decode(lv)
4289 except LenIndefForm as err:
4290 if not ctx.get("bered", False):
4291 raise err.__class__(
4293 klass=self.__class__,
4294 decode_path=decode_path,
4297 llen, vlen, v = 1, 0, lv[1:]
4298 sub_offset = offset + tlen + llen
4300 while v[:EOC_LEN].tobytes() != EOC:
4301 chunk, v = Any().decode(
4304 decode_path=decode_path + (str(chunk_i),),
4307 _ctx_immutable=False,
4309 vlen += chunk.tlvlen
4310 sub_offset += chunk.tlvlen
4312 tlvlen = tlen + llen + vlen + EOC_LEN
4313 obj = self.__class__(
4314 value=tlv[:tlvlen].tobytes(),
4316 optional=self.optional,
4317 _decoded=(offset, 0, tlvlen),
4321 return obj, v[EOC_LEN:]
4322 except DecodeError as err:
4323 raise err.__class__(
4325 klass=self.__class__,
4326 decode_path=decode_path,
4330 raise NotEnoughData(
4331 "encoded length is longer than data",
4332 klass=self.__class__,
4333 decode_path=decode_path,
4336 tlvlen = tlen + llen + l
4337 v, tail = tlv[:tlvlen], v[l:]
4338 obj = self.__class__(
4341 optional=self.optional,
4342 _decoded=(offset, 0, tlvlen),
4348 return pp_console_row(next(self.pps()))
4350 def pps(self, decode_path=()):
4353 asn1_type_name=self.asn1_type_name,
4354 obj_name=self.__class__.__name__,
4355 decode_path=decode_path,
4356 blob=self._value if self.ready else None,
4357 optional=self.optional,
4358 default=self == self.default,
4359 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4360 expl=None if self._expl is None else tag_decode(self._expl),
4365 expl_offset=self.expl_offset if self.expled else None,
4366 expl_tlen=self.expl_tlen if self.expled else None,
4367 expl_llen=self.expl_llen if self.expled else None,
4368 expl_vlen=self.expl_vlen if self.expled else None,
4369 expl_lenindef=self.expl_lenindef,
4370 lenindef=self.lenindef,
4373 defined_by, defined = self.defined or (None, None)
4374 if defined_by is not None:
4376 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4378 for pp in self.pps_lenindef(decode_path):
4382 ########################################################################
4383 # ASN.1 constructed types
4384 ########################################################################
4386 def get_def_by_path(defines_by_path, sub_decode_path):
4387 """Get define by decode path
4389 for path, define in defines_by_path:
4390 if len(path) != len(sub_decode_path):
4392 for p1, p2 in zip(path, sub_decode_path):
4393 if (p1 != any) and (p1 != p2):
4399 def abs_decode_path(decode_path, rel_path):
4400 """Create an absolute decode path from current and relative ones
4402 :param decode_path: current decode path, starting point. Tuple of strings
4403 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4404 If first tuple's element is "/", then treat it as
4405 an absolute path, ignoring ``decode_path`` as
4406 starting point. Also this tuple can contain ".."
4407 elements, stripping the leading element from
4410 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4411 ("foo", "bar", "baz", "whatever")
4412 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4414 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4417 if rel_path[0] == "/":
4419 if rel_path[0] == "..":
4420 return abs_decode_path(decode_path[:-1], rel_path[1:])
4421 return decode_path + rel_path
4424 class Sequence(Obj):
4425 """``SEQUENCE`` structure type
4427 You have to make specification of sequence::
4429 class Extension(Sequence):
4431 ("extnID", ObjectIdentifier()),
4432 ("critical", Boolean(default=False)),
4433 ("extnValue", OctetString()),
4436 Then, you can work with it as with dictionary.
4438 >>> ext = Extension()
4439 >>> Extension().specs
4441 ('extnID', OBJECT IDENTIFIER),
4442 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4443 ('extnValue', OCTET STRING),
4445 >>> ext["extnID"] = "1.2.3"
4446 Traceback (most recent call last):
4447 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4448 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4450 You can determine if sequence is ready to be encoded:
4455 Traceback (most recent call last):
4456 pyderasn.ObjNotReady: object is not ready: extnValue
4457 >>> ext["extnValue"] = OctetString(b"foobar")
4461 Value you want to assign, must have the same **type** as in
4462 corresponding specification, but it can have different tags,
4463 optional/default attributes -- they will be taken from specification
4466 class TBSCertificate(Sequence):
4468 ("version", Version(expl=tag_ctxc(0), default="v1")),
4471 >>> tbs = TBSCertificate()
4472 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4474 Assign ``None`` to remove value from sequence.
4476 You can set values in Sequence during its initialization:
4478 >>> AlgorithmIdentifier((
4479 ("algorithm", ObjectIdentifier("1.2.3")),
4480 ("parameters", Any(Null()))
4482 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4484 You can determine if value exists/set in the sequence and take its value:
4486 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4489 OBJECT IDENTIFIER 1.2.3
4491 But pay attention that if value has default, then it won't be (not
4492 in) in the sequence (because ``DEFAULT`` must not be encoded in
4493 DER), but you can read its value:
4495 >>> "critical" in ext, ext["critical"]
4496 (False, BOOLEAN False)
4497 >>> ext["critical"] = Boolean(True)
4498 >>> "critical" in ext, ext["critical"]
4499 (True, BOOLEAN True)
4501 All defaulted values are always optional.
4503 .. _allow_default_values_ctx:
4505 DER prohibits default value encoding and will raise an error if
4506 default value is unexpectedly met during decode.
4507 If :ref:`bered <bered_ctx>` context option is set, then no error
4508 will be raised, but ``bered`` attribute set. You can disable strict
4509 defaulted values existence validation by setting
4510 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4512 Two sequences are equal if they have equal specification (schema),
4513 implicit/explicit tagging and the same values.
4515 __slots__ = ("specs",)
4516 tag_default = tag_encode(form=TagFormConstructed, num=16)
4517 asn1_type_name = "SEQUENCE"
4529 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4531 schema = getattr(self, "schema", ())
4533 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4536 if value is not None:
4537 if issubclass(value.__class__, Sequence):
4538 self._value = value._value
4539 elif hasattr(value, "__iter__"):
4540 for seq_key, seq_value in value:
4541 self[seq_key] = seq_value
4543 raise InvalidValueType((Sequence,))
4544 if default is not None:
4545 if not issubclass(default.__class__, Sequence):
4546 raise InvalidValueType((Sequence,))
4547 default_value = default._value
4548 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4549 default_obj.specs = self.specs
4550 default_obj._value = default_value
4551 self.default = default_obj
4553 self._value = default_obj.copy()._value
4557 for name, spec in iteritems(self.specs):
4558 value = self._value.get(name)
4570 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4572 return any(value.bered for value in itervalues(self._value))
4575 obj = self.__class__(schema=self.specs)
4577 obj._expl = self._expl
4578 obj.default = self.default
4579 obj.optional = self.optional
4580 obj.offset = self.offset
4581 obj.llen = self.llen
4582 obj.vlen = self.vlen
4583 obj.expl_lenindef = self.expl_lenindef
4584 obj.lenindef = self.lenindef
4585 obj.ber_encoded = self.ber_encoded
4586 obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4589 def __eq__(self, their):
4590 if not isinstance(their, self.__class__):
4593 self.specs == their.specs and
4594 self.tag == their.tag and
4595 self._expl == their._expl and
4596 self._value == their._value
4607 return self.__class__(
4610 impl=self.tag if impl is None else impl,
4611 expl=self._expl if expl is None else expl,
4612 default=self.default if default is None else default,
4613 optional=self.optional if optional is None else optional,
4616 def __contains__(self, key):
4617 return key in self._value
4619 def __setitem__(self, key, value):
4620 spec = self.specs.get(key)
4622 raise ObjUnknown(key)
4624 self._value.pop(key, None)
4626 if not isinstance(value, spec.__class__):
4627 raise InvalidValueType((spec.__class__,))
4628 value = spec(value=value)
4629 if spec.default is not None and value == spec.default:
4630 self._value.pop(key, None)
4632 self._value[key] = value
4634 def __getitem__(self, key):
4635 value = self._value.get(key)
4636 if value is not None:
4638 spec = self.specs.get(key)
4640 raise ObjUnknown(key)
4641 if spec.default is not None:
4645 def _encoded_values(self):
4647 for name, spec in iteritems(self.specs):
4648 value = self._value.get(name)
4652 raise ObjNotReady(name)
4653 raws.append(value.encode())
4657 v = b"".join(self._encoded_values())
4658 return b"".join((self.tag, len_encode(len(v)), v))
4660 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4662 t, tlen, lv = tag_strip(tlv)
4663 except DecodeError as err:
4664 raise err.__class__(
4666 klass=self.__class__,
4667 decode_path=decode_path,
4672 klass=self.__class__,
4673 decode_path=decode_path,
4676 if tag_only: # pragma: no cover
4679 ctx_bered = ctx.get("bered", False)
4681 l, llen, v = len_decode(lv)
4682 except LenIndefForm as err:
4684 raise err.__class__(
4686 klass=self.__class__,
4687 decode_path=decode_path,
4690 l, llen, v = 0, 1, lv[1:]
4692 except DecodeError as err:
4693 raise err.__class__(
4695 klass=self.__class__,
4696 decode_path=decode_path,
4700 raise NotEnoughData(
4701 "encoded length is longer than data",
4702 klass=self.__class__,
4703 decode_path=decode_path,
4707 v, tail = v[:l], v[l:]
4709 sub_offset = offset + tlen + llen
4712 ctx_allow_default_values = ctx.get("allow_default_values", False)
4713 for name, spec in iteritems(self.specs):
4714 if spec.optional and (
4715 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4719 sub_decode_path = decode_path + (name,)
4721 value, v_tail = spec.decode(
4725 decode_path=sub_decode_path,
4727 _ctx_immutable=False,
4734 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4735 if defined is not None:
4736 defined_by, defined_spec = defined
4737 if issubclass(value.__class__, SequenceOf):
4738 for i, _value in enumerate(value):
4739 sub_sub_decode_path = sub_decode_path + (
4741 DecodePathDefBy(defined_by),
4743 defined_value, defined_tail = defined_spec.decode(
4744 memoryview(bytes(_value)),
4746 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4747 if value.expled else (value.tlen + value.llen)
4750 decode_path=sub_sub_decode_path,
4752 _ctx_immutable=False,
4754 if len(defined_tail) > 0:
4757 klass=self.__class__,
4758 decode_path=sub_sub_decode_path,
4761 _value.defined = (defined_by, defined_value)
4763 defined_value, defined_tail = defined_spec.decode(
4764 memoryview(bytes(value)),
4766 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4767 if value.expled else (value.tlen + value.llen)
4770 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4772 _ctx_immutable=False,
4774 if len(defined_tail) > 0:
4777 klass=self.__class__,
4778 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4781 value.defined = (defined_by, defined_value)
4783 value_len = value.fulllen
4785 sub_offset += value_len
4787 if spec.default is not None and value == spec.default:
4788 if ctx_bered or ctx_allow_default_values:
4792 "DEFAULT value met",
4793 klass=self.__class__,
4794 decode_path=sub_decode_path,
4797 values[name] = value
4799 spec_defines = getattr(spec, "defines", ())
4800 if len(spec_defines) == 0:
4801 defines_by_path = ctx.get("defines_by_path", ())
4802 if len(defines_by_path) > 0:
4803 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4804 if spec_defines is not None and len(spec_defines) > 0:
4805 for rel_path, schema in spec_defines:
4806 defined = schema.get(value, None)
4807 if defined is not None:
4808 ctx.setdefault("_defines", []).append((
4809 abs_decode_path(sub_decode_path[:-1], rel_path),
4813 if v[:EOC_LEN].tobytes() != EOC:
4816 klass=self.__class__,
4817 decode_path=decode_path,
4825 klass=self.__class__,
4826 decode_path=decode_path,
4829 obj = self.__class__(
4833 default=self.default,
4834 optional=self.optional,
4835 _decoded=(offset, llen, vlen),
4838 obj.lenindef = lenindef
4839 obj.ber_encoded = ber_encoded
4843 value = pp_console_row(next(self.pps()))
4845 for name in self.specs:
4846 _value = self._value.get(name)
4849 cols.append("%s: %s" % (name, repr(_value)))
4850 return "%s[%s]" % (value, "; ".join(cols))
4852 def pps(self, decode_path=()):
4855 asn1_type_name=self.asn1_type_name,
4856 obj_name=self.__class__.__name__,
4857 decode_path=decode_path,
4858 optional=self.optional,
4859 default=self == self.default,
4860 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4861 expl=None if self._expl is None else tag_decode(self._expl),
4866 expl_offset=self.expl_offset if self.expled else None,
4867 expl_tlen=self.expl_tlen if self.expled else None,
4868 expl_llen=self.expl_llen if self.expled else None,
4869 expl_vlen=self.expl_vlen if self.expled else None,
4870 expl_lenindef=self.expl_lenindef,
4871 lenindef=self.lenindef,
4872 ber_encoded=self.ber_encoded,
4875 for name in self.specs:
4876 value = self._value.get(name)
4879 yield value.pps(decode_path=decode_path + (name,))
4880 for pp in self.pps_lenindef(decode_path):
4884 class Set(Sequence):
4885 """``SET`` structure type
4887 Its usage is identical to :py:class:`pyderasn.Sequence`.
4889 .. _allow_unordered_set_ctx:
4891 DER prohibits unordered values encoding and will raise an error
4892 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4893 then no error will occure. Also you can disable strict values
4894 ordering check by setting ``"allow_unordered_set": True``
4895 :ref:`context <ctx>` option.
4898 tag_default = tag_encode(form=TagFormConstructed, num=17)
4899 asn1_type_name = "SET"
4902 raws = self._encoded_values()
4905 return b"".join((self.tag, len_encode(len(v)), v))
4907 def _specs_items(self):
4908 return iteritems(self.specs)
4910 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4912 t, tlen, lv = tag_strip(tlv)
4913 except DecodeError as err:
4914 raise err.__class__(
4916 klass=self.__class__,
4917 decode_path=decode_path,
4922 klass=self.__class__,
4923 decode_path=decode_path,
4929 ctx_bered = ctx.get("bered", False)
4931 l, llen, v = len_decode(lv)
4932 except LenIndefForm as err:
4934 raise err.__class__(
4936 klass=self.__class__,
4937 decode_path=decode_path,
4940 l, llen, v = 0, 1, lv[1:]
4942 except DecodeError as err:
4943 raise err.__class__(
4945 klass=self.__class__,
4946 decode_path=decode_path,
4950 raise NotEnoughData(
4951 "encoded length is longer than data",
4952 klass=self.__class__,
4956 v, tail = v[:l], v[l:]
4958 sub_offset = offset + tlen + llen
4961 ctx_allow_default_values = ctx.get("allow_default_values", False)
4962 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4963 value_prev = memoryview(v[:0])
4966 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4968 for name, spec in self._specs_items():
4969 sub_decode_path = decode_path + (name,)
4975 decode_path=sub_decode_path,
4978 _ctx_immutable=False,
4985 klass=self.__class__,
4986 decode_path=decode_path,
4989 value, v_tail = spec.decode(
4993 decode_path=sub_decode_path,
4995 _ctx_immutable=False,
4997 value_len = value.fulllen
4998 if value_prev.tobytes() > v[:value_len].tobytes():
4999 if ctx_bered or ctx_allow_unordered_set:
5003 "unordered " + self.asn1_type_name,
5004 klass=self.__class__,
5005 decode_path=sub_decode_path,
5008 if spec.default is None or value != spec.default:
5010 elif ctx_bered or ctx_allow_default_values:
5014 "DEFAULT value met",
5015 klass=self.__class__,
5016 decode_path=sub_decode_path,
5019 values[name] = value
5020 value_prev = v[:value_len]
5021 sub_offset += value_len
5024 obj = self.__class__(
5028 default=self.default,
5029 optional=self.optional,
5030 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5033 if v[:EOC_LEN].tobytes() != EOC:
5036 klass=self.__class__,
5037 decode_path=decode_path,
5045 "not all values are ready",
5046 klass=self.__class__,
5047 decode_path=decode_path,
5050 obj.ber_encoded = ber_encoded
5054 class SequenceOf(Obj):
5055 """``SEQUENCE OF`` sequence type
5057 For that kind of type you must specify the object it will carry on
5058 (bounds are for example here, not required)::
5060 class Ints(SequenceOf):
5065 >>> ints.append(Integer(123))
5066 >>> ints.append(Integer(234))
5068 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5069 >>> [int(i) for i in ints]
5071 >>> ints.append(Integer(345))
5072 Traceback (most recent call last):
5073 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5076 >>> ints[1] = Integer(345)
5078 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5080 Also you can initialize sequence with preinitialized values:
5082 >>> ints = Ints([Integer(123), Integer(234)])
5084 __slots__ = ("spec", "_bound_min", "_bound_max")
5085 tag_default = tag_encode(form=TagFormConstructed, num=16)
5086 asn1_type_name = "SEQUENCE OF"
5099 super(SequenceOf, self).__init__(
5107 schema = getattr(self, "schema", None)
5109 raise ValueError("schema must be specified")
5111 self._bound_min, self._bound_max = getattr(
5115 ) if bounds is None else bounds
5117 if value is not None:
5118 self._value = self._value_sanitize(value)
5119 if default is not None:
5120 default_value = self._value_sanitize(default)
5121 default_obj = self.__class__(
5126 default_obj._value = default_value
5127 self.default = default_obj
5129 self._value = default_obj.copy()._value
5131 def _value_sanitize(self, value):
5132 if issubclass(value.__class__, SequenceOf):
5133 value = value._value
5134 elif hasattr(value, "__iter__"):
5137 raise InvalidValueType((self.__class__, iter))
5138 if not self._bound_min <= len(value) <= self._bound_max:
5139 raise BoundsError(self._bound_min, len(value), self._bound_max)
5141 if not isinstance(v, self.spec.__class__):
5142 raise InvalidValueType((self.spec.__class__,))
5147 return all(v.ready for v in self._value)
5151 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5153 return any(v.bered for v in self._value)
5156 obj = self.__class__(schema=self.spec)
5157 obj._bound_min = self._bound_min
5158 obj._bound_max = self._bound_max
5160 obj._expl = self._expl
5161 obj.default = self.default
5162 obj.optional = self.optional
5163 obj.offset = self.offset
5164 obj.llen = self.llen
5165 obj.vlen = self.vlen
5166 obj.expl_lenindef = self.expl_lenindef
5167 obj.lenindef = self.lenindef
5168 obj.ber_encoded = self.ber_encoded
5169 obj._value = [v.copy() for v in self._value]
5172 def __eq__(self, their):
5173 if isinstance(their, self.__class__):
5175 self.spec == their.spec and
5176 self.tag == their.tag and
5177 self._expl == their._expl and
5178 self._value == their._value
5180 if hasattr(their, "__iter__"):
5181 return self._value == list(their)
5193 return self.__class__(
5197 (self._bound_min, self._bound_max)
5198 if bounds is None else bounds
5200 impl=self.tag if impl is None else impl,
5201 expl=self._expl if expl is None else expl,
5202 default=self.default if default is None else default,
5203 optional=self.optional if optional is None else optional,
5206 def __contains__(self, key):
5207 return key in self._value
5209 def append(self, value):
5210 if not isinstance(value, self.spec.__class__):
5211 raise InvalidValueType((self.spec.__class__,))
5212 if len(self._value) + 1 > self._bound_max:
5215 len(self._value) + 1,
5218 self._value.append(value)
5221 self._assert_ready()
5222 return iter(self._value)
5225 self._assert_ready()
5226 return len(self._value)
5228 def __setitem__(self, key, value):
5229 if not isinstance(value, self.spec.__class__):
5230 raise InvalidValueType((self.spec.__class__,))
5231 self._value[key] = self.spec(value=value)
5233 def __getitem__(self, key):
5234 return self._value[key]
5236 def _encoded_values(self):
5237 return [v.encode() for v in self._value]
5240 v = b"".join(self._encoded_values())
5241 return b"".join((self.tag, len_encode(len(v)), v))
5243 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5245 t, tlen, lv = tag_strip(tlv)
5246 except DecodeError as err:
5247 raise err.__class__(
5249 klass=self.__class__,
5250 decode_path=decode_path,
5255 klass=self.__class__,
5256 decode_path=decode_path,
5262 ctx_bered = ctx.get("bered", False)
5264 l, llen, v = len_decode(lv)
5265 except LenIndefForm as err:
5267 raise err.__class__(
5269 klass=self.__class__,
5270 decode_path=decode_path,
5273 l, llen, v = 0, 1, lv[1:]
5275 except DecodeError as err:
5276 raise err.__class__(
5278 klass=self.__class__,
5279 decode_path=decode_path,
5283 raise NotEnoughData(
5284 "encoded length is longer than data",
5285 klass=self.__class__,
5286 decode_path=decode_path,
5290 v, tail = v[:l], v[l:]
5292 sub_offset = offset + tlen + llen
5294 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5295 value_prev = memoryview(v[:0])
5299 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5301 sub_decode_path = decode_path + (str(len(_value)),)
5302 value, v_tail = spec.decode(
5306 decode_path=sub_decode_path,
5308 _ctx_immutable=False,
5310 value_len = value.fulllen
5312 if value_prev.tobytes() > v[:value_len].tobytes():
5313 if ctx_bered or ctx_allow_unordered_set:
5317 "unordered " + self.asn1_type_name,
5318 klass=self.__class__,
5319 decode_path=sub_decode_path,
5322 value_prev = v[:value_len]
5323 _value.append(value)
5324 sub_offset += value_len
5328 obj = self.__class__(
5331 bounds=(self._bound_min, self._bound_max),
5334 default=self.default,
5335 optional=self.optional,
5336 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5338 except BoundsError as err:
5341 klass=self.__class__,
5342 decode_path=decode_path,
5346 if v[:EOC_LEN].tobytes() != EOC:
5349 klass=self.__class__,
5350 decode_path=decode_path,
5355 obj.ber_encoded = ber_encoded
5360 pp_console_row(next(self.pps())),
5361 ", ".join(repr(v) for v in self._value),
5364 def pps(self, decode_path=()):
5367 asn1_type_name=self.asn1_type_name,
5368 obj_name=self.__class__.__name__,
5369 decode_path=decode_path,
5370 optional=self.optional,
5371 default=self == self.default,
5372 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5373 expl=None if self._expl is None else tag_decode(self._expl),
5378 expl_offset=self.expl_offset if self.expled else None,
5379 expl_tlen=self.expl_tlen if self.expled else None,
5380 expl_llen=self.expl_llen if self.expled else None,
5381 expl_vlen=self.expl_vlen if self.expled else None,
5382 expl_lenindef=self.expl_lenindef,
5383 lenindef=self.lenindef,
5384 ber_encoded=self.ber_encoded,
5387 for i, value in enumerate(self._value):
5388 yield value.pps(decode_path=decode_path + (str(i),))
5389 for pp in self.pps_lenindef(decode_path):
5393 class SetOf(SequenceOf):
5394 """``SET OF`` sequence type
5396 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5399 tag_default = tag_encode(form=TagFormConstructed, num=17)
5400 asn1_type_name = "SET OF"
5403 raws = self._encoded_values()
5406 return b"".join((self.tag, len_encode(len(v)), v))
5408 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5409 return super(SetOf, self)._decode(
5415 ordering_check=True,
5419 def obj_by_path(pypath): # pragma: no cover
5420 """Import object specified as string Python path
5422 Modules must be separated from classes/functions with ``:``.
5424 >>> obj_by_path("foo.bar:Baz")
5425 <class 'foo.bar.Baz'>
5426 >>> obj_by_path("foo.bar:Baz.boo")
5427 <classmethod 'foo.bar.Baz.boo'>
5429 mod, objs = pypath.rsplit(":", 1)
5430 from importlib import import_module
5431 obj = import_module(mod)
5432 for obj_name in objs.split("."):
5433 obj = getattr(obj, obj_name)
5437 def generic_decoder(): # pragma: no cover
5438 # All of this below is a big hack with self references
5439 choice = PrimitiveTypes()
5440 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5441 choice.specs["SetOf"] = SetOf(schema=choice)
5442 for i in six_xrange(31):
5443 choice.specs["SequenceOf%d" % i] = SequenceOf(
5447 choice.specs["Any"] = Any()
5449 # Class name equals to type name, to omit it from output
5450 class SEQUENCEOF(SequenceOf):
5458 with_decode_path=False,
5459 decode_path_only=(),
5461 def _pprint_pps(pps):
5463 if hasattr(pp, "_fields"):
5465 decode_path_only != () and
5466 pp.decode_path[:len(decode_path_only)] != decode_path_only
5469 if pp.asn1_type_name == Choice.asn1_type_name:
5471 pp_kwargs = pp._asdict()
5472 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5473 pp = _pp(**pp_kwargs)
5474 yield pp_console_row(
5479 with_colours=with_colours,
5480 with_decode_path=with_decode_path,
5481 decode_path_len_decrease=len(decode_path_only),
5483 for row in pp_console_blob(
5485 decode_path_len_decrease=len(decode_path_only),
5489 for row in _pprint_pps(pp):
5491 return "\n".join(_pprint_pps(obj.pps()))
5492 return SEQUENCEOF(), pprint_any
5495 def main(): # pragma: no cover
5497 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5498 parser.add_argument(
5502 help="Skip that number of bytes from the beginning",
5504 parser.add_argument(
5506 help="Python path to dictionary with OIDs",
5508 parser.add_argument(
5510 help="Python path to schema definition to use",
5512 parser.add_argument(
5513 "--defines-by-path",
5514 help="Python path to decoder's defines_by_path",
5516 parser.add_argument(
5518 action="store_true",
5519 help="Disallow BER encoding",
5521 parser.add_argument(
5522 "--print-decode-path",
5523 action="store_true",
5524 help="Print decode paths",
5526 parser.add_argument(
5527 "--decode-path-only",
5528 help="Print only specified decode path",
5530 parser.add_argument(
5532 action="store_true",
5533 help="Allow explicit tag out-of-bound",
5535 parser.add_argument(
5537 type=argparse.FileType("rb"),
5538 help="Path to DER file you want to decode",
5540 args = parser.parse_args()
5541 args.DERFile.seek(args.skip)
5542 der = memoryview(args.DERFile.read())
5543 args.DERFile.close()
5544 oids = obj_by_path(args.oids) if args.oids else {}
5546 schema = obj_by_path(args.schema)
5547 from functools import partial
5548 pprinter = partial(pprint, big_blobs=True)
5550 schema, pprinter = generic_decoder()
5552 "bered": not args.nobered,
5553 "allow_expl_oob": args.allow_expl_oob,
5555 if args.defines_by_path is not None:
5556 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5557 obj, tail = schema().decode(der, ctx=ctx)
5561 with_colours=True if environ.get("NO_COLOR") is None else False,
5562 with_decode_path=args.print_decode_path,
5564 () if args.decode_path_only is None else
5565 tuple(args.decode_path_only.split(":"))
5569 print("\nTrailing data: %s" % hexenc(tail))
5572 if __name__ == "__main__":