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
559 from six import string_types
560 from six import text_type
561 from six import unichr as six_unichr
562 from six.moves import xrange as six_xrange
566 from termcolor import colored
567 except ImportError: # pragma: no cover
568 def colored(what, *args):
612 "TagClassApplication",
616 "TagFormConstructed",
627 TagClassUniversal = 0
628 TagClassApplication = 1 << 6
629 TagClassContext = 1 << 7
630 TagClassPrivate = 1 << 6 | 1 << 7
632 TagFormConstructed = 1 << 5
635 TagClassApplication: "APPLICATION ",
636 TagClassPrivate: "PRIVATE ",
637 TagClassUniversal: "UNIV ",
641 LENINDEF = b"\x80" # length indefinite mark
642 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
645 ########################################################################
647 ########################################################################
649 class ASN1Error(ValueError):
653 class DecodeError(ASN1Error):
654 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
656 :param str msg: reason of decode failing
657 :param klass: optional exact DecodeError inherited class (like
658 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
659 :py:exc:`InvalidLength`)
660 :param decode_path: tuple of strings. It contains human
661 readable names of the fields through which
662 decoding process has passed
663 :param int offset: binary offset where failure happened
665 super(DecodeError, self).__init__()
668 self.decode_path = decode_path
674 "" if self.klass is None else self.klass.__name__,
676 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
677 if len(self.decode_path) > 0 else ""
679 ("(at %d)" % self.offset) if self.offset > 0 else "",
685 return "%s(%s)" % (self.__class__.__name__, self)
688 class NotEnoughData(DecodeError):
692 class LenIndefForm(DecodeError):
696 class TagMismatch(DecodeError):
700 class InvalidLength(DecodeError):
704 class InvalidOID(DecodeError):
708 class ObjUnknown(ASN1Error):
709 def __init__(self, name):
710 super(ObjUnknown, self).__init__()
714 return "object is unknown: %s" % self.name
717 return "%s(%s)" % (self.__class__.__name__, self)
720 class ObjNotReady(ASN1Error):
721 def __init__(self, name):
722 super(ObjNotReady, self).__init__()
726 return "object is not ready: %s" % self.name
729 return "%s(%s)" % (self.__class__.__name__, self)
732 class InvalidValueType(ASN1Error):
733 def __init__(self, expected_types):
734 super(InvalidValueType, self).__init__()
735 self.expected_types = expected_types
738 return "invalid value type, expected: %s" % ", ".join(
739 [repr(t) for t in self.expected_types]
743 return "%s(%s)" % (self.__class__.__name__, self)
746 class BoundsError(ASN1Error):
747 def __init__(self, bound_min, value, bound_max):
748 super(BoundsError, self).__init__()
749 self.bound_min = bound_min
751 self.bound_max = bound_max
754 return "unsatisfied bounds: %s <= %s <= %s" % (
761 return "%s(%s)" % (self.__class__.__name__, self)
764 ########################################################################
766 ########################################################################
768 _hexdecoder = getdecoder("hex")
769 _hexencoder = getencoder("hex")
773 """Binary data to hexadecimal string convert
775 return _hexdecoder(data)[0]
779 """Hexadecimal string to binary data convert
781 return _hexencoder(data)[0].decode("ascii")
784 def int_bytes_len(num, byte_len=8):
787 return int(ceil(float(num.bit_length()) / byte_len))
790 def zero_ended_encode(num):
791 octets = bytearray(int_bytes_len(num, 7))
793 octets[i] = num & 0x7F
797 octets[i] = 0x80 | (num & 0x7F)
803 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
804 """Encode tag to binary form
806 :param int num: tag's number
807 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
808 :py:data:`pyderasn.TagClassContext`,
809 :py:data:`pyderasn.TagClassApplication`,
810 :py:data:`pyderasn.TagClassPrivate`)
811 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
812 :py:data:`pyderasn.TagFormConstructed`)
816 return int2byte(klass | form | num)
817 # [XX|X|11111][1.......][1.......] ... [0.......]
818 return int2byte(klass | form | 31) + zero_ended_encode(num)
822 """Decode tag from binary form
826 No validation is performed, assuming that it has already passed.
828 It returns tuple with three integers, as
829 :py:func:`pyderasn.tag_encode` accepts.
831 first_octet = byte2int(tag)
832 klass = first_octet & 0xC0
833 form = first_octet & 0x20
834 if first_octet & 0x1F < 0x1F:
835 return (klass, form, first_octet & 0x1F)
837 for octet in iterbytes(tag[1:]):
840 return (klass, form, num)
844 """Create CONTEXT PRIMITIVE tag
846 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
850 """Create CONTEXT CONSTRUCTED tag
852 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
856 """Take off tag from the data
858 :returns: (encoded tag, tag length, remaining data)
861 raise NotEnoughData("no data at all")
862 if byte2int(data) & 0x1F < 31:
863 return data[:1], 1, data[1:]
868 raise DecodeError("unfinished tag")
869 if indexbytes(data, i) & 0x80 == 0:
872 return data[:i], i, data[i:]
878 octets = bytearray(int_bytes_len(l) + 1)
879 octets[0] = 0x80 | (len(octets) - 1)
880 for i in six_xrange(len(octets) - 1, 0, -1):
886 def len_decode(data):
889 :returns: (decoded length, length's length, remaining data)
890 :raises LenIndefForm: if indefinite form encoding is met
893 raise NotEnoughData("no data at all")
894 first_octet = byte2int(data)
895 if first_octet & 0x80 == 0:
896 return first_octet, 1, data[1:]
897 octets_num = first_octet & 0x7F
898 if octets_num + 1 > len(data):
899 raise NotEnoughData("encoded length is longer than data")
902 if byte2int(data[1:]) == 0:
903 raise DecodeError("leading zeros")
905 for v in iterbytes(data[1:1 + octets_num]):
908 raise DecodeError("long form instead of short one")
909 return l, 1 + octets_num, data[1 + octets_num:]
912 ########################################################################
914 ########################################################################
916 class AutoAddSlots(type):
917 def __new__(mcs, name, bases, _dict):
918 _dict["__slots__"] = _dict.get("__slots__", ())
919 return type.__new__(mcs, name, bases, _dict)
922 @add_metaclass(AutoAddSlots)
924 """Common ASN.1 object class
926 All ASN.1 types are inherited from it. It has metaclass that
927 automatically adds ``__slots__`` to all inherited classes.
951 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
952 self._expl = getattr(self, "expl", None) if expl is None else expl
953 if self.tag != self.tag_default and self._expl is not None:
954 raise ValueError("implicit and explicit tags can not be set simultaneously")
955 if default is not None:
957 self.optional = optional
958 self.offset, self.llen, self.vlen = _decoded
960 self.expl_lenindef = False
961 self.lenindef = False
962 self.ber_encoded = False
965 def ready(self): # pragma: no cover
966 """Is object ready to be encoded?
968 raise NotImplementedError()
970 def _assert_ready(self):
972 raise ObjNotReady(self.__class__.__name__)
976 """Is either object or any elements inside is BER encoded?
978 return self.expl_lenindef or self.lenindef or self.ber_encoded
982 """Is object decoded?
984 return (self.llen + self.vlen) > 0
986 def copy(self): # pragma: no cover
987 """Make a copy of object, safe to be mutated
989 raise NotImplementedError()
997 return self.tlen + self.llen + self.vlen
999 def __str__(self): # pragma: no cover
1000 return self.__bytes__() if PY2 else self.__unicode__()
1002 def __ne__(self, their):
1003 return not(self == their)
1005 def __gt__(self, their): # pragma: no cover
1006 return not(self < their)
1008 def __le__(self, their): # pragma: no cover
1009 return (self == their) or (self < their)
1011 def __ge__(self, their): # pragma: no cover
1012 return (self == their) or (self > their)
1014 def _encode(self): # pragma: no cover
1015 raise NotImplementedError()
1017 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1018 raise NotImplementedError()
1021 raw = self._encode()
1022 if self._expl is None:
1024 return b"".join((self._expl, len_encode(len(raw)), raw))
1034 _ctx_immutable=True,
1038 :param data: either binary or memoryview
1039 :param int offset: initial data's offset
1040 :param bool leavemm: do we need to leave memoryview of remaining
1041 data as is, or convert it to bytes otherwise
1042 :param ctx: optional :ref:`context <ctx>` governing decoding process
1043 :param tag_only: decode only the tag, without length and contents
1044 (used only in Choice and Set structures, trying to
1045 determine if tag satisfies the scheme)
1046 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1047 :returns: (Obj, remaining data)
1051 elif _ctx_immutable:
1053 tlv = memoryview(data)
1054 if self._expl is None:
1055 result = self._decode(
1058 decode_path=decode_path,
1067 t, tlen, lv = tag_strip(tlv)
1068 except DecodeError as err:
1069 raise err.__class__(
1071 klass=self.__class__,
1072 decode_path=decode_path,
1077 klass=self.__class__,
1078 decode_path=decode_path,
1082 l, llen, v = len_decode(lv)
1083 except LenIndefForm as err:
1084 if not ctx.get("bered", False):
1085 raise err.__class__(
1087 klass=self.__class__,
1088 decode_path=decode_path,
1092 offset += tlen + llen
1093 result = self._decode(
1096 decode_path=decode_path,
1100 if tag_only: # pragma: no cover
1103 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1104 if eoc_expected.tobytes() != EOC:
1107 klass=self.__class__,
1108 decode_path=decode_path,
1112 obj.expl_lenindef = True
1113 except DecodeError as err:
1114 raise err.__class__(
1116 klass=self.__class__,
1117 decode_path=decode_path,
1122 raise NotEnoughData(
1123 "encoded length is longer than data",
1124 klass=self.__class__,
1125 decode_path=decode_path,
1128 result = self._decode(
1130 offset=offset + tlen + llen,
1131 decode_path=decode_path,
1135 if tag_only: # pragma: no cover
1138 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1140 "explicit tag out-of-bound, longer than data",
1141 klass=self.__class__,
1142 decode_path=decode_path,
1145 return obj, (tail if leavemm else tail.tobytes())
1149 return self._expl is not None
1156 def expl_tlen(self):
1157 return len(self._expl)
1160 def expl_llen(self):
1161 if self.expl_lenindef:
1163 return len(len_encode(self.tlvlen))
1166 def expl_offset(self):
1167 return self.offset - self.expl_tlen - self.expl_llen
1170 def expl_vlen(self):
1174 def expl_tlvlen(self):
1175 return self.expl_tlen + self.expl_llen + self.expl_vlen
1178 def fulloffset(self):
1179 return self.expl_offset if self.expled else self.offset
1183 return self.expl_tlvlen if self.expled else self.tlvlen
1185 def pps_lenindef(self, decode_path):
1186 if self.lenindef and not (
1187 getattr(self, "defined", None) is not None and
1188 self.defined[1].lenindef
1191 asn1_type_name="EOC",
1193 decode_path=decode_path,
1195 self.offset + self.tlvlen -
1196 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1204 if self.expl_lenindef:
1206 asn1_type_name="EOC",
1207 obj_name="EXPLICIT",
1208 decode_path=decode_path,
1209 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1218 class DecodePathDefBy(object):
1219 """DEFINED BY representation inside decode path
1221 __slots__ = ("defined_by",)
1223 def __init__(self, defined_by):
1224 self.defined_by = defined_by
1226 def __ne__(self, their):
1227 return not(self == their)
1229 def __eq__(self, their):
1230 if not isinstance(their, self.__class__):
1232 return self.defined_by == their.defined_by
1235 return "DEFINED BY " + str(self.defined_by)
1238 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1241 ########################################################################
1243 ########################################################################
1245 PP = namedtuple("PP", (
1273 asn1_type_name="unknown",
1290 expl_lenindef=False,
1321 def _colourize(what, colour, with_colours, attrs=("bold",)):
1322 return colored(what, colour, attrs=attrs) if with_colours else what
1325 def colonize_hex(hexed):
1326 """Separate hexadecimal string with colons
1328 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1337 with_decode_path=False,
1338 decode_path_len_decrease=0,
1345 " " if pp.expl_offset is None else
1346 ("-%d" % (pp.offset - pp.expl_offset))
1348 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1350 col = _colourize(col, "red", with_colours, ())
1351 col += _colourize("B", "red", with_colours) if pp.bered else " "
1353 col = "[%d,%d,%4d]%s" % (
1357 LENINDEF_PP_CHAR if pp.lenindef else " "
1359 col = _colourize(col, "green", with_colours, ())
1361 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1362 if decode_path_len > 0:
1363 cols.append(" ." * decode_path_len)
1364 ent = pp.decode_path[-1]
1365 if isinstance(ent, DecodePathDefBy):
1366 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1367 value = str(ent.defined_by)
1369 oids is not None and
1370 ent.defined_by.asn1_type_name ==
1371 ObjectIdentifier.asn1_type_name and
1374 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1376 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1378 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1379 if pp.expl is not None:
1380 klass, _, num = pp.expl
1381 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1382 cols.append(_colourize(col, "blue", with_colours))
1383 if pp.impl is not None:
1384 klass, _, num = pp.impl
1385 col = "[%s%d]" % (TagClassReprs[klass], num)
1386 cols.append(_colourize(col, "blue", with_colours))
1387 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1388 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1390 cols.append(_colourize("BER", "red", with_colours))
1391 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1392 if pp.value is not None:
1394 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1396 oids is not None and
1397 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1400 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1401 if pp.asn1_type_name == Integer.asn1_type_name:
1402 hex_repr = hex(int(pp.obj._value))[2:].upper()
1403 if len(hex_repr) % 2 != 0:
1404 hex_repr = "0" + hex_repr
1405 cols.append(_colourize(
1406 "(%s)" % colonize_hex(hex_repr),
1411 if isinstance(pp.blob, binary_type):
1412 cols.append(hexenc(pp.blob))
1413 elif isinstance(pp.blob, tuple):
1414 cols.append(", ".join(pp.blob))
1416 cols.append(_colourize("OPTIONAL", "red", with_colours))
1418 cols.append(_colourize("DEFAULT", "red", with_colours))
1419 if with_decode_path:
1420 cols.append(_colourize(
1421 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1425 return " ".join(cols)
1428 def pp_console_blob(pp, decode_path_len_decrease=0):
1429 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1430 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1431 if decode_path_len > 0:
1432 cols.append(" ." * (decode_path_len + 1))
1433 if isinstance(pp.blob, binary_type):
1434 blob = hexenc(pp.blob).upper()
1435 for i in six_xrange(0, len(blob), 32):
1436 chunk = blob[i:i + 32]
1437 yield " ".join(cols + [colonize_hex(chunk)])
1438 elif isinstance(pp.blob, tuple):
1439 yield " ".join(cols + [", ".join(pp.blob)])
1447 with_decode_path=False,
1448 decode_path_only=(),
1450 """Pretty print object
1452 :param Obj obj: object you want to pretty print
1453 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1454 from it is met, then its humand readable form is printed
1455 :param big_blobs: if large binary objects are met (like OctetString
1456 values), do we need to print them too, on separate
1458 :param with_colours: colourize output, if ``termcolor`` library
1460 :param with_decode_path: print decode path
1461 :param decode_path_only: print only that specified decode path
1463 def _pprint_pps(pps):
1465 if hasattr(pp, "_fields"):
1467 decode_path_only != () and
1469 str(p) for p in pp.decode_path[:len(decode_path_only)]
1470 ) != decode_path_only
1474 yield pp_console_row(
1479 with_colours=with_colours,
1480 with_decode_path=with_decode_path,
1481 decode_path_len_decrease=len(decode_path_only),
1483 for row in pp_console_blob(
1485 decode_path_len_decrease=len(decode_path_only),
1489 yield pp_console_row(
1494 with_colours=with_colours,
1495 with_decode_path=with_decode_path,
1496 decode_path_len_decrease=len(decode_path_only),
1499 for row in _pprint_pps(pp):
1501 return "\n".join(_pprint_pps(obj.pps()))
1504 ########################################################################
1505 # ASN.1 primitive types
1506 ########################################################################
1509 """``BOOLEAN`` boolean type
1511 >>> b = Boolean(True)
1513 >>> b == Boolean(True)
1519 tag_default = tag_encode(1)
1520 asn1_type_name = "BOOLEAN"
1532 :param value: set the value. Either boolean type, or
1533 :py:class:`pyderasn.Boolean` object
1534 :param bytes impl: override default tag with ``IMPLICIT`` one
1535 :param bytes expl: override default tag with ``EXPLICIT`` one
1536 :param default: set default value. Type same as in ``value``
1537 :param bool optional: is object ``OPTIONAL`` in sequence
1539 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1540 self._value = None if value is None else self._value_sanitize(value)
1541 if default is not None:
1542 default = self._value_sanitize(default)
1543 self.default = self.__class__(
1549 self._value = default
1551 def _value_sanitize(self, value):
1552 if issubclass(value.__class__, Boolean):
1554 if isinstance(value, bool):
1556 raise InvalidValueType((self.__class__, bool))
1560 return self._value is not None
1563 obj = self.__class__()
1564 obj._value = self._value
1566 obj._expl = self._expl
1567 obj.default = self.default
1568 obj.optional = self.optional
1569 obj.offset = self.offset
1570 obj.llen = self.llen
1571 obj.vlen = self.vlen
1572 obj.expl_lenindef = self.expl_lenindef
1573 obj.lenindef = self.lenindef
1574 obj.ber_encoded = self.ber_encoded
1577 def __nonzero__(self):
1578 self._assert_ready()
1582 self._assert_ready()
1585 def __eq__(self, their):
1586 if isinstance(their, bool):
1587 return self._value == their
1588 if not issubclass(their.__class__, Boolean):
1591 self._value == their._value and
1592 self.tag == their.tag and
1593 self._expl == their._expl
1604 return self.__class__(
1606 impl=self.tag if impl is None else impl,
1607 expl=self._expl if expl is None else expl,
1608 default=self.default if default is None else default,
1609 optional=self.optional if optional is None else optional,
1613 self._assert_ready()
1617 (b"\xFF" if self._value else b"\x00"),
1620 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1622 t, _, lv = tag_strip(tlv)
1623 except DecodeError as err:
1624 raise err.__class__(
1626 klass=self.__class__,
1627 decode_path=decode_path,
1632 klass=self.__class__,
1633 decode_path=decode_path,
1639 l, _, v = len_decode(lv)
1640 except DecodeError as err:
1641 raise err.__class__(
1643 klass=self.__class__,
1644 decode_path=decode_path,
1648 raise InvalidLength(
1649 "Boolean's length must be equal to 1",
1650 klass=self.__class__,
1651 decode_path=decode_path,
1655 raise NotEnoughData(
1656 "encoded length is longer than data",
1657 klass=self.__class__,
1658 decode_path=decode_path,
1661 first_octet = byte2int(v)
1663 if first_octet == 0:
1665 elif first_octet == 0xFF:
1667 elif ctx.get("bered", False):
1672 "unacceptable Boolean value",
1673 klass=self.__class__,
1674 decode_path=decode_path,
1677 obj = self.__class__(
1681 default=self.default,
1682 optional=self.optional,
1683 _decoded=(offset, 1, 1),
1685 obj.ber_encoded = ber_encoded
1689 return pp_console_row(next(self.pps()))
1691 def pps(self, decode_path=()):
1694 asn1_type_name=self.asn1_type_name,
1695 obj_name=self.__class__.__name__,
1696 decode_path=decode_path,
1697 value=str(self._value) if self.ready else None,
1698 optional=self.optional,
1699 default=self == self.default,
1700 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1701 expl=None if self._expl is None else tag_decode(self._expl),
1706 expl_offset=self.expl_offset if self.expled else None,
1707 expl_tlen=self.expl_tlen if self.expled else None,
1708 expl_llen=self.expl_llen if self.expled else None,
1709 expl_vlen=self.expl_vlen if self.expled else None,
1710 expl_lenindef=self.expl_lenindef,
1711 ber_encoded=self.ber_encoded,
1714 for pp in self.pps_lenindef(decode_path):
1719 """``INTEGER`` integer type
1721 >>> b = Integer(-123)
1723 >>> b == Integer(-123)
1728 >>> Integer(2, bounds=(1, 3))
1730 >>> Integer(5, bounds=(1, 3))
1731 Traceback (most recent call last):
1732 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1736 class Version(Integer):
1743 >>> v = Version("v1")
1750 {'v3': 2, 'v1': 0, 'v2': 1}
1752 __slots__ = ("specs", "_bound_min", "_bound_max")
1753 tag_default = tag_encode(2)
1754 asn1_type_name = "INTEGER"
1768 :param value: set the value. Either integer type, named value
1769 (if ``schema`` is specified in the class), or
1770 :py:class:`pyderasn.Integer` object
1771 :param bounds: set ``(MIN, MAX)`` value constraint.
1772 (-inf, +inf) by default
1773 :param bytes impl: override default tag with ``IMPLICIT`` one
1774 :param bytes expl: override default tag with ``EXPLICIT`` one
1775 :param default: set default value. Type same as in ``value``
1776 :param bool optional: is object ``OPTIONAL`` in sequence
1778 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1780 specs = getattr(self, "schema", {}) if _specs is None else _specs
1781 self.specs = specs if isinstance(specs, dict) else dict(specs)
1782 self._bound_min, self._bound_max = getattr(
1785 (float("-inf"), float("+inf")),
1786 ) if bounds is None else bounds
1787 if value is not None:
1788 self._value = self._value_sanitize(value)
1789 if default is not None:
1790 default = self._value_sanitize(default)
1791 self.default = self.__class__(
1797 if self._value is None:
1798 self._value = default
1800 def _value_sanitize(self, value):
1801 if issubclass(value.__class__, Integer):
1802 value = value._value
1803 elif isinstance(value, integer_types):
1805 elif isinstance(value, str):
1806 value = self.specs.get(value)
1808 raise ObjUnknown("integer value: %s" % value)
1810 raise InvalidValueType((self.__class__, int, str))
1811 if not self._bound_min <= value <= self._bound_max:
1812 raise BoundsError(self._bound_min, value, self._bound_max)
1817 return self._value is not None
1820 obj = self.__class__(_specs=self.specs)
1821 obj._value = self._value
1822 obj._bound_min = self._bound_min
1823 obj._bound_max = self._bound_max
1825 obj._expl = self._expl
1826 obj.default = self.default
1827 obj.optional = self.optional
1828 obj.offset = self.offset
1829 obj.llen = self.llen
1830 obj.vlen = self.vlen
1831 obj.expl_lenindef = self.expl_lenindef
1832 obj.lenindef = self.lenindef
1833 obj.ber_encoded = self.ber_encoded
1837 self._assert_ready()
1838 return int(self._value)
1841 self._assert_ready()
1844 bytes(self._expl or b"") +
1845 str(self._value).encode("ascii"),
1848 def __eq__(self, their):
1849 if isinstance(their, integer_types):
1850 return self._value == their
1851 if not issubclass(their.__class__, Integer):
1854 self._value == their._value and
1855 self.tag == their.tag and
1856 self._expl == their._expl
1859 def __lt__(self, their):
1860 return self._value < their._value
1864 for name, value in self.specs.items():
1865 if value == self._value:
1877 return self.__class__(
1880 (self._bound_min, self._bound_max)
1881 if bounds is None else bounds
1883 impl=self.tag if impl is None else impl,
1884 expl=self._expl if expl is None else expl,
1885 default=self.default if default is None else default,
1886 optional=self.optional if optional is None else optional,
1891 self._assert_ready()
1895 octets = bytearray([0])
1899 octets = bytearray()
1901 octets.append((value & 0xFF) ^ 0xFF)
1903 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1906 octets = bytearray()
1908 octets.append(value & 0xFF)
1910 if octets[-1] & 0x80 > 0:
1913 octets = bytes(octets)
1915 bytes_len = ceil(value.bit_length() / 8) or 1
1918 octets = value.to_bytes(
1923 except OverflowError:
1927 return b"".join((self.tag, len_encode(len(octets)), octets))
1929 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1931 t, _, lv = tag_strip(tlv)
1932 except DecodeError as err:
1933 raise err.__class__(
1935 klass=self.__class__,
1936 decode_path=decode_path,
1941 klass=self.__class__,
1942 decode_path=decode_path,
1948 l, llen, v = len_decode(lv)
1949 except DecodeError as err:
1950 raise err.__class__(
1952 klass=self.__class__,
1953 decode_path=decode_path,
1957 raise NotEnoughData(
1958 "encoded length is longer than data",
1959 klass=self.__class__,
1960 decode_path=decode_path,
1964 raise NotEnoughData(
1966 klass=self.__class__,
1967 decode_path=decode_path,
1970 v, tail = v[:l], v[l:]
1971 first_octet = byte2int(v)
1973 second_octet = byte2int(v[1:])
1975 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1976 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1979 "non normalized integer",
1980 klass=self.__class__,
1981 decode_path=decode_path,
1986 if first_octet & 0x80 > 0:
1987 octets = bytearray()
1988 for octet in bytearray(v):
1989 octets.append(octet ^ 0xFF)
1990 for octet in octets:
1991 value = (value << 8) | octet
1995 for octet in bytearray(v):
1996 value = (value << 8) | octet
1998 value = int.from_bytes(v, byteorder="big", signed=True)
2000 obj = self.__class__(
2002 bounds=(self._bound_min, self._bound_max),
2005 default=self.default,
2006 optional=self.optional,
2008 _decoded=(offset, llen, l),
2010 except BoundsError as err:
2013 klass=self.__class__,
2014 decode_path=decode_path,
2020 return pp_console_row(next(self.pps()))
2022 def pps(self, decode_path=()):
2025 asn1_type_name=self.asn1_type_name,
2026 obj_name=self.__class__.__name__,
2027 decode_path=decode_path,
2028 value=(self.named or str(self._value)) if self.ready else None,
2029 optional=self.optional,
2030 default=self == self.default,
2031 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2032 expl=None if self._expl is None else tag_decode(self._expl),
2037 expl_offset=self.expl_offset if self.expled else None,
2038 expl_tlen=self.expl_tlen if self.expled else None,
2039 expl_llen=self.expl_llen if self.expled else None,
2040 expl_vlen=self.expl_vlen if self.expled else None,
2041 expl_lenindef=self.expl_lenindef,
2044 for pp in self.pps_lenindef(decode_path):
2048 class BitString(Obj):
2049 """``BIT STRING`` bit string type
2051 >>> BitString(b"hello world")
2052 BIT STRING 88 bits 68656c6c6f20776f726c64
2055 >>> b == b"hello world"
2060 >>> BitString("'0A3B5F291CD'H")
2061 BIT STRING 44 bits 0a3b5f291cd0
2062 >>> b = BitString("'010110000000'B")
2063 BIT STRING 12 bits 5800
2066 >>> b[0], b[1], b[2], b[3]
2067 (False, True, False, True)
2071 [False, True, False, True, True, False, False, False, False, False, False, False]
2075 class KeyUsage(BitString):
2077 ("digitalSignature", 0),
2078 ("nonRepudiation", 1),
2079 ("keyEncipherment", 2),
2082 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2083 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2085 ['nonRepudiation', 'keyEncipherment']
2087 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2091 Pay attention that BIT STRING can be encoded both in primitive
2092 and constructed forms. Decoder always checks constructed form tag
2093 additionally to specified primitive one. If BER decoding is
2094 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2095 of DER restrictions.
2097 __slots__ = ("tag_constructed", "specs", "defined")
2098 tag_default = tag_encode(3)
2099 asn1_type_name = "BIT STRING"
2112 :param value: set the value. Either binary type, tuple of named
2113 values (if ``schema`` is specified in the class),
2114 string in ``'XXX...'B`` form, or
2115 :py:class:`pyderasn.BitString` object
2116 :param bytes impl: override default tag with ``IMPLICIT`` one
2117 :param bytes expl: override default tag with ``EXPLICIT`` one
2118 :param default: set default value. Type same as in ``value``
2119 :param bool optional: is object ``OPTIONAL`` in sequence
2121 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2122 specs = getattr(self, "schema", {}) if _specs is None else _specs
2123 self.specs = specs if isinstance(specs, dict) else dict(specs)
2124 self._value = None if value is None else self._value_sanitize(value)
2125 if default is not None:
2126 default = self._value_sanitize(default)
2127 self.default = self.__class__(
2133 self._value = default
2135 tag_klass, _, tag_num = tag_decode(self.tag)
2136 self.tag_constructed = tag_encode(
2138 form=TagFormConstructed,
2142 def _bits2octets(self, bits):
2143 if len(self.specs) > 0:
2144 bits = bits.rstrip("0")
2146 bits += "0" * ((8 - (bit_len % 8)) % 8)
2147 octets = bytearray(len(bits) // 8)
2148 for i in six_xrange(len(octets)):
2149 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2150 return bit_len, bytes(octets)
2152 def _value_sanitize(self, value):
2153 if issubclass(value.__class__, BitString):
2155 if isinstance(value, (string_types, binary_type)):
2157 isinstance(value, string_types) and
2158 value.startswith("'")
2160 if value.endswith("'B"):
2162 if not set(value) <= set(("0", "1")):
2163 raise ValueError("B's coding contains unacceptable chars")
2164 return self._bits2octets(value)
2165 elif value.endswith("'H"):
2169 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2171 if isinstance(value, binary_type):
2172 return (len(value) * 8, value)
2174 raise InvalidValueType((self.__class__, string_types, binary_type))
2175 if isinstance(value, tuple):
2178 isinstance(value[0], integer_types) and
2179 isinstance(value[1], binary_type)
2184 bit = self.specs.get(name)
2186 raise ObjUnknown("BitString value: %s" % name)
2189 return self._bits2octets("")
2191 return self._bits2octets("".join(
2192 ("1" if bit in bits else "0")
2193 for bit in six_xrange(max(bits) + 1)
2195 raise InvalidValueType((self.__class__, binary_type, string_types))
2199 return self._value is not None
2202 obj = self.__class__(_specs=self.specs)
2204 if value is not None:
2205 value = (value[0], value[1])
2208 obj._expl = self._expl
2209 obj.default = self.default
2210 obj.optional = self.optional
2211 obj.offset = self.offset
2212 obj.llen = self.llen
2213 obj.vlen = self.vlen
2214 obj.expl_lenindef = self.expl_lenindef
2215 obj.lenindef = self.lenindef
2216 obj.ber_encoded = self.ber_encoded
2220 self._assert_ready()
2221 for i in six_xrange(self._value[0]):
2226 self._assert_ready()
2227 return self._value[0]
2229 def __bytes__(self):
2230 self._assert_ready()
2231 return self._value[1]
2233 def __eq__(self, their):
2234 if isinstance(their, bytes):
2235 return self._value[1] == their
2236 if not issubclass(their.__class__, BitString):
2239 self._value == their._value and
2240 self.tag == their.tag and
2241 self._expl == their._expl
2246 return [name for name, bit in self.specs.items() if self[bit]]
2256 return self.__class__(
2258 impl=self.tag if impl is None else impl,
2259 expl=self._expl if expl is None else expl,
2260 default=self.default if default is None else default,
2261 optional=self.optional if optional is None else optional,
2265 def __getitem__(self, key):
2266 if isinstance(key, int):
2267 bit_len, octets = self._value
2271 byte2int(memoryview(octets)[key // 8:]) >>
2274 if isinstance(key, string_types):
2275 value = self.specs.get(key)
2277 raise ObjUnknown("BitString value: %s" % key)
2279 raise InvalidValueType((int, str))
2282 self._assert_ready()
2283 bit_len, octets = self._value
2286 len_encode(len(octets) + 1),
2287 int2byte((8 - bit_len % 8) % 8),
2291 def _decode_chunk(self, lv, offset, decode_path, ctx):
2293 l, llen, v = len_decode(lv)
2294 except DecodeError as err:
2295 raise err.__class__(
2297 klass=self.__class__,
2298 decode_path=decode_path,
2302 raise NotEnoughData(
2303 "encoded length is longer than data",
2304 klass=self.__class__,
2305 decode_path=decode_path,
2309 raise NotEnoughData(
2311 klass=self.__class__,
2312 decode_path=decode_path,
2315 pad_size = byte2int(v)
2316 if l == 1 and pad_size != 0:
2318 "invalid empty value",
2319 klass=self.__class__,
2320 decode_path=decode_path,
2326 klass=self.__class__,
2327 decode_path=decode_path,
2330 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2333 klass=self.__class__,
2334 decode_path=decode_path,
2337 v, tail = v[:l], v[l:]
2338 obj = self.__class__(
2339 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2342 default=self.default,
2343 optional=self.optional,
2345 _decoded=(offset, llen, l),
2349 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2351 t, tlen, lv = tag_strip(tlv)
2352 except DecodeError as err:
2353 raise err.__class__(
2355 klass=self.__class__,
2356 decode_path=decode_path,
2360 if tag_only: # pragma: no cover
2362 return self._decode_chunk(lv, offset, decode_path, ctx)
2363 if t == self.tag_constructed:
2364 if not ctx.get("bered", False):
2366 "unallowed BER constructed encoding",
2367 klass=self.__class__,
2368 decode_path=decode_path,
2371 if tag_only: # pragma: no cover
2375 l, llen, v = len_decode(lv)
2376 except LenIndefForm:
2377 llen, l, v = 1, 0, lv[1:]
2379 except DecodeError as err:
2380 raise err.__class__(
2382 klass=self.__class__,
2383 decode_path=decode_path,
2387 raise NotEnoughData(
2388 "encoded length is longer than data",
2389 klass=self.__class__,
2390 decode_path=decode_path,
2393 if not lenindef and l == 0:
2394 raise NotEnoughData(
2396 klass=self.__class__,
2397 decode_path=decode_path,
2401 sub_offset = offset + tlen + llen
2405 if v[:EOC_LEN].tobytes() == EOC:
2412 "chunk out of bounds",
2413 klass=self.__class__,
2414 decode_path=decode_path + (str(len(chunks) - 1),),
2415 offset=chunks[-1].offset,
2417 sub_decode_path = decode_path + (str(len(chunks)),)
2419 chunk, v_tail = BitString().decode(
2422 decode_path=sub_decode_path,
2425 _ctx_immutable=False,
2429 "expected BitString encoded chunk",
2430 klass=self.__class__,
2431 decode_path=sub_decode_path,
2434 chunks.append(chunk)
2435 sub_offset += chunk.tlvlen
2436 vlen += chunk.tlvlen
2438 if len(chunks) == 0:
2441 klass=self.__class__,
2442 decode_path=decode_path,
2447 for chunk_i, chunk in enumerate(chunks[:-1]):
2448 if chunk.bit_len % 8 != 0:
2450 "BitString chunk is not multiple of 8 bits",
2451 klass=self.__class__,
2452 decode_path=decode_path + (str(chunk_i),),
2453 offset=chunk.offset,
2455 values.append(bytes(chunk))
2456 bit_len += chunk.bit_len
2457 chunk_last = chunks[-1]
2458 values.append(bytes(chunk_last))
2459 bit_len += chunk_last.bit_len
2460 obj = self.__class__(
2461 value=(bit_len, b"".join(values)),
2464 default=self.default,
2465 optional=self.optional,
2467 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2469 obj.lenindef = lenindef
2470 obj.ber_encoded = True
2471 return obj, (v[EOC_LEN:] if lenindef else v)
2473 klass=self.__class__,
2474 decode_path=decode_path,
2479 return pp_console_row(next(self.pps()))
2481 def pps(self, decode_path=()):
2485 bit_len, blob = self._value
2486 value = "%d bits" % bit_len
2487 if len(self.specs) > 0:
2488 blob = tuple(self.named)
2491 asn1_type_name=self.asn1_type_name,
2492 obj_name=self.__class__.__name__,
2493 decode_path=decode_path,
2496 optional=self.optional,
2497 default=self == self.default,
2498 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2499 expl=None if self._expl is None else tag_decode(self._expl),
2504 expl_offset=self.expl_offset if self.expled else None,
2505 expl_tlen=self.expl_tlen if self.expled else None,
2506 expl_llen=self.expl_llen if self.expled else None,
2507 expl_vlen=self.expl_vlen if self.expled else None,
2508 expl_lenindef=self.expl_lenindef,
2509 lenindef=self.lenindef,
2510 ber_encoded=self.ber_encoded,
2513 defined_by, defined = self.defined or (None, None)
2514 if defined_by is not None:
2516 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2518 for pp in self.pps_lenindef(decode_path):
2522 class OctetString(Obj):
2523 """``OCTET STRING`` binary string type
2525 >>> s = OctetString(b"hello world")
2526 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2527 >>> s == OctetString(b"hello world")
2532 >>> OctetString(b"hello", bounds=(4, 4))
2533 Traceback (most recent call last):
2534 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2535 >>> OctetString(b"hell", bounds=(4, 4))
2536 OCTET STRING 4 bytes 68656c6c
2540 Pay attention that OCTET STRING can be encoded both in primitive
2541 and constructed forms. Decoder always checks constructed form tag
2542 additionally to specified primitive one. If BER decoding is
2543 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2544 of DER restrictions.
2546 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2547 tag_default = tag_encode(4)
2548 asn1_type_name = "OCTET STRING"
2561 :param value: set the value. Either binary type, or
2562 :py:class:`pyderasn.OctetString` object
2563 :param bounds: set ``(MIN, MAX)`` value size constraint.
2564 (-inf, +inf) by default
2565 :param bytes impl: override default tag with ``IMPLICIT`` one
2566 :param bytes expl: override default tag with ``EXPLICIT`` one
2567 :param default: set default value. Type same as in ``value``
2568 :param bool optional: is object ``OPTIONAL`` in sequence
2570 super(OctetString, self).__init__(
2578 self._bound_min, self._bound_max = getattr(
2582 ) if bounds is None else bounds
2583 if value is not None:
2584 self._value = self._value_sanitize(value)
2585 if default is not None:
2586 default = self._value_sanitize(default)
2587 self.default = self.__class__(
2592 if self._value is None:
2593 self._value = default
2595 tag_klass, _, tag_num = tag_decode(self.tag)
2596 self.tag_constructed = tag_encode(
2598 form=TagFormConstructed,
2602 def _value_sanitize(self, value):
2603 if issubclass(value.__class__, OctetString):
2604 value = value._value
2605 elif isinstance(value, binary_type):
2608 raise InvalidValueType((self.__class__, bytes))
2609 if not self._bound_min <= len(value) <= self._bound_max:
2610 raise BoundsError(self._bound_min, len(value), self._bound_max)
2615 return self._value is not None
2618 obj = self.__class__()
2619 obj._value = self._value
2620 obj._bound_min = self._bound_min
2621 obj._bound_max = self._bound_max
2623 obj._expl = self._expl
2624 obj.default = self.default
2625 obj.optional = self.optional
2626 obj.offset = self.offset
2627 obj.llen = self.llen
2628 obj.vlen = self.vlen
2629 obj.expl_lenindef = self.expl_lenindef
2630 obj.lenindef = self.lenindef
2631 obj.ber_encoded = self.ber_encoded
2634 def __bytes__(self):
2635 self._assert_ready()
2638 def __eq__(self, their):
2639 if isinstance(their, binary_type):
2640 return self._value == their
2641 if not issubclass(their.__class__, OctetString):
2644 self._value == their._value and
2645 self.tag == their.tag and
2646 self._expl == their._expl
2649 def __lt__(self, their):
2650 return self._value < their._value
2661 return self.__class__(
2664 (self._bound_min, self._bound_max)
2665 if bounds is None else bounds
2667 impl=self.tag if impl is None else impl,
2668 expl=self._expl if expl is None else expl,
2669 default=self.default if default is None else default,
2670 optional=self.optional if optional is None else optional,
2674 self._assert_ready()
2677 len_encode(len(self._value)),
2681 def _decode_chunk(self, lv, offset, decode_path, ctx):
2683 l, llen, v = len_decode(lv)
2684 except DecodeError as err:
2685 raise err.__class__(
2687 klass=self.__class__,
2688 decode_path=decode_path,
2692 raise NotEnoughData(
2693 "encoded length is longer than data",
2694 klass=self.__class__,
2695 decode_path=decode_path,
2698 v, tail = v[:l], v[l:]
2700 obj = self.__class__(
2702 bounds=(self._bound_min, self._bound_max),
2705 default=self.default,
2706 optional=self.optional,
2707 _decoded=(offset, llen, l),
2709 except DecodeError as err:
2712 klass=self.__class__,
2713 decode_path=decode_path,
2716 except BoundsError as err:
2719 klass=self.__class__,
2720 decode_path=decode_path,
2725 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2727 t, tlen, lv = tag_strip(tlv)
2728 except DecodeError as err:
2729 raise err.__class__(
2731 klass=self.__class__,
2732 decode_path=decode_path,
2738 return self._decode_chunk(lv, offset, decode_path, ctx)
2739 if t == self.tag_constructed:
2740 if not ctx.get("bered", False):
2742 "unallowed BER constructed encoding",
2743 klass=self.__class__,
2744 decode_path=decode_path,
2751 l, llen, v = len_decode(lv)
2752 except LenIndefForm:
2753 llen, l, v = 1, 0, lv[1:]
2755 except DecodeError as err:
2756 raise err.__class__(
2758 klass=self.__class__,
2759 decode_path=decode_path,
2763 raise NotEnoughData(
2764 "encoded length is longer than data",
2765 klass=self.__class__,
2766 decode_path=decode_path,
2770 sub_offset = offset + tlen + llen
2774 if v[:EOC_LEN].tobytes() == EOC:
2781 "chunk out of bounds",
2782 klass=self.__class__,
2783 decode_path=decode_path + (str(len(chunks) - 1),),
2784 offset=chunks[-1].offset,
2786 sub_decode_path = decode_path + (str(len(chunks)),)
2788 chunk, v_tail = OctetString().decode(
2791 decode_path=sub_decode_path,
2794 _ctx_immutable=False,
2798 "expected OctetString encoded chunk",
2799 klass=self.__class__,
2800 decode_path=sub_decode_path,
2803 chunks.append(chunk)
2804 sub_offset += chunk.tlvlen
2805 vlen += chunk.tlvlen
2808 obj = self.__class__(
2809 value=b"".join(bytes(chunk) for chunk in chunks),
2810 bounds=(self._bound_min, self._bound_max),
2813 default=self.default,
2814 optional=self.optional,
2815 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2817 except DecodeError as err:
2820 klass=self.__class__,
2821 decode_path=decode_path,
2824 except BoundsError as err:
2827 klass=self.__class__,
2828 decode_path=decode_path,
2831 obj.lenindef = lenindef
2832 obj.ber_encoded = True
2833 return obj, (v[EOC_LEN:] if lenindef else v)
2835 klass=self.__class__,
2836 decode_path=decode_path,
2841 return pp_console_row(next(self.pps()))
2843 def pps(self, decode_path=()):
2846 asn1_type_name=self.asn1_type_name,
2847 obj_name=self.__class__.__name__,
2848 decode_path=decode_path,
2849 value=("%d bytes" % len(self._value)) if self.ready else None,
2850 blob=self._value if self.ready else None,
2851 optional=self.optional,
2852 default=self == self.default,
2853 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2854 expl=None if self._expl is None else tag_decode(self._expl),
2859 expl_offset=self.expl_offset if self.expled else None,
2860 expl_tlen=self.expl_tlen if self.expled else None,
2861 expl_llen=self.expl_llen if self.expled else None,
2862 expl_vlen=self.expl_vlen if self.expled else None,
2863 expl_lenindef=self.expl_lenindef,
2864 lenindef=self.lenindef,
2865 ber_encoded=self.ber_encoded,
2868 defined_by, defined = self.defined or (None, None)
2869 if defined_by is not None:
2871 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2873 for pp in self.pps_lenindef(decode_path):
2878 """``NULL`` null object
2886 tag_default = tag_encode(5)
2887 asn1_type_name = "NULL"
2891 value=None, # unused, but Sequence passes it
2898 :param bytes impl: override default tag with ``IMPLICIT`` one
2899 :param bytes expl: override default tag with ``EXPLICIT`` one
2900 :param bool optional: is object ``OPTIONAL`` in sequence
2902 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2910 obj = self.__class__()
2912 obj._expl = self._expl
2913 obj.default = self.default
2914 obj.optional = self.optional
2915 obj.offset = self.offset
2916 obj.llen = self.llen
2917 obj.vlen = self.vlen
2918 obj.expl_lenindef = self.expl_lenindef
2919 obj.lenindef = self.lenindef
2920 obj.ber_encoded = self.ber_encoded
2923 def __eq__(self, their):
2924 if not issubclass(their.__class__, Null):
2927 self.tag == their.tag and
2928 self._expl == their._expl
2938 return self.__class__(
2939 impl=self.tag if impl is None else impl,
2940 expl=self._expl if expl is None else expl,
2941 optional=self.optional if optional is None else optional,
2945 return self.tag + len_encode(0)
2947 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2949 t, _, lv = tag_strip(tlv)
2950 except DecodeError as err:
2951 raise err.__class__(
2953 klass=self.__class__,
2954 decode_path=decode_path,
2959 klass=self.__class__,
2960 decode_path=decode_path,
2963 if tag_only: # pragma: no cover
2966 l, _, v = len_decode(lv)
2967 except DecodeError as err:
2968 raise err.__class__(
2970 klass=self.__class__,
2971 decode_path=decode_path,
2975 raise InvalidLength(
2976 "Null must have zero length",
2977 klass=self.__class__,
2978 decode_path=decode_path,
2981 obj = self.__class__(
2984 optional=self.optional,
2985 _decoded=(offset, 1, 0),
2990 return pp_console_row(next(self.pps()))
2992 def pps(self, decode_path=()):
2995 asn1_type_name=self.asn1_type_name,
2996 obj_name=self.__class__.__name__,
2997 decode_path=decode_path,
2998 optional=self.optional,
2999 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3000 expl=None if self._expl is None else tag_decode(self._expl),
3005 expl_offset=self.expl_offset if self.expled else None,
3006 expl_tlen=self.expl_tlen if self.expled else None,
3007 expl_llen=self.expl_llen if self.expled else None,
3008 expl_vlen=self.expl_vlen if self.expled else None,
3009 expl_lenindef=self.expl_lenindef,
3012 for pp in self.pps_lenindef(decode_path):
3016 class ObjectIdentifier(Obj):
3017 """``OBJECT IDENTIFIER`` OID type
3019 >>> oid = ObjectIdentifier((1, 2, 3))
3020 OBJECT IDENTIFIER 1.2.3
3021 >>> oid == ObjectIdentifier("1.2.3")
3027 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3028 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3030 >>> str(ObjectIdentifier((3, 1)))
3031 Traceback (most recent call last):
3032 pyderasn.InvalidOID: unacceptable first arc value
3034 __slots__ = ("defines",)
3035 tag_default = tag_encode(6)
3036 asn1_type_name = "OBJECT IDENTIFIER"
3049 :param value: set the value. Either tuples of integers,
3050 string of "."-concatenated integers, or
3051 :py:class:`pyderasn.ObjectIdentifier` object
3052 :param defines: sequence of tuples. Each tuple has two elements.
3053 First one is relative to current one decode
3054 path, aiming to the field defined by that OID.
3055 Read about relative path in
3056 :py:func:`pyderasn.abs_decode_path`. Second
3057 tuple element is ``{OID: pyderasn.Obj()}``
3058 dictionary, mapping between current OID value
3059 and structure applied to defined field.
3060 :ref:`Read about DEFINED BY <definedby>`
3061 :param bytes impl: override default tag with ``IMPLICIT`` one
3062 :param bytes expl: override default tag with ``EXPLICIT`` one
3063 :param default: set default value. Type same as in ``value``
3064 :param bool optional: is object ``OPTIONAL`` in sequence
3066 super(ObjectIdentifier, self).__init__(
3074 if value is not None:
3075 self._value = self._value_sanitize(value)
3076 if default is not None:
3077 default = self._value_sanitize(default)
3078 self.default = self.__class__(
3083 if self._value is None:
3084 self._value = default
3085 self.defines = defines
3087 def __add__(self, their):
3088 if isinstance(their, self.__class__):
3089 return self.__class__(self._value + their._value)
3090 if isinstance(their, tuple):
3091 return self.__class__(self._value + their)
3092 raise InvalidValueType((self.__class__, tuple))
3094 def _value_sanitize(self, value):
3095 if issubclass(value.__class__, ObjectIdentifier):
3097 if isinstance(value, string_types):
3099 value = tuple(int(arc) for arc in value.split("."))
3101 raise InvalidOID("unacceptable arcs values")
3102 if isinstance(value, tuple):
3104 raise InvalidOID("less than 2 arcs")
3105 first_arc = value[0]
3106 if first_arc in (0, 1):
3107 if not (0 <= value[1] <= 39):
3108 raise InvalidOID("second arc is too wide")
3109 elif first_arc == 2:
3112 raise InvalidOID("unacceptable first arc value")
3114 raise InvalidValueType((self.__class__, str, tuple))
3118 return self._value is not None
3121 obj = self.__class__()
3122 obj._value = self._value
3123 obj.defines = self.defines
3125 obj._expl = self._expl
3126 obj.default = self.default
3127 obj.optional = self.optional
3128 obj.offset = self.offset
3129 obj.llen = self.llen
3130 obj.vlen = self.vlen
3131 obj.expl_lenindef = self.expl_lenindef
3132 obj.lenindef = self.lenindef
3133 obj.ber_encoded = self.ber_encoded
3137 self._assert_ready()
3138 return iter(self._value)
3141 return ".".join(str(arc) for arc in self._value or ())
3144 self._assert_ready()
3147 bytes(self._expl or b"") +
3148 str(self._value).encode("ascii"),
3151 def __eq__(self, their):
3152 if isinstance(their, tuple):
3153 return self._value == their
3154 if not issubclass(their.__class__, ObjectIdentifier):
3157 self.tag == their.tag and
3158 self._expl == their._expl and
3159 self._value == their._value
3162 def __lt__(self, their):
3163 return self._value < their._value
3174 return self.__class__(
3176 defines=self.defines if defines is None else defines,
3177 impl=self.tag if impl is None else impl,
3178 expl=self._expl if expl is None else expl,
3179 default=self.default if default is None else default,
3180 optional=self.optional if optional is None else optional,
3184 self._assert_ready()
3186 first_value = value[1]
3187 first_arc = value[0]
3190 elif first_arc == 1:
3192 elif first_arc == 2:
3194 else: # pragma: no cover
3195 raise RuntimeError("invalid arc is stored")
3196 octets = [zero_ended_encode(first_value)]
3197 for arc in value[2:]:
3198 octets.append(zero_ended_encode(arc))
3199 v = b"".join(octets)
3200 return b"".join((self.tag, len_encode(len(v)), v))
3202 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3204 t, _, lv = tag_strip(tlv)
3205 except DecodeError as err:
3206 raise err.__class__(
3208 klass=self.__class__,
3209 decode_path=decode_path,
3214 klass=self.__class__,
3215 decode_path=decode_path,
3218 if tag_only: # pragma: no cover
3221 l, llen, v = len_decode(lv)
3222 except DecodeError as err:
3223 raise err.__class__(
3225 klass=self.__class__,
3226 decode_path=decode_path,
3230 raise NotEnoughData(
3231 "encoded length is longer than data",
3232 klass=self.__class__,
3233 decode_path=decode_path,
3237 raise NotEnoughData(
3239 klass=self.__class__,
3240 decode_path=decode_path,
3243 v, tail = v[:l], v[l:]
3250 octet = indexbytes(v, i)
3251 if i == 0 and octet == 0x80:
3252 if ctx.get("bered", False):
3255 raise DecodeError("non normalized arc encoding")
3256 arc = (arc << 7) | (octet & 0x7F)
3257 if octet & 0x80 == 0:
3265 klass=self.__class__,
3266 decode_path=decode_path,
3270 second_arc = arcs[0]
3271 if 0 <= second_arc <= 39:
3273 elif 40 <= second_arc <= 79:
3279 obj = self.__class__(
3280 value=tuple([first_arc, second_arc] + arcs[1:]),
3283 default=self.default,
3284 optional=self.optional,
3285 _decoded=(offset, llen, l),
3288 obj.ber_encoded = True
3292 return pp_console_row(next(self.pps()))
3294 def pps(self, decode_path=()):
3297 asn1_type_name=self.asn1_type_name,
3298 obj_name=self.__class__.__name__,
3299 decode_path=decode_path,
3300 value=str(self) if self.ready else None,
3301 optional=self.optional,
3302 default=self == self.default,
3303 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3304 expl=None if self._expl is None else tag_decode(self._expl),
3309 expl_offset=self.expl_offset if self.expled else None,
3310 expl_tlen=self.expl_tlen if self.expled else None,
3311 expl_llen=self.expl_llen if self.expled else None,
3312 expl_vlen=self.expl_vlen if self.expled else None,
3313 expl_lenindef=self.expl_lenindef,
3314 ber_encoded=self.ber_encoded,
3317 for pp in self.pps_lenindef(decode_path):
3321 class Enumerated(Integer):
3322 """``ENUMERATED`` integer type
3324 This type is identical to :py:class:`pyderasn.Integer`, but requires
3325 schema to be specified and does not accept values missing from it.
3328 tag_default = tag_encode(10)
3329 asn1_type_name = "ENUMERATED"
3340 bounds=None, # dummy argument, workability for Integer.decode
3342 super(Enumerated, self).__init__(
3351 if len(self.specs) == 0:
3352 raise ValueError("schema must be specified")
3354 def _value_sanitize(self, value):
3355 if isinstance(value, self.__class__):
3356 value = value._value
3357 elif isinstance(value, integer_types):
3358 for _value in self.specs.values():
3363 "unknown integer value: %s" % value,
3364 klass=self.__class__,
3366 elif isinstance(value, string_types):
3367 value = self.specs.get(value)
3369 raise ObjUnknown("integer value: %s" % value)
3371 raise InvalidValueType((self.__class__, int, str))
3375 obj = self.__class__(_specs=self.specs)
3376 obj._value = self._value
3377 obj._bound_min = self._bound_min
3378 obj._bound_max = self._bound_max
3380 obj._expl = self._expl
3381 obj.default = self.default
3382 obj.optional = self.optional
3383 obj.offset = self.offset
3384 obj.llen = self.llen
3385 obj.vlen = self.vlen
3386 obj.expl_lenindef = self.expl_lenindef
3387 obj.lenindef = self.lenindef
3388 obj.ber_encoded = self.ber_encoded
3400 return self.__class__(
3402 impl=self.tag if impl is None else impl,
3403 expl=self._expl if expl is None else expl,
3404 default=self.default if default is None else default,
3405 optional=self.optional if optional is None else optional,
3410 class CommonString(OctetString):
3411 """Common class for all strings
3413 Everything resembles :py:class:`pyderasn.OctetString`, except
3414 ability to deal with unicode text strings.
3416 >>> hexenc("привет мир".encode("utf-8"))
3417 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3418 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3420 >>> s = UTF8String("привет мир")
3421 UTF8String UTF8String привет мир
3423 'привет мир'
3424 >>> hexenc(bytes(s))
3425 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3427 >>> PrintableString("привет мир")
3428 Traceback (most recent call last):
3429 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3431 >>> BMPString("ада", bounds=(2, 2))
3432 Traceback (most recent call last):
3433 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3434 >>> s = BMPString("ад", bounds=(2, 2))
3437 >>> hexenc(bytes(s))
3445 * - :py:class:`pyderasn.UTF8String`
3447 * - :py:class:`pyderasn.NumericString`
3449 * - :py:class:`pyderasn.PrintableString`
3451 * - :py:class:`pyderasn.TeletexString`
3453 * - :py:class:`pyderasn.T61String`
3455 * - :py:class:`pyderasn.VideotexString`
3457 * - :py:class:`pyderasn.IA5String`
3459 * - :py:class:`pyderasn.GraphicString`
3461 * - :py:class:`pyderasn.VisibleString`
3463 * - :py:class:`pyderasn.ISO646String`
3465 * - :py:class:`pyderasn.GeneralString`
3467 * - :py:class:`pyderasn.UniversalString`
3469 * - :py:class:`pyderasn.BMPString`
3472 __slots__ = ("encoding",)
3474 def _value_sanitize(self, value):
3476 value_decoded = None
3477 if isinstance(value, self.__class__):
3478 value_raw = value._value
3479 elif isinstance(value, text_type):
3480 value_decoded = value
3481 elif isinstance(value, binary_type):
3484 raise InvalidValueType((self.__class__, text_type, binary_type))
3487 value_decoded.encode(self.encoding)
3488 if value_raw is None else value_raw
3491 value_raw.decode(self.encoding)
3492 if value_decoded is None else value_decoded
3494 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3495 raise DecodeError(str(err))
3496 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3504 def __eq__(self, their):
3505 if isinstance(their, binary_type):
3506 return self._value == their
3507 if isinstance(their, text_type):
3508 return self._value == their.encode(self.encoding)
3509 if not isinstance(their, self.__class__):
3512 self._value == their._value and
3513 self.tag == their.tag and
3514 self._expl == their._expl
3517 def __unicode__(self):
3519 return self._value.decode(self.encoding)
3520 return text_type(self._value)
3523 return pp_console_row(next(self.pps(no_unicode=PY2)))
3525 def pps(self, decode_path=(), no_unicode=False):
3528 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3531 asn1_type_name=self.asn1_type_name,
3532 obj_name=self.__class__.__name__,
3533 decode_path=decode_path,
3535 optional=self.optional,
3536 default=self == self.default,
3537 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3538 expl=None if self._expl is None else tag_decode(self._expl),
3543 expl_offset=self.expl_offset if self.expled else None,
3544 expl_tlen=self.expl_tlen if self.expled else None,
3545 expl_llen=self.expl_llen if self.expled else None,
3546 expl_vlen=self.expl_vlen if self.expled else None,
3547 expl_lenindef=self.expl_lenindef,
3548 ber_encoded=self.ber_encoded,
3551 for pp in self.pps_lenindef(decode_path):
3555 class UTF8String(CommonString):
3557 tag_default = tag_encode(12)
3559 asn1_type_name = "UTF8String"
3562 class AllowableCharsMixin(object):
3564 def allowable_chars(self):
3566 return self._allowable_chars
3567 return set(six_unichr(c) for c in self._allowable_chars)
3570 class NumericString(AllowableCharsMixin, CommonString):
3573 Its value is properly sanitized: only ASCII digits with spaces can
3576 >>> NumericString().allowable_chars
3577 set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3580 tag_default = tag_encode(18)
3582 asn1_type_name = "NumericString"
3583 _allowable_chars = set(digits.encode("ascii") + b" ")
3585 def _value_sanitize(self, value):
3586 value = super(NumericString, self)._value_sanitize(value)
3587 if not set(value) <= self._allowable_chars:
3588 raise DecodeError("non-numeric value")
3592 class PrintableString(AllowableCharsMixin, CommonString):
3595 Its value is properly sanitized: see X.680 41.4 table 10.
3597 >>> PrintableString().allowable_chars
3598 >>> set([' ', "'", ..., 'z'])
3601 tag_default = tag_encode(19)
3603 asn1_type_name = "PrintableString"
3604 _allowable_chars = set(
3605 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3608 def _value_sanitize(self, value):
3609 value = super(PrintableString, self)._value_sanitize(value)
3610 if not set(value) <= self._allowable_chars:
3611 raise DecodeError("non-printable value")
3615 class TeletexString(CommonString):
3617 tag_default = tag_encode(20)
3619 asn1_type_name = "TeletexString"
3622 class T61String(TeletexString):
3624 asn1_type_name = "T61String"
3627 class VideotexString(CommonString):
3629 tag_default = tag_encode(21)
3630 encoding = "iso-8859-1"
3631 asn1_type_name = "VideotexString"
3634 class IA5String(CommonString):
3636 tag_default = tag_encode(22)
3638 asn1_type_name = "IA5"
3641 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3642 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3643 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3646 class UTCTime(CommonString):
3647 """``UTCTime`` datetime type
3649 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3650 UTCTime UTCTime 2017-09-30T22:07:50
3656 datetime.datetime(2017, 9, 30, 22, 7, 50)
3657 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3658 datetime.datetime(1957, 9, 30, 22, 7, 50)
3661 tag_default = tag_encode(23)
3663 asn1_type_name = "UTCTime"
3665 fmt = "%y%m%d%H%M%SZ"
3675 bounds=None, # dummy argument, workability for OctetString.decode
3678 :param value: set the value. Either datetime type, or
3679 :py:class:`pyderasn.UTCTime` object
3680 :param bytes impl: override default tag with ``IMPLICIT`` one
3681 :param bytes expl: override default tag with ``EXPLICIT`` one
3682 :param default: set default value. Type same as in ``value``
3683 :param bool optional: is object ``OPTIONAL`` in sequence
3685 super(UTCTime, self).__init__(
3693 if value is not None:
3694 self._value = self._value_sanitize(value)
3695 if default is not None:
3696 default = self._value_sanitize(default)
3697 self.default = self.__class__(
3702 if self._value is None:
3703 self._value = default
3705 def _value_sanitize(self, value):
3706 if isinstance(value, self.__class__):
3708 if isinstance(value, datetime):
3709 return value.strftime(self.fmt).encode("ascii")
3710 if isinstance(value, binary_type):
3712 value_decoded = value.decode("ascii")
3713 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3714 raise DecodeError("invalid UTCTime encoding")
3715 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3717 datetime.strptime(value_decoded, self.fmt)
3718 except (TypeError, ValueError):
3719 raise DecodeError("invalid UTCTime format")
3722 raise DecodeError("invalid UTCTime length")
3723 raise InvalidValueType((self.__class__, datetime))
3725 def __eq__(self, their):
3726 if isinstance(their, binary_type):
3727 return self._value == their
3728 if isinstance(their, datetime):
3729 return self.todatetime() == their
3730 if not isinstance(their, self.__class__):
3733 self._value == their._value and
3734 self.tag == their.tag and
3735 self._expl == their._expl
3738 def todatetime(self):
3739 """Convert to datetime
3743 Pay attention that UTCTime can not hold full year, so all years
3744 having < 50 years are treated as 20xx, 19xx otherwise, according
3745 to X.509 recomendation.
3747 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3748 year = value.year % 100
3750 year=(2000 + year) if year < 50 else (1900 + year),
3754 minute=value.minute,
3755 second=value.second,
3759 return pp_console_row(next(self.pps()))
3761 def pps(self, decode_path=()):
3764 asn1_type_name=self.asn1_type_name,
3765 obj_name=self.__class__.__name__,
3766 decode_path=decode_path,
3767 value=self.todatetime().isoformat() if self.ready else None,
3768 optional=self.optional,
3769 default=self == self.default,
3770 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3771 expl=None if self._expl is None else tag_decode(self._expl),
3776 expl_offset=self.expl_offset if self.expled else None,
3777 expl_tlen=self.expl_tlen if self.expled else None,
3778 expl_llen=self.expl_llen if self.expled else None,
3779 expl_vlen=self.expl_vlen if self.expled else None,
3780 expl_lenindef=self.expl_lenindef,
3781 ber_encoded=self.ber_encoded,
3784 for pp in self.pps_lenindef(decode_path):
3788 class GeneralizedTime(UTCTime):
3789 """``GeneralizedTime`` datetime type
3791 This type is similar to :py:class:`pyderasn.UTCTime`.
3793 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3794 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3796 '20170930220750.000123Z'
3797 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3798 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3801 tag_default = tag_encode(24)
3802 asn1_type_name = "GeneralizedTime"
3804 fmt = "%Y%m%d%H%M%SZ"
3805 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3807 def _value_sanitize(self, value):
3808 if isinstance(value, self.__class__):
3810 if isinstance(value, datetime):
3811 return value.strftime(
3812 self.fmt_ms if value.microsecond > 0 else self.fmt
3814 if isinstance(value, binary_type):
3816 value_decoded = value.decode("ascii")
3817 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3818 raise DecodeError("invalid GeneralizedTime encoding")
3819 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3821 datetime.strptime(value_decoded, self.fmt)
3822 except (TypeError, ValueError):
3824 "invalid GeneralizedTime (without ms) format",
3827 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3829 datetime.strptime(value_decoded, self.fmt_ms)
3830 except (TypeError, ValueError):
3832 "invalid GeneralizedTime (with ms) format",
3837 "invalid GeneralizedTime length",
3838 klass=self.__class__,
3840 raise InvalidValueType((self.__class__, datetime))
3842 def todatetime(self):
3843 value = self._value.decode("ascii")
3844 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3845 return datetime.strptime(value, self.fmt)
3846 return datetime.strptime(value, self.fmt_ms)
3849 class GraphicString(CommonString):
3851 tag_default = tag_encode(25)
3852 encoding = "iso-8859-1"
3853 asn1_type_name = "GraphicString"
3856 class VisibleString(CommonString):
3858 tag_default = tag_encode(26)
3860 asn1_type_name = "VisibleString"
3863 class ISO646String(VisibleString):
3865 asn1_type_name = "ISO646String"
3868 class GeneralString(CommonString):
3870 tag_default = tag_encode(27)
3871 encoding = "iso-8859-1"
3872 asn1_type_name = "GeneralString"
3875 class UniversalString(CommonString):
3877 tag_default = tag_encode(28)
3878 encoding = "utf-32-be"
3879 asn1_type_name = "UniversalString"
3882 class BMPString(CommonString):
3884 tag_default = tag_encode(30)
3885 encoding = "utf-16-be"
3886 asn1_type_name = "BMPString"
3890 """``CHOICE`` special type
3894 class GeneralName(Choice):
3896 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3897 ("dNSName", IA5String(impl=tag_ctxp(2))),
3900 >>> gn = GeneralName()
3902 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3903 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3904 >>> gn["dNSName"] = IA5String("bar.baz")
3905 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3906 >>> gn["rfc822Name"]
3909 [2] IA5String IA5 bar.baz
3912 >>> gn.value == gn["dNSName"]
3915 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3917 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3918 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3920 __slots__ = ("specs",)
3922 asn1_type_name = "CHOICE"
3935 :param value: set the value. Either ``(choice, value)`` tuple, or
3936 :py:class:`pyderasn.Choice` object
3937 :param bytes impl: can not be set, do **not** use it
3938 :param bytes expl: override default tag with ``EXPLICIT`` one
3939 :param default: set default value. Type same as in ``value``
3940 :param bool optional: is object ``OPTIONAL`` in sequence
3942 if impl is not None:
3943 raise ValueError("no implicit tag allowed for CHOICE")
3944 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3946 schema = getattr(self, "schema", ())
3947 if len(schema) == 0:
3948 raise ValueError("schema must be specified")
3950 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3953 if value is not None:
3954 self._value = self._value_sanitize(value)
3955 if default is not None:
3956 default_value = self._value_sanitize(default)
3957 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3958 default_obj.specs = self.specs
3959 default_obj._value = default_value
3960 self.default = default_obj
3962 self._value = default_obj.copy()._value
3964 def _value_sanitize(self, value):
3965 if isinstance(value, self.__class__):
3967 if isinstance(value, tuple) and len(value) == 2:
3969 spec = self.specs.get(choice)
3971 raise ObjUnknown(choice)
3972 if not isinstance(obj, spec.__class__):
3973 raise InvalidValueType((spec,))
3974 return (choice, spec(obj))
3975 raise InvalidValueType((self.__class__, tuple))
3979 return self._value is not None and self._value[1].ready
3983 return self.expl_lenindef or (
3984 (self._value is not None) and
3985 self._value[1].bered
3989 obj = self.__class__(schema=self.specs)
3990 obj._expl = self._expl
3991 obj.default = self.default
3992 obj.optional = self.optional
3993 obj.offset = self.offset
3994 obj.llen = self.llen
3995 obj.vlen = self.vlen
3996 obj.expl_lenindef = self.expl_lenindef
3997 obj.lenindef = self.lenindef
3998 obj.ber_encoded = self.ber_encoded
4000 if value is not None:
4001 obj._value = (value[0], value[1].copy())
4004 def __eq__(self, their):
4005 if isinstance(their, tuple) and len(their) == 2:
4006 return self._value == their
4007 if not isinstance(their, self.__class__):
4010 self.specs == their.specs and
4011 self._value == their._value
4021 return self.__class__(
4024 expl=self._expl if expl is None else expl,
4025 default=self.default if default is None else default,
4026 optional=self.optional if optional is None else optional,
4031 self._assert_ready()
4032 return self._value[0]
4036 self._assert_ready()
4037 return self._value[1]
4039 def __getitem__(self, key):
4040 if key not in self.specs:
4041 raise ObjUnknown(key)
4042 if self._value is None:
4044 choice, value = self._value
4049 def __setitem__(self, key, value):
4050 spec = self.specs.get(key)
4052 raise ObjUnknown(key)
4053 if not isinstance(value, spec.__class__):
4054 raise InvalidValueType((spec.__class__,))
4055 self._value = (key, spec(value))
4063 return self._value[1].decoded if self.ready else False
4066 self._assert_ready()
4067 return self._value[1].encode()
4069 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4070 for choice, spec in self.specs.items():
4071 sub_decode_path = decode_path + (choice,)
4077 decode_path=sub_decode_path,
4080 _ctx_immutable=False,
4087 klass=self.__class__,
4088 decode_path=decode_path,
4091 if tag_only: # pragma: no cover
4093 value, tail = spec.decode(
4097 decode_path=sub_decode_path,
4099 _ctx_immutable=False,
4101 obj = self.__class__(
4104 default=self.default,
4105 optional=self.optional,
4106 _decoded=(offset, 0, value.fulllen),
4108 obj._value = (choice, value)
4112 value = pp_console_row(next(self.pps()))
4114 value = "%s[%r]" % (value, self.value)
4117 def pps(self, decode_path=()):
4120 asn1_type_name=self.asn1_type_name,
4121 obj_name=self.__class__.__name__,
4122 decode_path=decode_path,
4123 value=self.choice if self.ready else None,
4124 optional=self.optional,
4125 default=self == self.default,
4126 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4127 expl=None if self._expl is None else tag_decode(self._expl),
4132 expl_lenindef=self.expl_lenindef,
4136 yield self.value.pps(decode_path=decode_path + (self.choice,))
4137 for pp in self.pps_lenindef(decode_path):
4141 class PrimitiveTypes(Choice):
4142 """Predefined ``CHOICE`` for all generic primitive types
4144 It could be useful for general decoding of some unspecified values:
4146 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4147 OCTET STRING 3 bytes 666f6f
4148 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4152 schema = tuple((klass.__name__, klass()) for klass in (
4177 """``ANY`` special type
4179 >>> Any(Integer(-123))
4181 >>> a = Any(OctetString(b"hello world").encode())
4182 ANY 040b68656c6c6f20776f726c64
4183 >>> hexenc(bytes(a))
4184 b'0x040x0bhello world'
4186 __slots__ = ("defined",)
4187 tag_default = tag_encode(0)
4188 asn1_type_name = "ANY"
4198 :param value: set the value. Either any kind of pyderasn's
4199 **ready** object, or bytes. Pay attention that
4200 **no** validation is performed is raw binary value
4202 :param bytes expl: override default tag with ``EXPLICIT`` one
4203 :param bool optional: is object ``OPTIONAL`` in sequence
4205 super(Any, self).__init__(None, expl, None, optional, _decoded)
4206 self._value = None if value is None else self._value_sanitize(value)
4209 def _value_sanitize(self, value):
4210 if isinstance(value, self.__class__):
4212 if isinstance(value, Obj):
4213 return value.encode()
4214 if isinstance(value, binary_type):
4216 raise InvalidValueType((self.__class__, Obj, binary_type))
4220 return self._value is not None
4224 if self.expl_lenindef or self.lenindef:
4226 if self.defined is None:
4228 return self.defined[1].bered
4231 obj = self.__class__()
4232 obj._value = self._value
4234 obj._expl = self._expl
4235 obj.optional = self.optional
4236 obj.offset = self.offset
4237 obj.llen = self.llen
4238 obj.vlen = self.vlen
4239 obj.expl_lenindef = self.expl_lenindef
4240 obj.lenindef = self.lenindef
4241 obj.ber_encoded = self.ber_encoded
4244 def __eq__(self, their):
4245 if isinstance(their, binary_type):
4246 return self._value == their
4247 if issubclass(their.__class__, Any):
4248 return self._value == their._value
4257 return self.__class__(
4259 expl=self._expl if expl is None else expl,
4260 optional=self.optional if optional is None else optional,
4263 def __bytes__(self):
4264 self._assert_ready()
4272 self._assert_ready()
4275 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4277 t, tlen, lv = tag_strip(tlv)
4278 except DecodeError as err:
4279 raise err.__class__(
4281 klass=self.__class__,
4282 decode_path=decode_path,
4286 l, llen, v = len_decode(lv)
4287 except LenIndefForm as err:
4288 if not ctx.get("bered", False):
4289 raise err.__class__(
4291 klass=self.__class__,
4292 decode_path=decode_path,
4295 llen, vlen, v = 1, 0, lv[1:]
4296 sub_offset = offset + tlen + llen
4298 while v[:EOC_LEN].tobytes() != EOC:
4299 chunk, v = Any().decode(
4302 decode_path=decode_path + (str(chunk_i),),
4305 _ctx_immutable=False,
4307 vlen += chunk.tlvlen
4308 sub_offset += chunk.tlvlen
4310 tlvlen = tlen + llen + vlen + EOC_LEN
4311 obj = self.__class__(
4312 value=tlv[:tlvlen].tobytes(),
4314 optional=self.optional,
4315 _decoded=(offset, 0, tlvlen),
4319 return obj, v[EOC_LEN:]
4320 except DecodeError as err:
4321 raise err.__class__(
4323 klass=self.__class__,
4324 decode_path=decode_path,
4328 raise NotEnoughData(
4329 "encoded length is longer than data",
4330 klass=self.__class__,
4331 decode_path=decode_path,
4334 tlvlen = tlen + llen + l
4335 v, tail = tlv[:tlvlen], v[l:]
4336 obj = self.__class__(
4339 optional=self.optional,
4340 _decoded=(offset, 0, tlvlen),
4346 return pp_console_row(next(self.pps()))
4348 def pps(self, decode_path=()):
4351 asn1_type_name=self.asn1_type_name,
4352 obj_name=self.__class__.__name__,
4353 decode_path=decode_path,
4354 blob=self._value if self.ready else None,
4355 optional=self.optional,
4356 default=self == self.default,
4357 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4358 expl=None if self._expl is None else tag_decode(self._expl),
4363 expl_offset=self.expl_offset if self.expled else None,
4364 expl_tlen=self.expl_tlen if self.expled else None,
4365 expl_llen=self.expl_llen if self.expled else None,
4366 expl_vlen=self.expl_vlen if self.expled else None,
4367 expl_lenindef=self.expl_lenindef,
4368 lenindef=self.lenindef,
4371 defined_by, defined = self.defined or (None, None)
4372 if defined_by is not None:
4374 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4376 for pp in self.pps_lenindef(decode_path):
4380 ########################################################################
4381 # ASN.1 constructed types
4382 ########################################################################
4384 def get_def_by_path(defines_by_path, sub_decode_path):
4385 """Get define by decode path
4387 for path, define in defines_by_path:
4388 if len(path) != len(sub_decode_path):
4390 for p1, p2 in zip(path, sub_decode_path):
4391 if (p1 != any) and (p1 != p2):
4397 def abs_decode_path(decode_path, rel_path):
4398 """Create an absolute decode path from current and relative ones
4400 :param decode_path: current decode path, starting point. Tuple of strings
4401 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4402 If first tuple's element is "/", then treat it as
4403 an absolute path, ignoring ``decode_path`` as
4404 starting point. Also this tuple can contain ".."
4405 elements, stripping the leading element from
4408 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4409 ("foo", "bar", "baz", "whatever")
4410 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4412 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4415 if rel_path[0] == "/":
4417 if rel_path[0] == "..":
4418 return abs_decode_path(decode_path[:-1], rel_path[1:])
4419 return decode_path + rel_path
4422 class Sequence(Obj):
4423 """``SEQUENCE`` structure type
4425 You have to make specification of sequence::
4427 class Extension(Sequence):
4429 ("extnID", ObjectIdentifier()),
4430 ("critical", Boolean(default=False)),
4431 ("extnValue", OctetString()),
4434 Then, you can work with it as with dictionary.
4436 >>> ext = Extension()
4437 >>> Extension().specs
4439 ('extnID', OBJECT IDENTIFIER),
4440 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4441 ('extnValue', OCTET STRING),
4443 >>> ext["extnID"] = "1.2.3"
4444 Traceback (most recent call last):
4445 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4446 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4448 You can determine if sequence is ready to be encoded:
4453 Traceback (most recent call last):
4454 pyderasn.ObjNotReady: object is not ready: extnValue
4455 >>> ext["extnValue"] = OctetString(b"foobar")
4459 Value you want to assign, must have the same **type** as in
4460 corresponding specification, but it can have different tags,
4461 optional/default attributes -- they will be taken from specification
4464 class TBSCertificate(Sequence):
4466 ("version", Version(expl=tag_ctxc(0), default="v1")),
4469 >>> tbs = TBSCertificate()
4470 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4472 Assign ``None`` to remove value from sequence.
4474 You can set values in Sequence during its initialization:
4476 >>> AlgorithmIdentifier((
4477 ("algorithm", ObjectIdentifier("1.2.3")),
4478 ("parameters", Any(Null()))
4480 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4482 You can determine if value exists/set in the sequence and take its value:
4484 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4487 OBJECT IDENTIFIER 1.2.3
4489 But pay attention that if value has default, then it won't be (not
4490 in) in the sequence (because ``DEFAULT`` must not be encoded in
4491 DER), but you can read its value:
4493 >>> "critical" in ext, ext["critical"]
4494 (False, BOOLEAN False)
4495 >>> ext["critical"] = Boolean(True)
4496 >>> "critical" in ext, ext["critical"]
4497 (True, BOOLEAN True)
4499 All defaulted values are always optional.
4501 .. _allow_default_values_ctx:
4503 DER prohibits default value encoding and will raise an error if
4504 default value is unexpectedly met during decode.
4505 If :ref:`bered <bered_ctx>` context option is set, then no error
4506 will be raised, but ``bered`` attribute set. You can disable strict
4507 defaulted values existence validation by setting
4508 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4510 Two sequences are equal if they have equal specification (schema),
4511 implicit/explicit tagging and the same values.
4513 __slots__ = ("specs",)
4514 tag_default = tag_encode(form=TagFormConstructed, num=16)
4515 asn1_type_name = "SEQUENCE"
4527 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4529 schema = getattr(self, "schema", ())
4531 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4534 if value is not None:
4535 if issubclass(value.__class__, Sequence):
4536 self._value = value._value
4537 elif hasattr(value, "__iter__"):
4538 for seq_key, seq_value in value:
4539 self[seq_key] = seq_value
4541 raise InvalidValueType((Sequence,))
4542 if default is not None:
4543 if not issubclass(default.__class__, Sequence):
4544 raise InvalidValueType((Sequence,))
4545 default_value = default._value
4546 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4547 default_obj.specs = self.specs
4548 default_obj._value = default_value
4549 self.default = default_obj
4551 self._value = default_obj.copy()._value
4555 for name, spec in self.specs.items():
4556 value = self._value.get(name)
4568 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4570 return any(value.bered for value in self._value.values())
4573 obj = self.__class__(schema=self.specs)
4575 obj._expl = self._expl
4576 obj.default = self.default
4577 obj.optional = self.optional
4578 obj.offset = self.offset
4579 obj.llen = self.llen
4580 obj.vlen = self.vlen
4581 obj.expl_lenindef = self.expl_lenindef
4582 obj.lenindef = self.lenindef
4583 obj.ber_encoded = self.ber_encoded
4584 obj._value = {k: v.copy() for k, v in self._value.items()}
4587 def __eq__(self, their):
4588 if not isinstance(their, self.__class__):
4591 self.specs == their.specs and
4592 self.tag == their.tag and
4593 self._expl == their._expl and
4594 self._value == their._value
4605 return self.__class__(
4608 impl=self.tag if impl is None else impl,
4609 expl=self._expl if expl is None else expl,
4610 default=self.default if default is None else default,
4611 optional=self.optional if optional is None else optional,
4614 def __contains__(self, key):
4615 return key in self._value
4617 def __setitem__(self, key, value):
4618 spec = self.specs.get(key)
4620 raise ObjUnknown(key)
4622 self._value.pop(key, None)
4624 if not isinstance(value, spec.__class__):
4625 raise InvalidValueType((spec.__class__,))
4626 value = spec(value=value)
4627 if spec.default is not None and value == spec.default:
4628 self._value.pop(key, None)
4630 self._value[key] = value
4632 def __getitem__(self, key):
4633 value = self._value.get(key)
4634 if value is not None:
4636 spec = self.specs.get(key)
4638 raise ObjUnknown(key)
4639 if spec.default is not None:
4643 def _encoded_values(self):
4645 for name, spec in self.specs.items():
4646 value = self._value.get(name)
4650 raise ObjNotReady(name)
4651 raws.append(value.encode())
4655 v = b"".join(self._encoded_values())
4656 return b"".join((self.tag, len_encode(len(v)), v))
4658 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4660 t, tlen, lv = tag_strip(tlv)
4661 except DecodeError as err:
4662 raise err.__class__(
4664 klass=self.__class__,
4665 decode_path=decode_path,
4670 klass=self.__class__,
4671 decode_path=decode_path,
4674 if tag_only: # pragma: no cover
4677 ctx_bered = ctx.get("bered", False)
4679 l, llen, v = len_decode(lv)
4680 except LenIndefForm as err:
4682 raise err.__class__(
4684 klass=self.__class__,
4685 decode_path=decode_path,
4688 l, llen, v = 0, 1, lv[1:]
4690 except DecodeError as err:
4691 raise err.__class__(
4693 klass=self.__class__,
4694 decode_path=decode_path,
4698 raise NotEnoughData(
4699 "encoded length is longer than data",
4700 klass=self.__class__,
4701 decode_path=decode_path,
4705 v, tail = v[:l], v[l:]
4707 sub_offset = offset + tlen + llen
4710 ctx_allow_default_values = ctx.get("allow_default_values", False)
4711 for name, spec in self.specs.items():
4712 if spec.optional and (
4713 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4717 sub_decode_path = decode_path + (name,)
4719 value, v_tail = spec.decode(
4723 decode_path=sub_decode_path,
4725 _ctx_immutable=False,
4732 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4733 if defined is not None:
4734 defined_by, defined_spec = defined
4735 if issubclass(value.__class__, SequenceOf):
4736 for i, _value in enumerate(value):
4737 sub_sub_decode_path = sub_decode_path + (
4739 DecodePathDefBy(defined_by),
4741 defined_value, defined_tail = defined_spec.decode(
4742 memoryview(bytes(_value)),
4744 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4745 if value.expled else (value.tlen + value.llen)
4748 decode_path=sub_sub_decode_path,
4750 _ctx_immutable=False,
4752 if len(defined_tail) > 0:
4755 klass=self.__class__,
4756 decode_path=sub_sub_decode_path,
4759 _value.defined = (defined_by, defined_value)
4761 defined_value, defined_tail = defined_spec.decode(
4762 memoryview(bytes(value)),
4764 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4765 if value.expled else (value.tlen + value.llen)
4768 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4770 _ctx_immutable=False,
4772 if len(defined_tail) > 0:
4775 klass=self.__class__,
4776 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4779 value.defined = (defined_by, defined_value)
4781 value_len = value.fulllen
4783 sub_offset += value_len
4785 if spec.default is not None and value == spec.default:
4786 if ctx_bered or ctx_allow_default_values:
4790 "DEFAULT value met",
4791 klass=self.__class__,
4792 decode_path=sub_decode_path,
4795 values[name] = value
4797 spec_defines = getattr(spec, "defines", ())
4798 if len(spec_defines) == 0:
4799 defines_by_path = ctx.get("defines_by_path", ())
4800 if len(defines_by_path) > 0:
4801 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4802 if spec_defines is not None and len(spec_defines) > 0:
4803 for rel_path, schema in spec_defines:
4804 defined = schema.get(value, None)
4805 if defined is not None:
4806 ctx.setdefault("_defines", []).append((
4807 abs_decode_path(sub_decode_path[:-1], rel_path),
4811 if v[:EOC_LEN].tobytes() != EOC:
4814 klass=self.__class__,
4815 decode_path=decode_path,
4823 klass=self.__class__,
4824 decode_path=decode_path,
4827 obj = self.__class__(
4831 default=self.default,
4832 optional=self.optional,
4833 _decoded=(offset, llen, vlen),
4836 obj.lenindef = lenindef
4837 obj.ber_encoded = ber_encoded
4841 value = pp_console_row(next(self.pps()))
4843 for name in self.specs:
4844 _value = self._value.get(name)
4847 cols.append("%s: %s" % (name, repr(_value)))
4848 return "%s[%s]" % (value, "; ".join(cols))
4850 def pps(self, decode_path=()):
4853 asn1_type_name=self.asn1_type_name,
4854 obj_name=self.__class__.__name__,
4855 decode_path=decode_path,
4856 optional=self.optional,
4857 default=self == self.default,
4858 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4859 expl=None if self._expl is None else tag_decode(self._expl),
4864 expl_offset=self.expl_offset if self.expled else None,
4865 expl_tlen=self.expl_tlen if self.expled else None,
4866 expl_llen=self.expl_llen if self.expled else None,
4867 expl_vlen=self.expl_vlen if self.expled else None,
4868 expl_lenindef=self.expl_lenindef,
4869 lenindef=self.lenindef,
4870 ber_encoded=self.ber_encoded,
4873 for name in self.specs:
4874 value = self._value.get(name)
4877 yield value.pps(decode_path=decode_path + (name,))
4878 for pp in self.pps_lenindef(decode_path):
4882 class Set(Sequence):
4883 """``SET`` structure type
4885 Its usage is identical to :py:class:`pyderasn.Sequence`.
4887 .. _allow_unordered_set_ctx:
4889 DER prohibits unordered values encoding and will raise an error
4890 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4891 then no error will occure. Also you can disable strict values
4892 ordering check by setting ``"allow_unordered_set": True``
4893 :ref:`context <ctx>` option.
4896 tag_default = tag_encode(form=TagFormConstructed, num=17)
4897 asn1_type_name = "SET"
4900 raws = self._encoded_values()
4903 return b"".join((self.tag, len_encode(len(v)), v))
4905 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4907 t, tlen, lv = tag_strip(tlv)
4908 except DecodeError as err:
4909 raise err.__class__(
4911 klass=self.__class__,
4912 decode_path=decode_path,
4917 klass=self.__class__,
4918 decode_path=decode_path,
4924 ctx_bered = ctx.get("bered", False)
4926 l, llen, v = len_decode(lv)
4927 except LenIndefForm as err:
4929 raise err.__class__(
4931 klass=self.__class__,
4932 decode_path=decode_path,
4935 l, llen, v = 0, 1, lv[1:]
4937 except DecodeError as err:
4938 raise err.__class__(
4940 klass=self.__class__,
4941 decode_path=decode_path,
4945 raise NotEnoughData(
4946 "encoded length is longer than data",
4947 klass=self.__class__,
4951 v, tail = v[:l], v[l:]
4953 sub_offset = offset + tlen + llen
4956 ctx_allow_default_values = ctx.get("allow_default_values", False)
4957 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4958 value_prev = memoryview(v[:0])
4959 specs_items = self.specs.items
4961 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4963 for name, spec in specs_items():
4964 sub_decode_path = decode_path + (name,)
4970 decode_path=sub_decode_path,
4973 _ctx_immutable=False,
4980 klass=self.__class__,
4981 decode_path=decode_path,
4984 value, v_tail = spec.decode(
4988 decode_path=sub_decode_path,
4990 _ctx_immutable=False,
4992 value_len = value.fulllen
4993 if value_prev.tobytes() > v[:value_len].tobytes():
4994 if ctx_bered or ctx_allow_unordered_set:
4998 "unordered " + self.asn1_type_name,
4999 klass=self.__class__,
5000 decode_path=sub_decode_path,
5003 if spec.default is None or value != spec.default:
5005 elif ctx_bered or ctx_allow_default_values:
5009 "DEFAULT value met",
5010 klass=self.__class__,
5011 decode_path=sub_decode_path,
5014 values[name] = value
5015 value_prev = v[:value_len]
5016 sub_offset += value_len
5019 obj = self.__class__(
5023 default=self.default,
5024 optional=self.optional,
5025 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5028 if v[:EOC_LEN].tobytes() != EOC:
5031 klass=self.__class__,
5032 decode_path=decode_path,
5040 "not all values are ready",
5041 klass=self.__class__,
5042 decode_path=decode_path,
5045 obj.ber_encoded = ber_encoded
5049 class SequenceOf(Obj):
5050 """``SEQUENCE OF`` sequence type
5052 For that kind of type you must specify the object it will carry on
5053 (bounds are for example here, not required)::
5055 class Ints(SequenceOf):
5060 >>> ints.append(Integer(123))
5061 >>> ints.append(Integer(234))
5063 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5064 >>> [int(i) for i in ints]
5066 >>> ints.append(Integer(345))
5067 Traceback (most recent call last):
5068 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5071 >>> ints[1] = Integer(345)
5073 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5075 Also you can initialize sequence with preinitialized values:
5077 >>> ints = Ints([Integer(123), Integer(234)])
5079 __slots__ = ("spec", "_bound_min", "_bound_max")
5080 tag_default = tag_encode(form=TagFormConstructed, num=16)
5081 asn1_type_name = "SEQUENCE OF"
5094 super(SequenceOf, self).__init__(
5102 schema = getattr(self, "schema", None)
5104 raise ValueError("schema must be specified")
5106 self._bound_min, self._bound_max = getattr(
5110 ) if bounds is None else bounds
5112 if value is not None:
5113 self._value = self._value_sanitize(value)
5114 if default is not None:
5115 default_value = self._value_sanitize(default)
5116 default_obj = self.__class__(
5121 default_obj._value = default_value
5122 self.default = default_obj
5124 self._value = default_obj.copy()._value
5126 def _value_sanitize(self, value):
5127 if issubclass(value.__class__, SequenceOf):
5128 value = value._value
5129 elif hasattr(value, "__iter__"):
5132 raise InvalidValueType((self.__class__, iter))
5133 if not self._bound_min <= len(value) <= self._bound_max:
5134 raise BoundsError(self._bound_min, len(value), self._bound_max)
5136 if not isinstance(v, self.spec.__class__):
5137 raise InvalidValueType((self.spec.__class__,))
5142 return all(v.ready for v in self._value)
5146 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5148 return any(v.bered for v in self._value)
5151 obj = self.__class__(schema=self.spec)
5152 obj._bound_min = self._bound_min
5153 obj._bound_max = self._bound_max
5155 obj._expl = self._expl
5156 obj.default = self.default
5157 obj.optional = self.optional
5158 obj.offset = self.offset
5159 obj.llen = self.llen
5160 obj.vlen = self.vlen
5161 obj.expl_lenindef = self.expl_lenindef
5162 obj.lenindef = self.lenindef
5163 obj.ber_encoded = self.ber_encoded
5164 obj._value = [v.copy() for v in self._value]
5167 def __eq__(self, their):
5168 if isinstance(their, self.__class__):
5170 self.spec == their.spec and
5171 self.tag == their.tag and
5172 self._expl == their._expl and
5173 self._value == their._value
5175 if hasattr(their, "__iter__"):
5176 return self._value == list(their)
5188 return self.__class__(
5192 (self._bound_min, self._bound_max)
5193 if bounds is None else bounds
5195 impl=self.tag if impl is None else impl,
5196 expl=self._expl if expl is None else expl,
5197 default=self.default if default is None else default,
5198 optional=self.optional if optional is None else optional,
5201 def __contains__(self, key):
5202 return key in self._value
5204 def append(self, value):
5205 if not isinstance(value, self.spec.__class__):
5206 raise InvalidValueType((self.spec.__class__,))
5207 if len(self._value) + 1 > self._bound_max:
5210 len(self._value) + 1,
5213 self._value.append(value)
5216 self._assert_ready()
5217 return iter(self._value)
5220 self._assert_ready()
5221 return len(self._value)
5223 def __setitem__(self, key, value):
5224 if not isinstance(value, self.spec.__class__):
5225 raise InvalidValueType((self.spec.__class__,))
5226 self._value[key] = self.spec(value=value)
5228 def __getitem__(self, key):
5229 return self._value[key]
5231 def _encoded_values(self):
5232 return [v.encode() for v in self._value]
5235 v = b"".join(self._encoded_values())
5236 return b"".join((self.tag, len_encode(len(v)), v))
5238 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5240 t, tlen, lv = tag_strip(tlv)
5241 except DecodeError as err:
5242 raise err.__class__(
5244 klass=self.__class__,
5245 decode_path=decode_path,
5250 klass=self.__class__,
5251 decode_path=decode_path,
5257 ctx_bered = ctx.get("bered", False)
5259 l, llen, v = len_decode(lv)
5260 except LenIndefForm as err:
5262 raise err.__class__(
5264 klass=self.__class__,
5265 decode_path=decode_path,
5268 l, llen, v = 0, 1, lv[1:]
5270 except DecodeError as err:
5271 raise err.__class__(
5273 klass=self.__class__,
5274 decode_path=decode_path,
5278 raise NotEnoughData(
5279 "encoded length is longer than data",
5280 klass=self.__class__,
5281 decode_path=decode_path,
5285 v, tail = v[:l], v[l:]
5287 sub_offset = offset + tlen + llen
5289 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5290 value_prev = memoryview(v[:0])
5294 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5296 sub_decode_path = decode_path + (str(len(_value)),)
5297 value, v_tail = spec.decode(
5301 decode_path=sub_decode_path,
5303 _ctx_immutable=False,
5305 value_len = value.fulllen
5307 if value_prev.tobytes() > v[:value_len].tobytes():
5308 if ctx_bered or ctx_allow_unordered_set:
5312 "unordered " + self.asn1_type_name,
5313 klass=self.__class__,
5314 decode_path=sub_decode_path,
5317 value_prev = v[:value_len]
5318 _value.append(value)
5319 sub_offset += value_len
5323 obj = self.__class__(
5326 bounds=(self._bound_min, self._bound_max),
5329 default=self.default,
5330 optional=self.optional,
5331 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5333 except BoundsError as err:
5336 klass=self.__class__,
5337 decode_path=decode_path,
5341 if v[:EOC_LEN].tobytes() != EOC:
5344 klass=self.__class__,
5345 decode_path=decode_path,
5350 obj.ber_encoded = ber_encoded
5355 pp_console_row(next(self.pps())),
5356 ", ".join(repr(v) for v in self._value),
5359 def pps(self, decode_path=()):
5362 asn1_type_name=self.asn1_type_name,
5363 obj_name=self.__class__.__name__,
5364 decode_path=decode_path,
5365 optional=self.optional,
5366 default=self == self.default,
5367 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5368 expl=None if self._expl is None else tag_decode(self._expl),
5373 expl_offset=self.expl_offset if self.expled else None,
5374 expl_tlen=self.expl_tlen if self.expled else None,
5375 expl_llen=self.expl_llen if self.expled else None,
5376 expl_vlen=self.expl_vlen if self.expled else None,
5377 expl_lenindef=self.expl_lenindef,
5378 lenindef=self.lenindef,
5379 ber_encoded=self.ber_encoded,
5382 for i, value in enumerate(self._value):
5383 yield value.pps(decode_path=decode_path + (str(i),))
5384 for pp in self.pps_lenindef(decode_path):
5388 class SetOf(SequenceOf):
5389 """``SET OF`` sequence type
5391 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5394 tag_default = tag_encode(form=TagFormConstructed, num=17)
5395 asn1_type_name = "SET OF"
5398 raws = self._encoded_values()
5401 return b"".join((self.tag, len_encode(len(v)), v))
5403 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5404 return super(SetOf, self)._decode(
5410 ordering_check=True,
5414 def obj_by_path(pypath): # pragma: no cover
5415 """Import object specified as string Python path
5417 Modules must be separated from classes/functions with ``:``.
5419 >>> obj_by_path("foo.bar:Baz")
5420 <class 'foo.bar.Baz'>
5421 >>> obj_by_path("foo.bar:Baz.boo")
5422 <classmethod 'foo.bar.Baz.boo'>
5424 mod, objs = pypath.rsplit(":", 1)
5425 from importlib import import_module
5426 obj = import_module(mod)
5427 for obj_name in objs.split("."):
5428 obj = getattr(obj, obj_name)
5432 def generic_decoder(): # pragma: no cover
5433 # All of this below is a big hack with self references
5434 choice = PrimitiveTypes()
5435 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5436 choice.specs["SetOf"] = SetOf(schema=choice)
5437 for i in six_xrange(31):
5438 choice.specs["SequenceOf%d" % i] = SequenceOf(
5442 choice.specs["Any"] = Any()
5444 # Class name equals to type name, to omit it from output
5445 class SEQUENCEOF(SequenceOf):
5453 with_decode_path=False,
5454 decode_path_only=(),
5456 def _pprint_pps(pps):
5458 if hasattr(pp, "_fields"):
5460 decode_path_only != () and
5461 pp.decode_path[:len(decode_path_only)] != decode_path_only
5464 if pp.asn1_type_name == Choice.asn1_type_name:
5466 pp_kwargs = pp._asdict()
5467 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5468 pp = _pp(**pp_kwargs)
5469 yield pp_console_row(
5474 with_colours=with_colours,
5475 with_decode_path=with_decode_path,
5476 decode_path_len_decrease=len(decode_path_only),
5478 for row in pp_console_blob(
5480 decode_path_len_decrease=len(decode_path_only),
5484 for row in _pprint_pps(pp):
5486 return "\n".join(_pprint_pps(obj.pps()))
5487 return SEQUENCEOF(), pprint_any
5490 def main(): # pragma: no cover
5492 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5493 parser.add_argument(
5497 help="Skip that number of bytes from the beginning",
5499 parser.add_argument(
5501 help="Python path to dictionary with OIDs",
5503 parser.add_argument(
5505 help="Python path to schema definition to use",
5507 parser.add_argument(
5508 "--defines-by-path",
5509 help="Python path to decoder's defines_by_path",
5511 parser.add_argument(
5513 action="store_true",
5514 help="Disallow BER encoding",
5516 parser.add_argument(
5517 "--print-decode-path",
5518 action="store_true",
5519 help="Print decode paths",
5521 parser.add_argument(
5522 "--decode-path-only",
5523 help="Print only specified decode path",
5525 parser.add_argument(
5527 action="store_true",
5528 help="Allow explicit tag out-of-bound",
5530 parser.add_argument(
5532 type=argparse.FileType("rb"),
5533 help="Path to DER file you want to decode",
5535 args = parser.parse_args()
5536 args.DERFile.seek(args.skip)
5537 der = memoryview(args.DERFile.read())
5538 args.DERFile.close()
5539 oids = obj_by_path(args.oids) if args.oids else {}
5541 schema = obj_by_path(args.schema)
5542 from functools import partial
5543 pprinter = partial(pprint, big_blobs=True)
5545 schema, pprinter = generic_decoder()
5547 "bered": not args.nobered,
5548 "allow_expl_oob": args.allow_expl_oob,
5550 if args.defines_by_path is not None:
5551 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5552 obj, tail = schema().decode(der, ctx=ctx)
5556 with_colours=True if environ.get("NO_COLOR") is None else False,
5557 with_decode_path=args.print_decode_path,
5559 () if args.decode_path_only is None else
5560 tuple(args.decode_path_only.split(":"))
5564 print("\nTrailing data: %s" % hexenc(tail))
5567 if __name__ == "__main__":