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 SET01 = frozenset(("0", "1"))
2053 class BitString(Obj):
2054 """``BIT STRING`` bit string type
2056 >>> BitString(b"hello world")
2057 BIT STRING 88 bits 68656c6c6f20776f726c64
2060 >>> b == b"hello world"
2065 >>> BitString("'0A3B5F291CD'H")
2066 BIT STRING 44 bits 0a3b5f291cd0
2067 >>> b = BitString("'010110000000'B")
2068 BIT STRING 12 bits 5800
2071 >>> b[0], b[1], b[2], b[3]
2072 (False, True, False, True)
2076 [False, True, False, True, True, False, False, False, False, False, False, False]
2080 class KeyUsage(BitString):
2082 ("digitalSignature", 0),
2083 ("nonRepudiation", 1),
2084 ("keyEncipherment", 2),
2087 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2088 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2090 ['nonRepudiation', 'keyEncipherment']
2092 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2096 Pay attention that BIT STRING can be encoded both in primitive
2097 and constructed forms. Decoder always checks constructed form tag
2098 additionally to specified primitive one. If BER decoding is
2099 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2100 of DER restrictions.
2102 __slots__ = ("tag_constructed", "specs", "defined")
2103 tag_default = tag_encode(3)
2104 asn1_type_name = "BIT STRING"
2117 :param value: set the value. Either binary type, tuple of named
2118 values (if ``schema`` is specified in the class),
2119 string in ``'XXX...'B`` form, or
2120 :py:class:`pyderasn.BitString` object
2121 :param bytes impl: override default tag with ``IMPLICIT`` one
2122 :param bytes expl: override default tag with ``EXPLICIT`` one
2123 :param default: set default value. Type same as in ``value``
2124 :param bool optional: is object ``OPTIONAL`` in sequence
2126 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2127 specs = getattr(self, "schema", {}) if _specs is None else _specs
2128 self.specs = specs if isinstance(specs, dict) else dict(specs)
2129 self._value = None if value is None else self._value_sanitize(value)
2130 if default is not None:
2131 default = self._value_sanitize(default)
2132 self.default = self.__class__(
2138 self._value = default
2140 tag_klass, _, tag_num = tag_decode(self.tag)
2141 self.tag_constructed = tag_encode(
2143 form=TagFormConstructed,
2147 def _bits2octets(self, bits):
2148 if len(self.specs) > 0:
2149 bits = bits.rstrip("0")
2151 bits += "0" * ((8 - (bit_len % 8)) % 8)
2152 octets = bytearray(len(bits) // 8)
2153 for i in six_xrange(len(octets)):
2154 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2155 return bit_len, bytes(octets)
2157 def _value_sanitize(self, value):
2158 if issubclass(value.__class__, BitString):
2160 if isinstance(value, (string_types, binary_type)):
2162 isinstance(value, string_types) and
2163 value.startswith("'")
2165 if value.endswith("'B"):
2167 if not frozenset(value) <= SET01:
2168 raise ValueError("B's coding contains unacceptable chars")
2169 return self._bits2octets(value)
2170 elif value.endswith("'H"):
2174 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2176 if isinstance(value, binary_type):
2177 return (len(value) * 8, value)
2179 raise InvalidValueType((self.__class__, string_types, binary_type))
2180 if isinstance(value, tuple):
2183 isinstance(value[0], integer_types) and
2184 isinstance(value[1], binary_type)
2189 bit = self.specs.get(name)
2191 raise ObjUnknown("BitString value: %s" % name)
2194 return self._bits2octets("")
2195 bits = frozenset(bits)
2196 return self._bits2octets("".join(
2197 ("1" if bit in bits else "0")
2198 for bit in six_xrange(max(bits) + 1)
2200 raise InvalidValueType((self.__class__, binary_type, string_types))
2204 return self._value is not None
2207 obj = self.__class__(_specs=self.specs)
2209 if value is not None:
2210 value = (value[0], value[1])
2213 obj._expl = self._expl
2214 obj.default = self.default
2215 obj.optional = self.optional
2216 obj.offset = self.offset
2217 obj.llen = self.llen
2218 obj.vlen = self.vlen
2219 obj.expl_lenindef = self.expl_lenindef
2220 obj.lenindef = self.lenindef
2221 obj.ber_encoded = self.ber_encoded
2225 self._assert_ready()
2226 for i in six_xrange(self._value[0]):
2231 self._assert_ready()
2232 return self._value[0]
2234 def __bytes__(self):
2235 self._assert_ready()
2236 return self._value[1]
2238 def __eq__(self, their):
2239 if isinstance(their, bytes):
2240 return self._value[1] == their
2241 if not issubclass(their.__class__, BitString):
2244 self._value == their._value and
2245 self.tag == their.tag and
2246 self._expl == their._expl
2251 return [name for name, bit in iteritems(self.specs) if self[bit]]
2261 return self.__class__(
2263 impl=self.tag if impl is None else impl,
2264 expl=self._expl if expl is None else expl,
2265 default=self.default if default is None else default,
2266 optional=self.optional if optional is None else optional,
2270 def __getitem__(self, key):
2271 if isinstance(key, int):
2272 bit_len, octets = self._value
2276 byte2int(memoryview(octets)[key // 8:]) >>
2279 if isinstance(key, string_types):
2280 value = self.specs.get(key)
2282 raise ObjUnknown("BitString value: %s" % key)
2284 raise InvalidValueType((int, str))
2287 self._assert_ready()
2288 bit_len, octets = self._value
2291 len_encode(len(octets) + 1),
2292 int2byte((8 - bit_len % 8) % 8),
2296 def _decode_chunk(self, lv, offset, decode_path, ctx):
2298 l, llen, v = len_decode(lv)
2299 except DecodeError as err:
2300 raise err.__class__(
2302 klass=self.__class__,
2303 decode_path=decode_path,
2307 raise NotEnoughData(
2308 "encoded length is longer than data",
2309 klass=self.__class__,
2310 decode_path=decode_path,
2314 raise NotEnoughData(
2316 klass=self.__class__,
2317 decode_path=decode_path,
2320 pad_size = byte2int(v)
2321 if l == 1 and pad_size != 0:
2323 "invalid empty value",
2324 klass=self.__class__,
2325 decode_path=decode_path,
2331 klass=self.__class__,
2332 decode_path=decode_path,
2335 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2338 klass=self.__class__,
2339 decode_path=decode_path,
2342 v, tail = v[:l], v[l:]
2343 obj = self.__class__(
2344 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2347 default=self.default,
2348 optional=self.optional,
2350 _decoded=(offset, llen, l),
2354 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2356 t, tlen, lv = tag_strip(tlv)
2357 except DecodeError as err:
2358 raise err.__class__(
2360 klass=self.__class__,
2361 decode_path=decode_path,
2365 if tag_only: # pragma: no cover
2367 return self._decode_chunk(lv, offset, decode_path, ctx)
2368 if t == self.tag_constructed:
2369 if not ctx.get("bered", False):
2371 "unallowed BER constructed encoding",
2372 klass=self.__class__,
2373 decode_path=decode_path,
2376 if tag_only: # pragma: no cover
2380 l, llen, v = len_decode(lv)
2381 except LenIndefForm:
2382 llen, l, v = 1, 0, lv[1:]
2384 except DecodeError as err:
2385 raise err.__class__(
2387 klass=self.__class__,
2388 decode_path=decode_path,
2392 raise NotEnoughData(
2393 "encoded length is longer than data",
2394 klass=self.__class__,
2395 decode_path=decode_path,
2398 if not lenindef and l == 0:
2399 raise NotEnoughData(
2401 klass=self.__class__,
2402 decode_path=decode_path,
2406 sub_offset = offset + tlen + llen
2410 if v[:EOC_LEN].tobytes() == EOC:
2417 "chunk out of bounds",
2418 klass=self.__class__,
2419 decode_path=decode_path + (str(len(chunks) - 1),),
2420 offset=chunks[-1].offset,
2422 sub_decode_path = decode_path + (str(len(chunks)),)
2424 chunk, v_tail = BitString().decode(
2427 decode_path=sub_decode_path,
2430 _ctx_immutable=False,
2434 "expected BitString encoded chunk",
2435 klass=self.__class__,
2436 decode_path=sub_decode_path,
2439 chunks.append(chunk)
2440 sub_offset += chunk.tlvlen
2441 vlen += chunk.tlvlen
2443 if len(chunks) == 0:
2446 klass=self.__class__,
2447 decode_path=decode_path,
2452 for chunk_i, chunk in enumerate(chunks[:-1]):
2453 if chunk.bit_len % 8 != 0:
2455 "BitString chunk is not multiple of 8 bits",
2456 klass=self.__class__,
2457 decode_path=decode_path + (str(chunk_i),),
2458 offset=chunk.offset,
2460 values.append(bytes(chunk))
2461 bit_len += chunk.bit_len
2462 chunk_last = chunks[-1]
2463 values.append(bytes(chunk_last))
2464 bit_len += chunk_last.bit_len
2465 obj = self.__class__(
2466 value=(bit_len, b"".join(values)),
2469 default=self.default,
2470 optional=self.optional,
2472 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2474 obj.lenindef = lenindef
2475 obj.ber_encoded = True
2476 return obj, (v[EOC_LEN:] if lenindef else v)
2478 klass=self.__class__,
2479 decode_path=decode_path,
2484 return pp_console_row(next(self.pps()))
2486 def pps(self, decode_path=()):
2490 bit_len, blob = self._value
2491 value = "%d bits" % bit_len
2492 if len(self.specs) > 0:
2493 blob = tuple(self.named)
2496 asn1_type_name=self.asn1_type_name,
2497 obj_name=self.__class__.__name__,
2498 decode_path=decode_path,
2501 optional=self.optional,
2502 default=self == self.default,
2503 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2504 expl=None if self._expl is None else tag_decode(self._expl),
2509 expl_offset=self.expl_offset if self.expled else None,
2510 expl_tlen=self.expl_tlen if self.expled else None,
2511 expl_llen=self.expl_llen if self.expled else None,
2512 expl_vlen=self.expl_vlen if self.expled else None,
2513 expl_lenindef=self.expl_lenindef,
2514 lenindef=self.lenindef,
2515 ber_encoded=self.ber_encoded,
2518 defined_by, defined = self.defined or (None, None)
2519 if defined_by is not None:
2521 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2523 for pp in self.pps_lenindef(decode_path):
2527 class OctetString(Obj):
2528 """``OCTET STRING`` binary string type
2530 >>> s = OctetString(b"hello world")
2531 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2532 >>> s == OctetString(b"hello world")
2537 >>> OctetString(b"hello", bounds=(4, 4))
2538 Traceback (most recent call last):
2539 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2540 >>> OctetString(b"hell", bounds=(4, 4))
2541 OCTET STRING 4 bytes 68656c6c
2545 Pay attention that OCTET STRING can be encoded both in primitive
2546 and constructed forms. Decoder always checks constructed form tag
2547 additionally to specified primitive one. If BER decoding is
2548 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2549 of DER restrictions.
2551 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2552 tag_default = tag_encode(4)
2553 asn1_type_name = "OCTET STRING"
2566 :param value: set the value. Either binary type, or
2567 :py:class:`pyderasn.OctetString` object
2568 :param bounds: set ``(MIN, MAX)`` value size constraint.
2569 (-inf, +inf) by default
2570 :param bytes impl: override default tag with ``IMPLICIT`` one
2571 :param bytes expl: override default tag with ``EXPLICIT`` one
2572 :param default: set default value. Type same as in ``value``
2573 :param bool optional: is object ``OPTIONAL`` in sequence
2575 super(OctetString, self).__init__(
2583 self._bound_min, self._bound_max = getattr(
2587 ) if bounds is None else bounds
2588 if value is not None:
2589 self._value = self._value_sanitize(value)
2590 if default is not None:
2591 default = self._value_sanitize(default)
2592 self.default = self.__class__(
2597 if self._value is None:
2598 self._value = default
2600 tag_klass, _, tag_num = tag_decode(self.tag)
2601 self.tag_constructed = tag_encode(
2603 form=TagFormConstructed,
2607 def _value_sanitize(self, value):
2608 if issubclass(value.__class__, OctetString):
2609 value = value._value
2610 elif isinstance(value, binary_type):
2613 raise InvalidValueType((self.__class__, bytes))
2614 if not self._bound_min <= len(value) <= self._bound_max:
2615 raise BoundsError(self._bound_min, len(value), self._bound_max)
2620 return self._value is not None
2623 obj = self.__class__()
2624 obj._value = self._value
2625 obj._bound_min = self._bound_min
2626 obj._bound_max = self._bound_max
2628 obj._expl = self._expl
2629 obj.default = self.default
2630 obj.optional = self.optional
2631 obj.offset = self.offset
2632 obj.llen = self.llen
2633 obj.vlen = self.vlen
2634 obj.expl_lenindef = self.expl_lenindef
2635 obj.lenindef = self.lenindef
2636 obj.ber_encoded = self.ber_encoded
2639 def __bytes__(self):
2640 self._assert_ready()
2643 def __eq__(self, their):
2644 if isinstance(their, binary_type):
2645 return self._value == their
2646 if not issubclass(their.__class__, OctetString):
2649 self._value == their._value and
2650 self.tag == their.tag and
2651 self._expl == their._expl
2654 def __lt__(self, their):
2655 return self._value < their._value
2666 return self.__class__(
2669 (self._bound_min, self._bound_max)
2670 if bounds is None else bounds
2672 impl=self.tag if impl is None else impl,
2673 expl=self._expl if expl is None else expl,
2674 default=self.default if default is None else default,
2675 optional=self.optional if optional is None else optional,
2679 self._assert_ready()
2682 len_encode(len(self._value)),
2686 def _decode_chunk(self, lv, offset, decode_path, ctx):
2688 l, llen, v = len_decode(lv)
2689 except DecodeError as err:
2690 raise err.__class__(
2692 klass=self.__class__,
2693 decode_path=decode_path,
2697 raise NotEnoughData(
2698 "encoded length is longer than data",
2699 klass=self.__class__,
2700 decode_path=decode_path,
2703 v, tail = v[:l], v[l:]
2705 obj = self.__class__(
2707 bounds=(self._bound_min, self._bound_max),
2710 default=self.default,
2711 optional=self.optional,
2712 _decoded=(offset, llen, l),
2714 except DecodeError as err:
2717 klass=self.__class__,
2718 decode_path=decode_path,
2721 except BoundsError as err:
2724 klass=self.__class__,
2725 decode_path=decode_path,
2730 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2732 t, tlen, lv = tag_strip(tlv)
2733 except DecodeError as err:
2734 raise err.__class__(
2736 klass=self.__class__,
2737 decode_path=decode_path,
2743 return self._decode_chunk(lv, offset, decode_path, ctx)
2744 if t == self.tag_constructed:
2745 if not ctx.get("bered", False):
2747 "unallowed BER constructed encoding",
2748 klass=self.__class__,
2749 decode_path=decode_path,
2756 l, llen, v = len_decode(lv)
2757 except LenIndefForm:
2758 llen, l, v = 1, 0, lv[1:]
2760 except DecodeError as err:
2761 raise err.__class__(
2763 klass=self.__class__,
2764 decode_path=decode_path,
2768 raise NotEnoughData(
2769 "encoded length is longer than data",
2770 klass=self.__class__,
2771 decode_path=decode_path,
2775 sub_offset = offset + tlen + llen
2779 if v[:EOC_LEN].tobytes() == EOC:
2786 "chunk out of bounds",
2787 klass=self.__class__,
2788 decode_path=decode_path + (str(len(chunks) - 1),),
2789 offset=chunks[-1].offset,
2791 sub_decode_path = decode_path + (str(len(chunks)),)
2793 chunk, v_tail = OctetString().decode(
2796 decode_path=sub_decode_path,
2799 _ctx_immutable=False,
2803 "expected OctetString encoded chunk",
2804 klass=self.__class__,
2805 decode_path=sub_decode_path,
2808 chunks.append(chunk)
2809 sub_offset += chunk.tlvlen
2810 vlen += chunk.tlvlen
2813 obj = self.__class__(
2814 value=b"".join(bytes(chunk) for chunk in chunks),
2815 bounds=(self._bound_min, self._bound_max),
2818 default=self.default,
2819 optional=self.optional,
2820 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2822 except DecodeError as err:
2825 klass=self.__class__,
2826 decode_path=decode_path,
2829 except BoundsError as err:
2832 klass=self.__class__,
2833 decode_path=decode_path,
2836 obj.lenindef = lenindef
2837 obj.ber_encoded = True
2838 return obj, (v[EOC_LEN:] if lenindef else v)
2840 klass=self.__class__,
2841 decode_path=decode_path,
2846 return pp_console_row(next(self.pps()))
2848 def pps(self, decode_path=()):
2851 asn1_type_name=self.asn1_type_name,
2852 obj_name=self.__class__.__name__,
2853 decode_path=decode_path,
2854 value=("%d bytes" % len(self._value)) if self.ready else None,
2855 blob=self._value if self.ready else None,
2856 optional=self.optional,
2857 default=self == self.default,
2858 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2859 expl=None if self._expl is None else tag_decode(self._expl),
2864 expl_offset=self.expl_offset if self.expled else None,
2865 expl_tlen=self.expl_tlen if self.expled else None,
2866 expl_llen=self.expl_llen if self.expled else None,
2867 expl_vlen=self.expl_vlen if self.expled else None,
2868 expl_lenindef=self.expl_lenindef,
2869 lenindef=self.lenindef,
2870 ber_encoded=self.ber_encoded,
2873 defined_by, defined = self.defined or (None, None)
2874 if defined_by is not None:
2876 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2878 for pp in self.pps_lenindef(decode_path):
2883 """``NULL`` null object
2891 tag_default = tag_encode(5)
2892 asn1_type_name = "NULL"
2896 value=None, # unused, but Sequence passes it
2903 :param bytes impl: override default tag with ``IMPLICIT`` one
2904 :param bytes expl: override default tag with ``EXPLICIT`` one
2905 :param bool optional: is object ``OPTIONAL`` in sequence
2907 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2915 obj = self.__class__()
2917 obj._expl = self._expl
2918 obj.default = self.default
2919 obj.optional = self.optional
2920 obj.offset = self.offset
2921 obj.llen = self.llen
2922 obj.vlen = self.vlen
2923 obj.expl_lenindef = self.expl_lenindef
2924 obj.lenindef = self.lenindef
2925 obj.ber_encoded = self.ber_encoded
2928 def __eq__(self, their):
2929 if not issubclass(their.__class__, Null):
2932 self.tag == their.tag and
2933 self._expl == their._expl
2943 return self.__class__(
2944 impl=self.tag if impl is None else impl,
2945 expl=self._expl if expl is None else expl,
2946 optional=self.optional if optional is None else optional,
2950 return self.tag + len_encode(0)
2952 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2954 t, _, lv = tag_strip(tlv)
2955 except DecodeError as err:
2956 raise err.__class__(
2958 klass=self.__class__,
2959 decode_path=decode_path,
2964 klass=self.__class__,
2965 decode_path=decode_path,
2968 if tag_only: # pragma: no cover
2971 l, _, v = len_decode(lv)
2972 except DecodeError as err:
2973 raise err.__class__(
2975 klass=self.__class__,
2976 decode_path=decode_path,
2980 raise InvalidLength(
2981 "Null must have zero length",
2982 klass=self.__class__,
2983 decode_path=decode_path,
2986 obj = self.__class__(
2989 optional=self.optional,
2990 _decoded=(offset, 1, 0),
2995 return pp_console_row(next(self.pps()))
2997 def pps(self, decode_path=()):
3000 asn1_type_name=self.asn1_type_name,
3001 obj_name=self.__class__.__name__,
3002 decode_path=decode_path,
3003 optional=self.optional,
3004 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3005 expl=None if self._expl is None else tag_decode(self._expl),
3010 expl_offset=self.expl_offset if self.expled else None,
3011 expl_tlen=self.expl_tlen if self.expled else None,
3012 expl_llen=self.expl_llen if self.expled else None,
3013 expl_vlen=self.expl_vlen if self.expled else None,
3014 expl_lenindef=self.expl_lenindef,
3017 for pp in self.pps_lenindef(decode_path):
3021 class ObjectIdentifier(Obj):
3022 """``OBJECT IDENTIFIER`` OID type
3024 >>> oid = ObjectIdentifier((1, 2, 3))
3025 OBJECT IDENTIFIER 1.2.3
3026 >>> oid == ObjectIdentifier("1.2.3")
3032 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3033 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3035 >>> str(ObjectIdentifier((3, 1)))
3036 Traceback (most recent call last):
3037 pyderasn.InvalidOID: unacceptable first arc value
3039 __slots__ = ("defines",)
3040 tag_default = tag_encode(6)
3041 asn1_type_name = "OBJECT IDENTIFIER"
3054 :param value: set the value. Either tuples of integers,
3055 string of "."-concatenated integers, or
3056 :py:class:`pyderasn.ObjectIdentifier` object
3057 :param defines: sequence of tuples. Each tuple has two elements.
3058 First one is relative to current one decode
3059 path, aiming to the field defined by that OID.
3060 Read about relative path in
3061 :py:func:`pyderasn.abs_decode_path`. Second
3062 tuple element is ``{OID: pyderasn.Obj()}``
3063 dictionary, mapping between current OID value
3064 and structure applied to defined field.
3065 :ref:`Read about DEFINED BY <definedby>`
3066 :param bytes impl: override default tag with ``IMPLICIT`` one
3067 :param bytes expl: override default tag with ``EXPLICIT`` one
3068 :param default: set default value. Type same as in ``value``
3069 :param bool optional: is object ``OPTIONAL`` in sequence
3071 super(ObjectIdentifier, self).__init__(
3079 if value is not None:
3080 self._value = self._value_sanitize(value)
3081 if default is not None:
3082 default = self._value_sanitize(default)
3083 self.default = self.__class__(
3088 if self._value is None:
3089 self._value = default
3090 self.defines = defines
3092 def __add__(self, their):
3093 if isinstance(their, self.__class__):
3094 return self.__class__(self._value + their._value)
3095 if isinstance(their, tuple):
3096 return self.__class__(self._value + their)
3097 raise InvalidValueType((self.__class__, tuple))
3099 def _value_sanitize(self, value):
3100 if issubclass(value.__class__, ObjectIdentifier):
3102 if isinstance(value, string_types):
3104 value = tuple(int(arc) for arc in value.split("."))
3106 raise InvalidOID("unacceptable arcs values")
3107 if isinstance(value, tuple):
3109 raise InvalidOID("less than 2 arcs")
3110 first_arc = value[0]
3111 if first_arc in (0, 1):
3112 if not (0 <= value[1] <= 39):
3113 raise InvalidOID("second arc is too wide")
3114 elif first_arc == 2:
3117 raise InvalidOID("unacceptable first arc value")
3119 raise InvalidValueType((self.__class__, str, tuple))
3123 return self._value is not None
3126 obj = self.__class__()
3127 obj._value = self._value
3128 obj.defines = self.defines
3130 obj._expl = self._expl
3131 obj.default = self.default
3132 obj.optional = self.optional
3133 obj.offset = self.offset
3134 obj.llen = self.llen
3135 obj.vlen = self.vlen
3136 obj.expl_lenindef = self.expl_lenindef
3137 obj.lenindef = self.lenindef
3138 obj.ber_encoded = self.ber_encoded
3142 self._assert_ready()
3143 return iter(self._value)
3146 return ".".join(str(arc) for arc in self._value or ())
3149 self._assert_ready()
3152 bytes(self._expl or b"") +
3153 str(self._value).encode("ascii"),
3156 def __eq__(self, their):
3157 if isinstance(their, tuple):
3158 return self._value == their
3159 if not issubclass(their.__class__, ObjectIdentifier):
3162 self.tag == their.tag and
3163 self._expl == their._expl and
3164 self._value == their._value
3167 def __lt__(self, their):
3168 return self._value < their._value
3179 return self.__class__(
3181 defines=self.defines if defines is None else defines,
3182 impl=self.tag if impl is None else impl,
3183 expl=self._expl if expl is None else expl,
3184 default=self.default if default is None else default,
3185 optional=self.optional if optional is None else optional,
3189 self._assert_ready()
3191 first_value = value[1]
3192 first_arc = value[0]
3195 elif first_arc == 1:
3197 elif first_arc == 2:
3199 else: # pragma: no cover
3200 raise RuntimeError("invalid arc is stored")
3201 octets = [zero_ended_encode(first_value)]
3202 for arc in value[2:]:
3203 octets.append(zero_ended_encode(arc))
3204 v = b"".join(octets)
3205 return b"".join((self.tag, len_encode(len(v)), v))
3207 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3209 t, _, lv = tag_strip(tlv)
3210 except DecodeError as err:
3211 raise err.__class__(
3213 klass=self.__class__,
3214 decode_path=decode_path,
3219 klass=self.__class__,
3220 decode_path=decode_path,
3223 if tag_only: # pragma: no cover
3226 l, llen, v = len_decode(lv)
3227 except DecodeError as err:
3228 raise err.__class__(
3230 klass=self.__class__,
3231 decode_path=decode_path,
3235 raise NotEnoughData(
3236 "encoded length is longer than data",
3237 klass=self.__class__,
3238 decode_path=decode_path,
3242 raise NotEnoughData(
3244 klass=self.__class__,
3245 decode_path=decode_path,
3248 v, tail = v[:l], v[l:]
3255 octet = indexbytes(v, i)
3256 if i == 0 and octet == 0x80:
3257 if ctx.get("bered", False):
3260 raise DecodeError("non normalized arc encoding")
3261 arc = (arc << 7) | (octet & 0x7F)
3262 if octet & 0x80 == 0:
3270 klass=self.__class__,
3271 decode_path=decode_path,
3275 second_arc = arcs[0]
3276 if 0 <= second_arc <= 39:
3278 elif 40 <= second_arc <= 79:
3284 obj = self.__class__(
3285 value=tuple([first_arc, second_arc] + arcs[1:]),
3288 default=self.default,
3289 optional=self.optional,
3290 _decoded=(offset, llen, l),
3293 obj.ber_encoded = True
3297 return pp_console_row(next(self.pps()))
3299 def pps(self, decode_path=()):
3302 asn1_type_name=self.asn1_type_name,
3303 obj_name=self.__class__.__name__,
3304 decode_path=decode_path,
3305 value=str(self) if self.ready else None,
3306 optional=self.optional,
3307 default=self == self.default,
3308 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3309 expl=None if self._expl is None else tag_decode(self._expl),
3314 expl_offset=self.expl_offset if self.expled else None,
3315 expl_tlen=self.expl_tlen if self.expled else None,
3316 expl_llen=self.expl_llen if self.expled else None,
3317 expl_vlen=self.expl_vlen if self.expled else None,
3318 expl_lenindef=self.expl_lenindef,
3319 ber_encoded=self.ber_encoded,
3322 for pp in self.pps_lenindef(decode_path):
3326 class Enumerated(Integer):
3327 """``ENUMERATED`` integer type
3329 This type is identical to :py:class:`pyderasn.Integer`, but requires
3330 schema to be specified and does not accept values missing from it.
3333 tag_default = tag_encode(10)
3334 asn1_type_name = "ENUMERATED"
3345 bounds=None, # dummy argument, workability for Integer.decode
3347 super(Enumerated, self).__init__(
3356 if len(self.specs) == 0:
3357 raise ValueError("schema must be specified")
3359 def _value_sanitize(self, value):
3360 if isinstance(value, self.__class__):
3361 value = value._value
3362 elif isinstance(value, integer_types):
3363 for _value in itervalues(self.specs):
3368 "unknown integer value: %s" % value,
3369 klass=self.__class__,
3371 elif isinstance(value, string_types):
3372 value = self.specs.get(value)
3374 raise ObjUnknown("integer value: %s" % value)
3376 raise InvalidValueType((self.__class__, int, str))
3380 obj = self.__class__(_specs=self.specs)
3381 obj._value = self._value
3382 obj._bound_min = self._bound_min
3383 obj._bound_max = self._bound_max
3385 obj._expl = self._expl
3386 obj.default = self.default
3387 obj.optional = self.optional
3388 obj.offset = self.offset
3389 obj.llen = self.llen
3390 obj.vlen = self.vlen
3391 obj.expl_lenindef = self.expl_lenindef
3392 obj.lenindef = self.lenindef
3393 obj.ber_encoded = self.ber_encoded
3405 return self.__class__(
3407 impl=self.tag if impl is None else impl,
3408 expl=self._expl if expl is None else expl,
3409 default=self.default if default is None else default,
3410 optional=self.optional if optional is None else optional,
3415 class CommonString(OctetString):
3416 """Common class for all strings
3418 Everything resembles :py:class:`pyderasn.OctetString`, except
3419 ability to deal with unicode text strings.
3421 >>> hexenc("привет мир".encode("utf-8"))
3422 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3423 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3425 >>> s = UTF8String("привет мир")
3426 UTF8String UTF8String привет мир
3428 'привет мир'
3429 >>> hexenc(bytes(s))
3430 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3432 >>> PrintableString("привет мир")
3433 Traceback (most recent call last):
3434 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3436 >>> BMPString("ада", bounds=(2, 2))
3437 Traceback (most recent call last):
3438 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3439 >>> s = BMPString("ад", bounds=(2, 2))
3442 >>> hexenc(bytes(s))
3450 * - :py:class:`pyderasn.UTF8String`
3452 * - :py:class:`pyderasn.NumericString`
3454 * - :py:class:`pyderasn.PrintableString`
3456 * - :py:class:`pyderasn.TeletexString`
3458 * - :py:class:`pyderasn.T61String`
3460 * - :py:class:`pyderasn.VideotexString`
3462 * - :py:class:`pyderasn.IA5String`
3464 * - :py:class:`pyderasn.GraphicString`
3466 * - :py:class:`pyderasn.VisibleString`
3468 * - :py:class:`pyderasn.ISO646String`
3470 * - :py:class:`pyderasn.GeneralString`
3472 * - :py:class:`pyderasn.UniversalString`
3474 * - :py:class:`pyderasn.BMPString`
3477 __slots__ = ("encoding",)
3479 def _value_sanitize(self, value):
3481 value_decoded = None
3482 if isinstance(value, self.__class__):
3483 value_raw = value._value
3484 elif isinstance(value, text_type):
3485 value_decoded = value
3486 elif isinstance(value, binary_type):
3489 raise InvalidValueType((self.__class__, text_type, binary_type))
3492 value_decoded.encode(self.encoding)
3493 if value_raw is None else value_raw
3496 value_raw.decode(self.encoding)
3497 if value_decoded is None else value_decoded
3499 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3500 raise DecodeError(str(err))
3501 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3509 def __eq__(self, their):
3510 if isinstance(their, binary_type):
3511 return self._value == their
3512 if isinstance(their, text_type):
3513 return self._value == their.encode(self.encoding)
3514 if not isinstance(their, self.__class__):
3517 self._value == their._value and
3518 self.tag == their.tag and
3519 self._expl == their._expl
3522 def __unicode__(self):
3524 return self._value.decode(self.encoding)
3525 return text_type(self._value)
3528 return pp_console_row(next(self.pps(no_unicode=PY2)))
3530 def pps(self, decode_path=(), no_unicode=False):
3533 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3536 asn1_type_name=self.asn1_type_name,
3537 obj_name=self.__class__.__name__,
3538 decode_path=decode_path,
3540 optional=self.optional,
3541 default=self == self.default,
3542 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3543 expl=None if self._expl is None else tag_decode(self._expl),
3548 expl_offset=self.expl_offset if self.expled else None,
3549 expl_tlen=self.expl_tlen if self.expled else None,
3550 expl_llen=self.expl_llen if self.expled else None,
3551 expl_vlen=self.expl_vlen if self.expled else None,
3552 expl_lenindef=self.expl_lenindef,
3553 ber_encoded=self.ber_encoded,
3556 for pp in self.pps_lenindef(decode_path):
3560 class UTF8String(CommonString):
3562 tag_default = tag_encode(12)
3564 asn1_type_name = "UTF8String"
3567 class AllowableCharsMixin(object):
3569 def allowable_chars(self):
3571 return self._allowable_chars
3572 return frozenset(six_unichr(c) for c in self._allowable_chars)
3575 class NumericString(AllowableCharsMixin, CommonString):
3578 Its value is properly sanitized: only ASCII digits with spaces can
3581 >>> NumericString().allowable_chars
3582 set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3585 tag_default = tag_encode(18)
3587 asn1_type_name = "NumericString"
3588 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3590 def _value_sanitize(self, value):
3591 value = super(NumericString, self)._value_sanitize(value)
3592 if not frozenset(value) <= self._allowable_chars:
3593 raise DecodeError("non-numeric value")
3597 class PrintableString(AllowableCharsMixin, CommonString):
3600 Its value is properly sanitized: see X.680 41.4 table 10.
3602 >>> PrintableString().allowable_chars
3603 >>> set([' ', "'", ..., 'z'])
3606 tag_default = tag_encode(19)
3608 asn1_type_name = "PrintableString"
3609 _allowable_chars = frozenset(
3610 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3613 def _value_sanitize(self, value):
3614 value = super(PrintableString, self)._value_sanitize(value)
3615 if not frozenset(value) <= self._allowable_chars:
3616 raise DecodeError("non-printable value")
3620 class TeletexString(CommonString):
3622 tag_default = tag_encode(20)
3624 asn1_type_name = "TeletexString"
3627 class T61String(TeletexString):
3629 asn1_type_name = "T61String"
3632 class VideotexString(CommonString):
3634 tag_default = tag_encode(21)
3635 encoding = "iso-8859-1"
3636 asn1_type_name = "VideotexString"
3639 class IA5String(CommonString):
3641 tag_default = tag_encode(22)
3643 asn1_type_name = "IA5"
3646 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3647 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3648 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3651 class UTCTime(CommonString):
3652 """``UTCTime`` datetime type
3654 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3655 UTCTime UTCTime 2017-09-30T22:07:50
3661 datetime.datetime(2017, 9, 30, 22, 7, 50)
3662 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3663 datetime.datetime(1957, 9, 30, 22, 7, 50)
3666 tag_default = tag_encode(23)
3668 asn1_type_name = "UTCTime"
3670 fmt = "%y%m%d%H%M%SZ"
3680 bounds=None, # dummy argument, workability for OctetString.decode
3683 :param value: set the value. Either datetime type, or
3684 :py:class:`pyderasn.UTCTime` object
3685 :param bytes impl: override default tag with ``IMPLICIT`` one
3686 :param bytes expl: override default tag with ``EXPLICIT`` one
3687 :param default: set default value. Type same as in ``value``
3688 :param bool optional: is object ``OPTIONAL`` in sequence
3690 super(UTCTime, self).__init__(
3698 if value is not None:
3699 self._value = self._value_sanitize(value)
3700 if default is not None:
3701 default = self._value_sanitize(default)
3702 self.default = self.__class__(
3707 if self._value is None:
3708 self._value = default
3710 def _value_sanitize(self, value):
3711 if isinstance(value, self.__class__):
3713 if isinstance(value, datetime):
3714 return value.strftime(self.fmt).encode("ascii")
3715 if isinstance(value, binary_type):
3717 value_decoded = value.decode("ascii")
3718 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3719 raise DecodeError("invalid UTCTime encoding")
3720 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3722 datetime.strptime(value_decoded, self.fmt)
3723 except (TypeError, ValueError):
3724 raise DecodeError("invalid UTCTime format")
3727 raise DecodeError("invalid UTCTime length")
3728 raise InvalidValueType((self.__class__, datetime))
3730 def __eq__(self, their):
3731 if isinstance(their, binary_type):
3732 return self._value == their
3733 if isinstance(their, datetime):
3734 return self.todatetime() == their
3735 if not isinstance(their, self.__class__):
3738 self._value == their._value and
3739 self.tag == their.tag and
3740 self._expl == their._expl
3743 def todatetime(self):
3744 """Convert to datetime
3748 Pay attention that UTCTime can not hold full year, so all years
3749 having < 50 years are treated as 20xx, 19xx otherwise, according
3750 to X.509 recomendation.
3752 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3753 year = value.year % 100
3755 year=(2000 + year) if year < 50 else (1900 + year),
3759 minute=value.minute,
3760 second=value.second,
3764 return pp_console_row(next(self.pps()))
3766 def pps(self, decode_path=()):
3769 asn1_type_name=self.asn1_type_name,
3770 obj_name=self.__class__.__name__,
3771 decode_path=decode_path,
3772 value=self.todatetime().isoformat() if self.ready else None,
3773 optional=self.optional,
3774 default=self == self.default,
3775 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3776 expl=None if self._expl is None else tag_decode(self._expl),
3781 expl_offset=self.expl_offset if self.expled else None,
3782 expl_tlen=self.expl_tlen if self.expled else None,
3783 expl_llen=self.expl_llen if self.expled else None,
3784 expl_vlen=self.expl_vlen if self.expled else None,
3785 expl_lenindef=self.expl_lenindef,
3786 ber_encoded=self.ber_encoded,
3789 for pp in self.pps_lenindef(decode_path):
3793 class GeneralizedTime(UTCTime):
3794 """``GeneralizedTime`` datetime type
3796 This type is similar to :py:class:`pyderasn.UTCTime`.
3798 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3799 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3801 '20170930220750.000123Z'
3802 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3803 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3806 tag_default = tag_encode(24)
3807 asn1_type_name = "GeneralizedTime"
3809 fmt = "%Y%m%d%H%M%SZ"
3810 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3812 def _value_sanitize(self, value):
3813 if isinstance(value, self.__class__):
3815 if isinstance(value, datetime):
3816 return value.strftime(
3817 self.fmt_ms if value.microsecond > 0 else self.fmt
3819 if isinstance(value, binary_type):
3821 value_decoded = value.decode("ascii")
3822 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3823 raise DecodeError("invalid GeneralizedTime encoding")
3824 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3826 datetime.strptime(value_decoded, self.fmt)
3827 except (TypeError, ValueError):
3829 "invalid GeneralizedTime (without ms) format",
3832 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3834 datetime.strptime(value_decoded, self.fmt_ms)
3835 except (TypeError, ValueError):
3837 "invalid GeneralizedTime (with ms) format",
3842 "invalid GeneralizedTime length",
3843 klass=self.__class__,
3845 raise InvalidValueType((self.__class__, datetime))
3847 def todatetime(self):
3848 value = self._value.decode("ascii")
3849 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3850 return datetime.strptime(value, self.fmt)
3851 return datetime.strptime(value, self.fmt_ms)
3854 class GraphicString(CommonString):
3856 tag_default = tag_encode(25)
3857 encoding = "iso-8859-1"
3858 asn1_type_name = "GraphicString"
3861 class VisibleString(CommonString):
3863 tag_default = tag_encode(26)
3865 asn1_type_name = "VisibleString"
3868 class ISO646String(VisibleString):
3870 asn1_type_name = "ISO646String"
3873 class GeneralString(CommonString):
3875 tag_default = tag_encode(27)
3876 encoding = "iso-8859-1"
3877 asn1_type_name = "GeneralString"
3880 class UniversalString(CommonString):
3882 tag_default = tag_encode(28)
3883 encoding = "utf-32-be"
3884 asn1_type_name = "UniversalString"
3887 class BMPString(CommonString):
3889 tag_default = tag_encode(30)
3890 encoding = "utf-16-be"
3891 asn1_type_name = "BMPString"
3895 """``CHOICE`` special type
3899 class GeneralName(Choice):
3901 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3902 ("dNSName", IA5String(impl=tag_ctxp(2))),
3905 >>> gn = GeneralName()
3907 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3908 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3909 >>> gn["dNSName"] = IA5String("bar.baz")
3910 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3911 >>> gn["rfc822Name"]
3914 [2] IA5String IA5 bar.baz
3917 >>> gn.value == gn["dNSName"]
3920 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3922 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3923 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3925 __slots__ = ("specs",)
3927 asn1_type_name = "CHOICE"
3940 :param value: set the value. Either ``(choice, value)`` tuple, or
3941 :py:class:`pyderasn.Choice` object
3942 :param bytes impl: can not be set, do **not** use it
3943 :param bytes expl: override default tag with ``EXPLICIT`` one
3944 :param default: set default value. Type same as in ``value``
3945 :param bool optional: is object ``OPTIONAL`` in sequence
3947 if impl is not None:
3948 raise ValueError("no implicit tag allowed for CHOICE")
3949 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3951 schema = getattr(self, "schema", ())
3952 if len(schema) == 0:
3953 raise ValueError("schema must be specified")
3955 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3958 if value is not None:
3959 self._value = self._value_sanitize(value)
3960 if default is not None:
3961 default_value = self._value_sanitize(default)
3962 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3963 default_obj.specs = self.specs
3964 default_obj._value = default_value
3965 self.default = default_obj
3967 self._value = default_obj.copy()._value
3969 def _value_sanitize(self, value):
3970 if isinstance(value, self.__class__):
3972 if isinstance(value, tuple) and len(value) == 2:
3974 spec = self.specs.get(choice)
3976 raise ObjUnknown(choice)
3977 if not isinstance(obj, spec.__class__):
3978 raise InvalidValueType((spec,))
3979 return (choice, spec(obj))
3980 raise InvalidValueType((self.__class__, tuple))
3984 return self._value is not None and self._value[1].ready
3988 return self.expl_lenindef or (
3989 (self._value is not None) and
3990 self._value[1].bered
3994 obj = self.__class__(schema=self.specs)
3995 obj._expl = self._expl
3996 obj.default = self.default
3997 obj.optional = self.optional
3998 obj.offset = self.offset
3999 obj.llen = self.llen
4000 obj.vlen = self.vlen
4001 obj.expl_lenindef = self.expl_lenindef
4002 obj.lenindef = self.lenindef
4003 obj.ber_encoded = self.ber_encoded
4005 if value is not None:
4006 obj._value = (value[0], value[1].copy())
4009 def __eq__(self, their):
4010 if isinstance(their, tuple) and len(their) == 2:
4011 return self._value == their
4012 if not isinstance(their, self.__class__):
4015 self.specs == their.specs and
4016 self._value == their._value
4026 return self.__class__(
4029 expl=self._expl if expl is None else expl,
4030 default=self.default if default is None else default,
4031 optional=self.optional if optional is None else optional,
4036 self._assert_ready()
4037 return self._value[0]
4041 self._assert_ready()
4042 return self._value[1]
4044 def __getitem__(self, key):
4045 if key not in self.specs:
4046 raise ObjUnknown(key)
4047 if self._value is None:
4049 choice, value = self._value
4054 def __setitem__(self, key, value):
4055 spec = self.specs.get(key)
4057 raise ObjUnknown(key)
4058 if not isinstance(value, spec.__class__):
4059 raise InvalidValueType((spec.__class__,))
4060 self._value = (key, spec(value))
4068 return self._value[1].decoded if self.ready else False
4071 self._assert_ready()
4072 return self._value[1].encode()
4074 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4075 for choice, spec in iteritems(self.specs):
4076 sub_decode_path = decode_path + (choice,)
4082 decode_path=sub_decode_path,
4085 _ctx_immutable=False,
4092 klass=self.__class__,
4093 decode_path=decode_path,
4096 if tag_only: # pragma: no cover
4098 value, tail = spec.decode(
4102 decode_path=sub_decode_path,
4104 _ctx_immutable=False,
4106 obj = self.__class__(
4109 default=self.default,
4110 optional=self.optional,
4111 _decoded=(offset, 0, value.fulllen),
4113 obj._value = (choice, value)
4117 value = pp_console_row(next(self.pps()))
4119 value = "%s[%r]" % (value, self.value)
4122 def pps(self, decode_path=()):
4125 asn1_type_name=self.asn1_type_name,
4126 obj_name=self.__class__.__name__,
4127 decode_path=decode_path,
4128 value=self.choice if self.ready else None,
4129 optional=self.optional,
4130 default=self == self.default,
4131 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4132 expl=None if self._expl is None else tag_decode(self._expl),
4137 expl_lenindef=self.expl_lenindef,
4141 yield self.value.pps(decode_path=decode_path + (self.choice,))
4142 for pp in self.pps_lenindef(decode_path):
4146 class PrimitiveTypes(Choice):
4147 """Predefined ``CHOICE`` for all generic primitive types
4149 It could be useful for general decoding of some unspecified values:
4151 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4152 OCTET STRING 3 bytes 666f6f
4153 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4157 schema = tuple((klass.__name__, klass()) for klass in (
4182 """``ANY`` special type
4184 >>> Any(Integer(-123))
4186 >>> a = Any(OctetString(b"hello world").encode())
4187 ANY 040b68656c6c6f20776f726c64
4188 >>> hexenc(bytes(a))
4189 b'0x040x0bhello world'
4191 __slots__ = ("defined",)
4192 tag_default = tag_encode(0)
4193 asn1_type_name = "ANY"
4203 :param value: set the value. Either any kind of pyderasn's
4204 **ready** object, or bytes. Pay attention that
4205 **no** validation is performed is raw binary value
4207 :param bytes expl: override default tag with ``EXPLICIT`` one
4208 :param bool optional: is object ``OPTIONAL`` in sequence
4210 super(Any, self).__init__(None, expl, None, optional, _decoded)
4211 self._value = None if value is None else self._value_sanitize(value)
4214 def _value_sanitize(self, value):
4215 if isinstance(value, self.__class__):
4217 if isinstance(value, Obj):
4218 return value.encode()
4219 if isinstance(value, binary_type):
4221 raise InvalidValueType((self.__class__, Obj, binary_type))
4225 return self._value is not None
4229 if self.expl_lenindef or self.lenindef:
4231 if self.defined is None:
4233 return self.defined[1].bered
4236 obj = self.__class__()
4237 obj._value = self._value
4239 obj._expl = self._expl
4240 obj.optional = self.optional
4241 obj.offset = self.offset
4242 obj.llen = self.llen
4243 obj.vlen = self.vlen
4244 obj.expl_lenindef = self.expl_lenindef
4245 obj.lenindef = self.lenindef
4246 obj.ber_encoded = self.ber_encoded
4249 def __eq__(self, their):
4250 if isinstance(their, binary_type):
4251 return self._value == their
4252 if issubclass(their.__class__, Any):
4253 return self._value == their._value
4262 return self.__class__(
4264 expl=self._expl if expl is None else expl,
4265 optional=self.optional if optional is None else optional,
4268 def __bytes__(self):
4269 self._assert_ready()
4277 self._assert_ready()
4280 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4282 t, tlen, lv = tag_strip(tlv)
4283 except DecodeError as err:
4284 raise err.__class__(
4286 klass=self.__class__,
4287 decode_path=decode_path,
4291 l, llen, v = len_decode(lv)
4292 except LenIndefForm as err:
4293 if not ctx.get("bered", False):
4294 raise err.__class__(
4296 klass=self.__class__,
4297 decode_path=decode_path,
4300 llen, vlen, v = 1, 0, lv[1:]
4301 sub_offset = offset + tlen + llen
4303 while v[:EOC_LEN].tobytes() != EOC:
4304 chunk, v = Any().decode(
4307 decode_path=decode_path + (str(chunk_i),),
4310 _ctx_immutable=False,
4312 vlen += chunk.tlvlen
4313 sub_offset += chunk.tlvlen
4315 tlvlen = tlen + llen + vlen + EOC_LEN
4316 obj = self.__class__(
4317 value=tlv[:tlvlen].tobytes(),
4319 optional=self.optional,
4320 _decoded=(offset, 0, tlvlen),
4324 return obj, v[EOC_LEN:]
4325 except DecodeError as err:
4326 raise err.__class__(
4328 klass=self.__class__,
4329 decode_path=decode_path,
4333 raise NotEnoughData(
4334 "encoded length is longer than data",
4335 klass=self.__class__,
4336 decode_path=decode_path,
4339 tlvlen = tlen + llen + l
4340 v, tail = tlv[:tlvlen], v[l:]
4341 obj = self.__class__(
4344 optional=self.optional,
4345 _decoded=(offset, 0, tlvlen),
4351 return pp_console_row(next(self.pps()))
4353 def pps(self, decode_path=()):
4356 asn1_type_name=self.asn1_type_name,
4357 obj_name=self.__class__.__name__,
4358 decode_path=decode_path,
4359 blob=self._value if self.ready else None,
4360 optional=self.optional,
4361 default=self == self.default,
4362 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4363 expl=None if self._expl is None else tag_decode(self._expl),
4368 expl_offset=self.expl_offset if self.expled else None,
4369 expl_tlen=self.expl_tlen if self.expled else None,
4370 expl_llen=self.expl_llen if self.expled else None,
4371 expl_vlen=self.expl_vlen if self.expled else None,
4372 expl_lenindef=self.expl_lenindef,
4373 lenindef=self.lenindef,
4376 defined_by, defined = self.defined or (None, None)
4377 if defined_by is not None:
4379 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4381 for pp in self.pps_lenindef(decode_path):
4385 ########################################################################
4386 # ASN.1 constructed types
4387 ########################################################################
4389 def get_def_by_path(defines_by_path, sub_decode_path):
4390 """Get define by decode path
4392 for path, define in defines_by_path:
4393 if len(path) != len(sub_decode_path):
4395 for p1, p2 in zip(path, sub_decode_path):
4396 if (p1 != any) and (p1 != p2):
4402 def abs_decode_path(decode_path, rel_path):
4403 """Create an absolute decode path from current and relative ones
4405 :param decode_path: current decode path, starting point. Tuple of strings
4406 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4407 If first tuple's element is "/", then treat it as
4408 an absolute path, ignoring ``decode_path`` as
4409 starting point. Also this tuple can contain ".."
4410 elements, stripping the leading element from
4413 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4414 ("foo", "bar", "baz", "whatever")
4415 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4417 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4420 if rel_path[0] == "/":
4422 if rel_path[0] == "..":
4423 return abs_decode_path(decode_path[:-1], rel_path[1:])
4424 return decode_path + rel_path
4427 class Sequence(Obj):
4428 """``SEQUENCE`` structure type
4430 You have to make specification of sequence::
4432 class Extension(Sequence):
4434 ("extnID", ObjectIdentifier()),
4435 ("critical", Boolean(default=False)),
4436 ("extnValue", OctetString()),
4439 Then, you can work with it as with dictionary.
4441 >>> ext = Extension()
4442 >>> Extension().specs
4444 ('extnID', OBJECT IDENTIFIER),
4445 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4446 ('extnValue', OCTET STRING),
4448 >>> ext["extnID"] = "1.2.3"
4449 Traceback (most recent call last):
4450 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4451 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4453 You can determine if sequence is ready to be encoded:
4458 Traceback (most recent call last):
4459 pyderasn.ObjNotReady: object is not ready: extnValue
4460 >>> ext["extnValue"] = OctetString(b"foobar")
4464 Value you want to assign, must have the same **type** as in
4465 corresponding specification, but it can have different tags,
4466 optional/default attributes -- they will be taken from specification
4469 class TBSCertificate(Sequence):
4471 ("version", Version(expl=tag_ctxc(0), default="v1")),
4474 >>> tbs = TBSCertificate()
4475 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4477 Assign ``None`` to remove value from sequence.
4479 You can set values in Sequence during its initialization:
4481 >>> AlgorithmIdentifier((
4482 ("algorithm", ObjectIdentifier("1.2.3")),
4483 ("parameters", Any(Null()))
4485 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4487 You can determine if value exists/set in the sequence and take its value:
4489 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4492 OBJECT IDENTIFIER 1.2.3
4494 But pay attention that if value has default, then it won't be (not
4495 in) in the sequence (because ``DEFAULT`` must not be encoded in
4496 DER), but you can read its value:
4498 >>> "critical" in ext, ext["critical"]
4499 (False, BOOLEAN False)
4500 >>> ext["critical"] = Boolean(True)
4501 >>> "critical" in ext, ext["critical"]
4502 (True, BOOLEAN True)
4504 All defaulted values are always optional.
4506 .. _allow_default_values_ctx:
4508 DER prohibits default value encoding and will raise an error if
4509 default value is unexpectedly met during decode.
4510 If :ref:`bered <bered_ctx>` context option is set, then no error
4511 will be raised, but ``bered`` attribute set. You can disable strict
4512 defaulted values existence validation by setting
4513 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4515 Two sequences are equal if they have equal specification (schema),
4516 implicit/explicit tagging and the same values.
4518 __slots__ = ("specs",)
4519 tag_default = tag_encode(form=TagFormConstructed, num=16)
4520 asn1_type_name = "SEQUENCE"
4532 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4534 schema = getattr(self, "schema", ())
4536 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4539 if value is not None:
4540 if issubclass(value.__class__, Sequence):
4541 self._value = value._value
4542 elif hasattr(value, "__iter__"):
4543 for seq_key, seq_value in value:
4544 self[seq_key] = seq_value
4546 raise InvalidValueType((Sequence,))
4547 if default is not None:
4548 if not issubclass(default.__class__, Sequence):
4549 raise InvalidValueType((Sequence,))
4550 default_value = default._value
4551 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4552 default_obj.specs = self.specs
4553 default_obj._value = default_value
4554 self.default = default_obj
4556 self._value = default_obj.copy()._value
4560 for name, spec in iteritems(self.specs):
4561 value = self._value.get(name)
4573 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4575 return any(value.bered for value in itervalues(self._value))
4578 obj = self.__class__(schema=self.specs)
4580 obj._expl = self._expl
4581 obj.default = self.default
4582 obj.optional = self.optional
4583 obj.offset = self.offset
4584 obj.llen = self.llen
4585 obj.vlen = self.vlen
4586 obj.expl_lenindef = self.expl_lenindef
4587 obj.lenindef = self.lenindef
4588 obj.ber_encoded = self.ber_encoded
4589 obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4592 def __eq__(self, their):
4593 if not isinstance(their, self.__class__):
4596 self.specs == their.specs and
4597 self.tag == their.tag and
4598 self._expl == their._expl and
4599 self._value == their._value
4610 return self.__class__(
4613 impl=self.tag if impl is None else impl,
4614 expl=self._expl if expl is None else expl,
4615 default=self.default if default is None else default,
4616 optional=self.optional if optional is None else optional,
4619 def __contains__(self, key):
4620 return key in self._value
4622 def __setitem__(self, key, value):
4623 spec = self.specs.get(key)
4625 raise ObjUnknown(key)
4627 self._value.pop(key, None)
4629 if not isinstance(value, spec.__class__):
4630 raise InvalidValueType((spec.__class__,))
4631 value = spec(value=value)
4632 if spec.default is not None and value == spec.default:
4633 self._value.pop(key, None)
4635 self._value[key] = value
4637 def __getitem__(self, key):
4638 value = self._value.get(key)
4639 if value is not None:
4641 spec = self.specs.get(key)
4643 raise ObjUnknown(key)
4644 if spec.default is not None:
4648 def _encoded_values(self):
4650 for name, spec in iteritems(self.specs):
4651 value = self._value.get(name)
4655 raise ObjNotReady(name)
4656 raws.append(value.encode())
4660 v = b"".join(self._encoded_values())
4661 return b"".join((self.tag, len_encode(len(v)), v))
4663 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4665 t, tlen, lv = tag_strip(tlv)
4666 except DecodeError as err:
4667 raise err.__class__(
4669 klass=self.__class__,
4670 decode_path=decode_path,
4675 klass=self.__class__,
4676 decode_path=decode_path,
4679 if tag_only: # pragma: no cover
4682 ctx_bered = ctx.get("bered", False)
4684 l, llen, v = len_decode(lv)
4685 except LenIndefForm as err:
4687 raise err.__class__(
4689 klass=self.__class__,
4690 decode_path=decode_path,
4693 l, llen, v = 0, 1, lv[1:]
4695 except DecodeError as err:
4696 raise err.__class__(
4698 klass=self.__class__,
4699 decode_path=decode_path,
4703 raise NotEnoughData(
4704 "encoded length is longer than data",
4705 klass=self.__class__,
4706 decode_path=decode_path,
4710 v, tail = v[:l], v[l:]
4712 sub_offset = offset + tlen + llen
4715 ctx_allow_default_values = ctx.get("allow_default_values", False)
4716 for name, spec in iteritems(self.specs):
4717 if spec.optional and (
4718 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4722 sub_decode_path = decode_path + (name,)
4724 value, v_tail = spec.decode(
4728 decode_path=sub_decode_path,
4730 _ctx_immutable=False,
4737 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4738 if defined is not None:
4739 defined_by, defined_spec = defined
4740 if issubclass(value.__class__, SequenceOf):
4741 for i, _value in enumerate(value):
4742 sub_sub_decode_path = sub_decode_path + (
4744 DecodePathDefBy(defined_by),
4746 defined_value, defined_tail = defined_spec.decode(
4747 memoryview(bytes(_value)),
4749 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4750 if value.expled else (value.tlen + value.llen)
4753 decode_path=sub_sub_decode_path,
4755 _ctx_immutable=False,
4757 if len(defined_tail) > 0:
4760 klass=self.__class__,
4761 decode_path=sub_sub_decode_path,
4764 _value.defined = (defined_by, defined_value)
4766 defined_value, defined_tail = defined_spec.decode(
4767 memoryview(bytes(value)),
4769 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4770 if value.expled else (value.tlen + value.llen)
4773 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4775 _ctx_immutable=False,
4777 if len(defined_tail) > 0:
4780 klass=self.__class__,
4781 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4784 value.defined = (defined_by, defined_value)
4786 value_len = value.fulllen
4788 sub_offset += value_len
4790 if spec.default is not None and value == spec.default:
4791 if ctx_bered or ctx_allow_default_values:
4795 "DEFAULT value met",
4796 klass=self.__class__,
4797 decode_path=sub_decode_path,
4800 values[name] = value
4802 spec_defines = getattr(spec, "defines", ())
4803 if len(spec_defines) == 0:
4804 defines_by_path = ctx.get("defines_by_path", ())
4805 if len(defines_by_path) > 0:
4806 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4807 if spec_defines is not None and len(spec_defines) > 0:
4808 for rel_path, schema in spec_defines:
4809 defined = schema.get(value, None)
4810 if defined is not None:
4811 ctx.setdefault("_defines", []).append((
4812 abs_decode_path(sub_decode_path[:-1], rel_path),
4816 if v[:EOC_LEN].tobytes() != EOC:
4819 klass=self.__class__,
4820 decode_path=decode_path,
4828 klass=self.__class__,
4829 decode_path=decode_path,
4832 obj = self.__class__(
4836 default=self.default,
4837 optional=self.optional,
4838 _decoded=(offset, llen, vlen),
4841 obj.lenindef = lenindef
4842 obj.ber_encoded = ber_encoded
4846 value = pp_console_row(next(self.pps()))
4848 for name in self.specs:
4849 _value = self._value.get(name)
4852 cols.append("%s: %s" % (name, repr(_value)))
4853 return "%s[%s]" % (value, "; ".join(cols))
4855 def pps(self, decode_path=()):
4858 asn1_type_name=self.asn1_type_name,
4859 obj_name=self.__class__.__name__,
4860 decode_path=decode_path,
4861 optional=self.optional,
4862 default=self == self.default,
4863 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4864 expl=None if self._expl is None else tag_decode(self._expl),
4869 expl_offset=self.expl_offset if self.expled else None,
4870 expl_tlen=self.expl_tlen if self.expled else None,
4871 expl_llen=self.expl_llen if self.expled else None,
4872 expl_vlen=self.expl_vlen if self.expled else None,
4873 expl_lenindef=self.expl_lenindef,
4874 lenindef=self.lenindef,
4875 ber_encoded=self.ber_encoded,
4878 for name in self.specs:
4879 value = self._value.get(name)
4882 yield value.pps(decode_path=decode_path + (name,))
4883 for pp in self.pps_lenindef(decode_path):
4887 class Set(Sequence):
4888 """``SET`` structure type
4890 Its usage is identical to :py:class:`pyderasn.Sequence`.
4892 .. _allow_unordered_set_ctx:
4894 DER prohibits unordered values encoding and will raise an error
4895 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4896 then no error will occure. Also you can disable strict values
4897 ordering check by setting ``"allow_unordered_set": True``
4898 :ref:`context <ctx>` option.
4901 tag_default = tag_encode(form=TagFormConstructed, num=17)
4902 asn1_type_name = "SET"
4905 raws = self._encoded_values()
4908 return b"".join((self.tag, len_encode(len(v)), v))
4910 def _specs_items(self):
4911 return iteritems(self.specs)
4913 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4915 t, tlen, lv = tag_strip(tlv)
4916 except DecodeError as err:
4917 raise err.__class__(
4919 klass=self.__class__,
4920 decode_path=decode_path,
4925 klass=self.__class__,
4926 decode_path=decode_path,
4932 ctx_bered = ctx.get("bered", False)
4934 l, llen, v = len_decode(lv)
4935 except LenIndefForm as err:
4937 raise err.__class__(
4939 klass=self.__class__,
4940 decode_path=decode_path,
4943 l, llen, v = 0, 1, lv[1:]
4945 except DecodeError as err:
4946 raise err.__class__(
4948 klass=self.__class__,
4949 decode_path=decode_path,
4953 raise NotEnoughData(
4954 "encoded length is longer than data",
4955 klass=self.__class__,
4959 v, tail = v[:l], v[l:]
4961 sub_offset = offset + tlen + llen
4964 ctx_allow_default_values = ctx.get("allow_default_values", False)
4965 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4966 value_prev = memoryview(v[:0])
4969 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4971 for name, spec in self._specs_items():
4972 sub_decode_path = decode_path + (name,)
4978 decode_path=sub_decode_path,
4981 _ctx_immutable=False,
4988 klass=self.__class__,
4989 decode_path=decode_path,
4992 value, v_tail = spec.decode(
4996 decode_path=sub_decode_path,
4998 _ctx_immutable=False,
5000 value_len = value.fulllen
5001 if value_prev.tobytes() > v[:value_len].tobytes():
5002 if ctx_bered or ctx_allow_unordered_set:
5006 "unordered " + self.asn1_type_name,
5007 klass=self.__class__,
5008 decode_path=sub_decode_path,
5011 if spec.default is None or value != spec.default:
5013 elif ctx_bered or ctx_allow_default_values:
5017 "DEFAULT value met",
5018 klass=self.__class__,
5019 decode_path=sub_decode_path,
5022 values[name] = value
5023 value_prev = v[:value_len]
5024 sub_offset += value_len
5027 obj = self.__class__(
5031 default=self.default,
5032 optional=self.optional,
5033 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5036 if v[:EOC_LEN].tobytes() != EOC:
5039 klass=self.__class__,
5040 decode_path=decode_path,
5048 "not all values are ready",
5049 klass=self.__class__,
5050 decode_path=decode_path,
5053 obj.ber_encoded = ber_encoded
5057 class SequenceOf(Obj):
5058 """``SEQUENCE OF`` sequence type
5060 For that kind of type you must specify the object it will carry on
5061 (bounds are for example here, not required)::
5063 class Ints(SequenceOf):
5068 >>> ints.append(Integer(123))
5069 >>> ints.append(Integer(234))
5071 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5072 >>> [int(i) for i in ints]
5074 >>> ints.append(Integer(345))
5075 Traceback (most recent call last):
5076 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5079 >>> ints[1] = Integer(345)
5081 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5083 Also you can initialize sequence with preinitialized values:
5085 >>> ints = Ints([Integer(123), Integer(234)])
5087 __slots__ = ("spec", "_bound_min", "_bound_max")
5088 tag_default = tag_encode(form=TagFormConstructed, num=16)
5089 asn1_type_name = "SEQUENCE OF"
5102 super(SequenceOf, self).__init__(
5110 schema = getattr(self, "schema", None)
5112 raise ValueError("schema must be specified")
5114 self._bound_min, self._bound_max = getattr(
5118 ) if bounds is None else bounds
5120 if value is not None:
5121 self._value = self._value_sanitize(value)
5122 if default is not None:
5123 default_value = self._value_sanitize(default)
5124 default_obj = self.__class__(
5129 default_obj._value = default_value
5130 self.default = default_obj
5132 self._value = default_obj.copy()._value
5134 def _value_sanitize(self, value):
5135 if issubclass(value.__class__, SequenceOf):
5136 value = value._value
5137 elif hasattr(value, "__iter__"):
5140 raise InvalidValueType((self.__class__, iter))
5141 if not self._bound_min <= len(value) <= self._bound_max:
5142 raise BoundsError(self._bound_min, len(value), self._bound_max)
5144 if not isinstance(v, self.spec.__class__):
5145 raise InvalidValueType((self.spec.__class__,))
5150 return all(v.ready for v in self._value)
5154 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5156 return any(v.bered for v in self._value)
5159 obj = self.__class__(schema=self.spec)
5160 obj._bound_min = self._bound_min
5161 obj._bound_max = self._bound_max
5163 obj._expl = self._expl
5164 obj.default = self.default
5165 obj.optional = self.optional
5166 obj.offset = self.offset
5167 obj.llen = self.llen
5168 obj.vlen = self.vlen
5169 obj.expl_lenindef = self.expl_lenindef
5170 obj.lenindef = self.lenindef
5171 obj.ber_encoded = self.ber_encoded
5172 obj._value = [v.copy() for v in self._value]
5175 def __eq__(self, their):
5176 if isinstance(their, self.__class__):
5178 self.spec == their.spec and
5179 self.tag == their.tag and
5180 self._expl == their._expl and
5181 self._value == their._value
5183 if hasattr(their, "__iter__"):
5184 return self._value == list(their)
5196 return self.__class__(
5200 (self._bound_min, self._bound_max)
5201 if bounds is None else bounds
5203 impl=self.tag if impl is None else impl,
5204 expl=self._expl if expl is None else expl,
5205 default=self.default if default is None else default,
5206 optional=self.optional if optional is None else optional,
5209 def __contains__(self, key):
5210 return key in self._value
5212 def append(self, value):
5213 if not isinstance(value, self.spec.__class__):
5214 raise InvalidValueType((self.spec.__class__,))
5215 if len(self._value) + 1 > self._bound_max:
5218 len(self._value) + 1,
5221 self._value.append(value)
5224 self._assert_ready()
5225 return iter(self._value)
5228 self._assert_ready()
5229 return len(self._value)
5231 def __setitem__(self, key, value):
5232 if not isinstance(value, self.spec.__class__):
5233 raise InvalidValueType((self.spec.__class__,))
5234 self._value[key] = self.spec(value=value)
5236 def __getitem__(self, key):
5237 return self._value[key]
5239 def _encoded_values(self):
5240 return [v.encode() for v in self._value]
5243 v = b"".join(self._encoded_values())
5244 return b"".join((self.tag, len_encode(len(v)), v))
5246 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5248 t, tlen, lv = tag_strip(tlv)
5249 except DecodeError as err:
5250 raise err.__class__(
5252 klass=self.__class__,
5253 decode_path=decode_path,
5258 klass=self.__class__,
5259 decode_path=decode_path,
5265 ctx_bered = ctx.get("bered", False)
5267 l, llen, v = len_decode(lv)
5268 except LenIndefForm as err:
5270 raise err.__class__(
5272 klass=self.__class__,
5273 decode_path=decode_path,
5276 l, llen, v = 0, 1, lv[1:]
5278 except DecodeError as err:
5279 raise err.__class__(
5281 klass=self.__class__,
5282 decode_path=decode_path,
5286 raise NotEnoughData(
5287 "encoded length is longer than data",
5288 klass=self.__class__,
5289 decode_path=decode_path,
5293 v, tail = v[:l], v[l:]
5295 sub_offset = offset + tlen + llen
5297 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5298 value_prev = memoryview(v[:0])
5302 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5304 sub_decode_path = decode_path + (str(len(_value)),)
5305 value, v_tail = spec.decode(
5309 decode_path=sub_decode_path,
5311 _ctx_immutable=False,
5313 value_len = value.fulllen
5315 if value_prev.tobytes() > v[:value_len].tobytes():
5316 if ctx_bered or ctx_allow_unordered_set:
5320 "unordered " + self.asn1_type_name,
5321 klass=self.__class__,
5322 decode_path=sub_decode_path,
5325 value_prev = v[:value_len]
5326 _value.append(value)
5327 sub_offset += value_len
5331 obj = self.__class__(
5334 bounds=(self._bound_min, self._bound_max),
5337 default=self.default,
5338 optional=self.optional,
5339 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5341 except BoundsError as err:
5344 klass=self.__class__,
5345 decode_path=decode_path,
5349 if v[:EOC_LEN].tobytes() != EOC:
5352 klass=self.__class__,
5353 decode_path=decode_path,
5358 obj.ber_encoded = ber_encoded
5363 pp_console_row(next(self.pps())),
5364 ", ".join(repr(v) for v in self._value),
5367 def pps(self, decode_path=()):
5370 asn1_type_name=self.asn1_type_name,
5371 obj_name=self.__class__.__name__,
5372 decode_path=decode_path,
5373 optional=self.optional,
5374 default=self == self.default,
5375 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5376 expl=None if self._expl is None else tag_decode(self._expl),
5381 expl_offset=self.expl_offset if self.expled else None,
5382 expl_tlen=self.expl_tlen if self.expled else None,
5383 expl_llen=self.expl_llen if self.expled else None,
5384 expl_vlen=self.expl_vlen if self.expled else None,
5385 expl_lenindef=self.expl_lenindef,
5386 lenindef=self.lenindef,
5387 ber_encoded=self.ber_encoded,
5390 for i, value in enumerate(self._value):
5391 yield value.pps(decode_path=decode_path + (str(i),))
5392 for pp in self.pps_lenindef(decode_path):
5396 class SetOf(SequenceOf):
5397 """``SET OF`` sequence type
5399 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5402 tag_default = tag_encode(form=TagFormConstructed, num=17)
5403 asn1_type_name = "SET OF"
5406 raws = self._encoded_values()
5409 return b"".join((self.tag, len_encode(len(v)), v))
5411 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5412 return super(SetOf, self)._decode(
5418 ordering_check=True,
5422 def obj_by_path(pypath): # pragma: no cover
5423 """Import object specified as string Python path
5425 Modules must be separated from classes/functions with ``:``.
5427 >>> obj_by_path("foo.bar:Baz")
5428 <class 'foo.bar.Baz'>
5429 >>> obj_by_path("foo.bar:Baz.boo")
5430 <classmethod 'foo.bar.Baz.boo'>
5432 mod, objs = pypath.rsplit(":", 1)
5433 from importlib import import_module
5434 obj = import_module(mod)
5435 for obj_name in objs.split("."):
5436 obj = getattr(obj, obj_name)
5440 def generic_decoder(): # pragma: no cover
5441 # All of this below is a big hack with self references
5442 choice = PrimitiveTypes()
5443 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5444 choice.specs["SetOf"] = SetOf(schema=choice)
5445 for i in six_xrange(31):
5446 choice.specs["SequenceOf%d" % i] = SequenceOf(
5450 choice.specs["Any"] = Any()
5452 # Class name equals to type name, to omit it from output
5453 class SEQUENCEOF(SequenceOf):
5461 with_decode_path=False,
5462 decode_path_only=(),
5464 def _pprint_pps(pps):
5466 if hasattr(pp, "_fields"):
5468 decode_path_only != () and
5469 pp.decode_path[:len(decode_path_only)] != decode_path_only
5472 if pp.asn1_type_name == Choice.asn1_type_name:
5474 pp_kwargs = pp._asdict()
5475 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5476 pp = _pp(**pp_kwargs)
5477 yield pp_console_row(
5482 with_colours=with_colours,
5483 with_decode_path=with_decode_path,
5484 decode_path_len_decrease=len(decode_path_only),
5486 for row in pp_console_blob(
5488 decode_path_len_decrease=len(decode_path_only),
5492 for row in _pprint_pps(pp):
5494 return "\n".join(_pprint_pps(obj.pps()))
5495 return SEQUENCEOF(), pprint_any
5498 def main(): # pragma: no cover
5500 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5501 parser.add_argument(
5505 help="Skip that number of bytes from the beginning",
5507 parser.add_argument(
5509 help="Python path to dictionary with OIDs",
5511 parser.add_argument(
5513 help="Python path to schema definition to use",
5515 parser.add_argument(
5516 "--defines-by-path",
5517 help="Python path to decoder's defines_by_path",
5519 parser.add_argument(
5521 action="store_true",
5522 help="Disallow BER encoding",
5524 parser.add_argument(
5525 "--print-decode-path",
5526 action="store_true",
5527 help="Print decode paths",
5529 parser.add_argument(
5530 "--decode-path-only",
5531 help="Print only specified decode path",
5533 parser.add_argument(
5535 action="store_true",
5536 help="Allow explicit tag out-of-bound",
5538 parser.add_argument(
5540 type=argparse.FileType("rb"),
5541 help="Path to DER file you want to decode",
5543 args = parser.parse_args()
5544 args.DERFile.seek(args.skip)
5545 der = memoryview(args.DERFile.read())
5546 args.DERFile.close()
5547 oids = obj_by_path(args.oids) if args.oids else {}
5549 schema = obj_by_path(args.schema)
5550 from functools import partial
5551 pprinter = partial(pprint, big_blobs=True)
5553 schema, pprinter = generic_decoder()
5555 "bered": not args.nobered,
5556 "allow_expl_oob": args.allow_expl_oob,
5558 if args.defines_by_path is not None:
5559 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5560 obj, tail = schema().decode(der, ctx=ctx)
5564 with_colours=True if environ.get("NO_COLOR") is None else False,
5565 with_decode_path=args.print_decode_path,
5567 () if args.decode_path_only is None else
5568 tuple(args.decode_path_only.split(":"))
5572 print("\nTrailing data: %s" % hexenc(tail))
5575 if __name__ == "__main__":