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 if value not in list(self.specs.values()):
3360 "unknown integer value: %s" % value,
3361 klass=self.__class__,
3363 elif isinstance(value, string_types):
3364 value = self.specs.get(value)
3366 raise ObjUnknown("integer value: %s" % value)
3368 raise InvalidValueType((self.__class__, int, str))
3372 obj = self.__class__(_specs=self.specs)
3373 obj._value = self._value
3374 obj._bound_min = self._bound_min
3375 obj._bound_max = self._bound_max
3377 obj._expl = self._expl
3378 obj.default = self.default
3379 obj.optional = self.optional
3380 obj.offset = self.offset
3381 obj.llen = self.llen
3382 obj.vlen = self.vlen
3383 obj.expl_lenindef = self.expl_lenindef
3384 obj.lenindef = self.lenindef
3385 obj.ber_encoded = self.ber_encoded
3397 return self.__class__(
3399 impl=self.tag if impl is None else impl,
3400 expl=self._expl if expl is None else expl,
3401 default=self.default if default is None else default,
3402 optional=self.optional if optional is None else optional,
3407 class CommonString(OctetString):
3408 """Common class for all strings
3410 Everything resembles :py:class:`pyderasn.OctetString`, except
3411 ability to deal with unicode text strings.
3413 >>> hexenc("привет мир".encode("utf-8"))
3414 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3415 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3417 >>> s = UTF8String("привет мир")
3418 UTF8String UTF8String привет мир
3420 'привет мир'
3421 >>> hexenc(bytes(s))
3422 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3424 >>> PrintableString("привет мир")
3425 Traceback (most recent call last):
3426 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3428 >>> BMPString("ада", bounds=(2, 2))
3429 Traceback (most recent call last):
3430 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3431 >>> s = BMPString("ад", bounds=(2, 2))
3434 >>> hexenc(bytes(s))
3442 * - :py:class:`pyderasn.UTF8String`
3444 * - :py:class:`pyderasn.NumericString`
3446 * - :py:class:`pyderasn.PrintableString`
3448 * - :py:class:`pyderasn.TeletexString`
3450 * - :py:class:`pyderasn.T61String`
3452 * - :py:class:`pyderasn.VideotexString`
3454 * - :py:class:`pyderasn.IA5String`
3456 * - :py:class:`pyderasn.GraphicString`
3458 * - :py:class:`pyderasn.VisibleString`
3460 * - :py:class:`pyderasn.ISO646String`
3462 * - :py:class:`pyderasn.GeneralString`
3464 * - :py:class:`pyderasn.UniversalString`
3466 * - :py:class:`pyderasn.BMPString`
3469 __slots__ = ("encoding",)
3471 def _value_sanitize(self, value):
3473 value_decoded = None
3474 if isinstance(value, self.__class__):
3475 value_raw = value._value
3476 elif isinstance(value, text_type):
3477 value_decoded = value
3478 elif isinstance(value, binary_type):
3481 raise InvalidValueType((self.__class__, text_type, binary_type))
3484 value_decoded.encode(self.encoding)
3485 if value_raw is None else value_raw
3488 value_raw.decode(self.encoding)
3489 if value_decoded is None else value_decoded
3491 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3492 raise DecodeError(str(err))
3493 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3501 def __eq__(self, their):
3502 if isinstance(their, binary_type):
3503 return self._value == their
3504 if isinstance(their, text_type):
3505 return self._value == their.encode(self.encoding)
3506 if not isinstance(their, self.__class__):
3509 self._value == their._value and
3510 self.tag == their.tag and
3511 self._expl == their._expl
3514 def __unicode__(self):
3516 return self._value.decode(self.encoding)
3517 return text_type(self._value)
3520 return pp_console_row(next(self.pps(no_unicode=PY2)))
3522 def pps(self, decode_path=(), no_unicode=False):
3525 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3528 asn1_type_name=self.asn1_type_name,
3529 obj_name=self.__class__.__name__,
3530 decode_path=decode_path,
3532 optional=self.optional,
3533 default=self == self.default,
3534 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3535 expl=None if self._expl is None else tag_decode(self._expl),
3540 expl_offset=self.expl_offset if self.expled else None,
3541 expl_tlen=self.expl_tlen if self.expled else None,
3542 expl_llen=self.expl_llen if self.expled else None,
3543 expl_vlen=self.expl_vlen if self.expled else None,
3544 expl_lenindef=self.expl_lenindef,
3545 ber_encoded=self.ber_encoded,
3548 for pp in self.pps_lenindef(decode_path):
3552 class UTF8String(CommonString):
3554 tag_default = tag_encode(12)
3556 asn1_type_name = "UTF8String"
3559 class AllowableCharsMixin(object):
3561 def allowable_chars(self):
3563 return self._allowable_chars
3564 return set(six_unichr(c) for c in self._allowable_chars)
3567 class NumericString(AllowableCharsMixin, CommonString):
3570 Its value is properly sanitized: only ASCII digits with spaces can
3573 >>> NumericString().allowable_chars
3574 set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3577 tag_default = tag_encode(18)
3579 asn1_type_name = "NumericString"
3580 _allowable_chars = set(digits.encode("ascii") + b" ")
3582 def _value_sanitize(self, value):
3583 value = super(NumericString, self)._value_sanitize(value)
3584 if not set(value) <= self._allowable_chars:
3585 raise DecodeError("non-numeric value")
3589 class PrintableString(AllowableCharsMixin, CommonString):
3592 Its value is properly sanitized: see X.680 41.4 table 10.
3594 >>> PrintableString().allowable_chars
3595 >>> set([' ', "'", ..., 'z'])
3598 tag_default = tag_encode(19)
3600 asn1_type_name = "PrintableString"
3601 _allowable_chars = set(
3602 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3605 def _value_sanitize(self, value):
3606 value = super(PrintableString, self)._value_sanitize(value)
3607 if not set(value) <= self._allowable_chars:
3608 raise DecodeError("non-printable value")
3612 class TeletexString(CommonString):
3614 tag_default = tag_encode(20)
3616 asn1_type_name = "TeletexString"
3619 class T61String(TeletexString):
3621 asn1_type_name = "T61String"
3624 class VideotexString(CommonString):
3626 tag_default = tag_encode(21)
3627 encoding = "iso-8859-1"
3628 asn1_type_name = "VideotexString"
3631 class IA5String(CommonString):
3633 tag_default = tag_encode(22)
3635 asn1_type_name = "IA5"
3638 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3639 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3640 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3643 class UTCTime(CommonString):
3644 """``UTCTime`` datetime type
3646 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3647 UTCTime UTCTime 2017-09-30T22:07:50
3653 datetime.datetime(2017, 9, 30, 22, 7, 50)
3654 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3655 datetime.datetime(1957, 9, 30, 22, 7, 50)
3658 tag_default = tag_encode(23)
3660 asn1_type_name = "UTCTime"
3662 fmt = "%y%m%d%H%M%SZ"
3672 bounds=None, # dummy argument, workability for OctetString.decode
3675 :param value: set the value. Either datetime type, or
3676 :py:class:`pyderasn.UTCTime` object
3677 :param bytes impl: override default tag with ``IMPLICIT`` one
3678 :param bytes expl: override default tag with ``EXPLICIT`` one
3679 :param default: set default value. Type same as in ``value``
3680 :param bool optional: is object ``OPTIONAL`` in sequence
3682 super(UTCTime, self).__init__(
3690 if value is not None:
3691 self._value = self._value_sanitize(value)
3692 if default is not None:
3693 default = self._value_sanitize(default)
3694 self.default = self.__class__(
3699 if self._value is None:
3700 self._value = default
3702 def _value_sanitize(self, value):
3703 if isinstance(value, self.__class__):
3705 if isinstance(value, datetime):
3706 return value.strftime(self.fmt).encode("ascii")
3707 if isinstance(value, binary_type):
3709 value_decoded = value.decode("ascii")
3710 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3711 raise DecodeError("invalid UTCTime encoding")
3712 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3714 datetime.strptime(value_decoded, self.fmt)
3715 except (TypeError, ValueError):
3716 raise DecodeError("invalid UTCTime format")
3719 raise DecodeError("invalid UTCTime length")
3720 raise InvalidValueType((self.__class__, datetime))
3722 def __eq__(self, their):
3723 if isinstance(their, binary_type):
3724 return self._value == their
3725 if isinstance(their, datetime):
3726 return self.todatetime() == their
3727 if not isinstance(their, self.__class__):
3730 self._value == their._value and
3731 self.tag == their.tag and
3732 self._expl == their._expl
3735 def todatetime(self):
3736 """Convert to datetime
3740 Pay attention that UTCTime can not hold full year, so all years
3741 having < 50 years are treated as 20xx, 19xx otherwise, according
3742 to X.509 recomendation.
3744 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3745 year = value.year % 100
3747 year=(2000 + year) if year < 50 else (1900 + year),
3751 minute=value.minute,
3752 second=value.second,
3756 return pp_console_row(next(self.pps()))
3758 def pps(self, decode_path=()):
3761 asn1_type_name=self.asn1_type_name,
3762 obj_name=self.__class__.__name__,
3763 decode_path=decode_path,
3764 value=self.todatetime().isoformat() if self.ready else None,
3765 optional=self.optional,
3766 default=self == self.default,
3767 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3768 expl=None if self._expl is None else tag_decode(self._expl),
3773 expl_offset=self.expl_offset if self.expled else None,
3774 expl_tlen=self.expl_tlen if self.expled else None,
3775 expl_llen=self.expl_llen if self.expled else None,
3776 expl_vlen=self.expl_vlen if self.expled else None,
3777 expl_lenindef=self.expl_lenindef,
3778 ber_encoded=self.ber_encoded,
3781 for pp in self.pps_lenindef(decode_path):
3785 class GeneralizedTime(UTCTime):
3786 """``GeneralizedTime`` datetime type
3788 This type is similar to :py:class:`pyderasn.UTCTime`.
3790 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3791 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3793 '20170930220750.000123Z'
3794 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3795 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3798 tag_default = tag_encode(24)
3799 asn1_type_name = "GeneralizedTime"
3801 fmt = "%Y%m%d%H%M%SZ"
3802 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3804 def _value_sanitize(self, value):
3805 if isinstance(value, self.__class__):
3807 if isinstance(value, datetime):
3808 return value.strftime(
3809 self.fmt_ms if value.microsecond > 0 else self.fmt
3811 if isinstance(value, binary_type):
3813 value_decoded = value.decode("ascii")
3814 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3815 raise DecodeError("invalid GeneralizedTime encoding")
3816 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3818 datetime.strptime(value_decoded, self.fmt)
3819 except (TypeError, ValueError):
3821 "invalid GeneralizedTime (without ms) format",
3824 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3826 datetime.strptime(value_decoded, self.fmt_ms)
3827 except (TypeError, ValueError):
3829 "invalid GeneralizedTime (with ms) format",
3834 "invalid GeneralizedTime length",
3835 klass=self.__class__,
3837 raise InvalidValueType((self.__class__, datetime))
3839 def todatetime(self):
3840 value = self._value.decode("ascii")
3841 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3842 return datetime.strptime(value, self.fmt)
3843 return datetime.strptime(value, self.fmt_ms)
3846 class GraphicString(CommonString):
3848 tag_default = tag_encode(25)
3849 encoding = "iso-8859-1"
3850 asn1_type_name = "GraphicString"
3853 class VisibleString(CommonString):
3855 tag_default = tag_encode(26)
3857 asn1_type_name = "VisibleString"
3860 class ISO646String(VisibleString):
3862 asn1_type_name = "ISO646String"
3865 class GeneralString(CommonString):
3867 tag_default = tag_encode(27)
3868 encoding = "iso-8859-1"
3869 asn1_type_name = "GeneralString"
3872 class UniversalString(CommonString):
3874 tag_default = tag_encode(28)
3875 encoding = "utf-32-be"
3876 asn1_type_name = "UniversalString"
3879 class BMPString(CommonString):
3881 tag_default = tag_encode(30)
3882 encoding = "utf-16-be"
3883 asn1_type_name = "BMPString"
3887 """``CHOICE`` special type
3891 class GeneralName(Choice):
3893 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3894 ("dNSName", IA5String(impl=tag_ctxp(2))),
3897 >>> gn = GeneralName()
3899 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3900 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3901 >>> gn["dNSName"] = IA5String("bar.baz")
3902 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3903 >>> gn["rfc822Name"]
3906 [2] IA5String IA5 bar.baz
3909 >>> gn.value == gn["dNSName"]
3912 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3914 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3915 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3917 __slots__ = ("specs",)
3919 asn1_type_name = "CHOICE"
3932 :param value: set the value. Either ``(choice, value)`` tuple, or
3933 :py:class:`pyderasn.Choice` object
3934 :param bytes impl: can not be set, do **not** use it
3935 :param bytes expl: override default tag with ``EXPLICIT`` one
3936 :param default: set default value. Type same as in ``value``
3937 :param bool optional: is object ``OPTIONAL`` in sequence
3939 if impl is not None:
3940 raise ValueError("no implicit tag allowed for CHOICE")
3941 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3943 schema = getattr(self, "schema", ())
3944 if len(schema) == 0:
3945 raise ValueError("schema must be specified")
3947 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3950 if value is not None:
3951 self._value = self._value_sanitize(value)
3952 if default is not None:
3953 default_value = self._value_sanitize(default)
3954 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3955 default_obj.specs = self.specs
3956 default_obj._value = default_value
3957 self.default = default_obj
3959 self._value = default_obj.copy()._value
3961 def _value_sanitize(self, value):
3962 if isinstance(value, self.__class__):
3964 if isinstance(value, tuple) and len(value) == 2:
3966 spec = self.specs.get(choice)
3968 raise ObjUnknown(choice)
3969 if not isinstance(obj, spec.__class__):
3970 raise InvalidValueType((spec,))
3971 return (choice, spec(obj))
3972 raise InvalidValueType((self.__class__, tuple))
3976 return self._value is not None and self._value[1].ready
3980 return self.expl_lenindef or (
3981 (self._value is not None) and
3982 self._value[1].bered
3986 obj = self.__class__(schema=self.specs)
3987 obj._expl = self._expl
3988 obj.default = self.default
3989 obj.optional = self.optional
3990 obj.offset = self.offset
3991 obj.llen = self.llen
3992 obj.vlen = self.vlen
3993 obj.expl_lenindef = self.expl_lenindef
3994 obj.lenindef = self.lenindef
3995 obj.ber_encoded = self.ber_encoded
3997 if value is not None:
3998 obj._value = (value[0], value[1].copy())
4001 def __eq__(self, their):
4002 if isinstance(their, tuple) and len(their) == 2:
4003 return self._value == their
4004 if not isinstance(their, self.__class__):
4007 self.specs == their.specs and
4008 self._value == their._value
4018 return self.__class__(
4021 expl=self._expl if expl is None else expl,
4022 default=self.default if default is None else default,
4023 optional=self.optional if optional is None else optional,
4028 self._assert_ready()
4029 return self._value[0]
4033 self._assert_ready()
4034 return self._value[1]
4036 def __getitem__(self, key):
4037 if key not in self.specs:
4038 raise ObjUnknown(key)
4039 if self._value is None:
4041 choice, value = self._value
4046 def __setitem__(self, key, value):
4047 spec = self.specs.get(key)
4049 raise ObjUnknown(key)
4050 if not isinstance(value, spec.__class__):
4051 raise InvalidValueType((spec.__class__,))
4052 self._value = (key, spec(value))
4060 return self._value[1].decoded if self.ready else False
4063 self._assert_ready()
4064 return self._value[1].encode()
4066 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4067 for choice, spec in self.specs.items():
4068 sub_decode_path = decode_path + (choice,)
4074 decode_path=sub_decode_path,
4077 _ctx_immutable=False,
4084 klass=self.__class__,
4085 decode_path=decode_path,
4088 if tag_only: # pragma: no cover
4090 value, tail = spec.decode(
4094 decode_path=sub_decode_path,
4096 _ctx_immutable=False,
4098 obj = self.__class__(
4101 default=self.default,
4102 optional=self.optional,
4103 _decoded=(offset, 0, value.fulllen),
4105 obj._value = (choice, value)
4109 value = pp_console_row(next(self.pps()))
4111 value = "%s[%r]" % (value, self.value)
4114 def pps(self, decode_path=()):
4117 asn1_type_name=self.asn1_type_name,
4118 obj_name=self.__class__.__name__,
4119 decode_path=decode_path,
4120 value=self.choice if self.ready else None,
4121 optional=self.optional,
4122 default=self == self.default,
4123 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4124 expl=None if self._expl is None else tag_decode(self._expl),
4129 expl_lenindef=self.expl_lenindef,
4133 yield self.value.pps(decode_path=decode_path + (self.choice,))
4134 for pp in self.pps_lenindef(decode_path):
4138 class PrimitiveTypes(Choice):
4139 """Predefined ``CHOICE`` for all generic primitive types
4141 It could be useful for general decoding of some unspecified values:
4143 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4144 OCTET STRING 3 bytes 666f6f
4145 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4149 schema = tuple((klass.__name__, klass()) for klass in (
4174 """``ANY`` special type
4176 >>> Any(Integer(-123))
4178 >>> a = Any(OctetString(b"hello world").encode())
4179 ANY 040b68656c6c6f20776f726c64
4180 >>> hexenc(bytes(a))
4181 b'0x040x0bhello world'
4183 __slots__ = ("defined",)
4184 tag_default = tag_encode(0)
4185 asn1_type_name = "ANY"
4195 :param value: set the value. Either any kind of pyderasn's
4196 **ready** object, or bytes. Pay attention that
4197 **no** validation is performed is raw binary value
4199 :param bytes expl: override default tag with ``EXPLICIT`` one
4200 :param bool optional: is object ``OPTIONAL`` in sequence
4202 super(Any, self).__init__(None, expl, None, optional, _decoded)
4203 self._value = None if value is None else self._value_sanitize(value)
4206 def _value_sanitize(self, value):
4207 if isinstance(value, self.__class__):
4209 if isinstance(value, Obj):
4210 return value.encode()
4211 if isinstance(value, binary_type):
4213 raise InvalidValueType((self.__class__, Obj, binary_type))
4217 return self._value is not None
4221 if self.expl_lenindef or self.lenindef:
4223 if self.defined is None:
4225 return self.defined[1].bered
4228 obj = self.__class__()
4229 obj._value = self._value
4231 obj._expl = self._expl
4232 obj.optional = self.optional
4233 obj.offset = self.offset
4234 obj.llen = self.llen
4235 obj.vlen = self.vlen
4236 obj.expl_lenindef = self.expl_lenindef
4237 obj.lenindef = self.lenindef
4238 obj.ber_encoded = self.ber_encoded
4241 def __eq__(self, their):
4242 if isinstance(their, binary_type):
4243 return self._value == their
4244 if issubclass(their.__class__, Any):
4245 return self._value == their._value
4254 return self.__class__(
4256 expl=self._expl if expl is None else expl,
4257 optional=self.optional if optional is None else optional,
4260 def __bytes__(self):
4261 self._assert_ready()
4269 self._assert_ready()
4272 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4274 t, tlen, lv = tag_strip(tlv)
4275 except DecodeError as err:
4276 raise err.__class__(
4278 klass=self.__class__,
4279 decode_path=decode_path,
4283 l, llen, v = len_decode(lv)
4284 except LenIndefForm as err:
4285 if not ctx.get("bered", False):
4286 raise err.__class__(
4288 klass=self.__class__,
4289 decode_path=decode_path,
4292 llen, vlen, v = 1, 0, lv[1:]
4293 sub_offset = offset + tlen + llen
4295 while v[:EOC_LEN].tobytes() != EOC:
4296 chunk, v = Any().decode(
4299 decode_path=decode_path + (str(chunk_i),),
4302 _ctx_immutable=False,
4304 vlen += chunk.tlvlen
4305 sub_offset += chunk.tlvlen
4307 tlvlen = tlen + llen + vlen + EOC_LEN
4308 obj = self.__class__(
4309 value=tlv[:tlvlen].tobytes(),
4311 optional=self.optional,
4312 _decoded=(offset, 0, tlvlen),
4316 return obj, v[EOC_LEN:]
4317 except DecodeError as err:
4318 raise err.__class__(
4320 klass=self.__class__,
4321 decode_path=decode_path,
4325 raise NotEnoughData(
4326 "encoded length is longer than data",
4327 klass=self.__class__,
4328 decode_path=decode_path,
4331 tlvlen = tlen + llen + l
4332 v, tail = tlv[:tlvlen], v[l:]
4333 obj = self.__class__(
4336 optional=self.optional,
4337 _decoded=(offset, 0, tlvlen),
4343 return pp_console_row(next(self.pps()))
4345 def pps(self, decode_path=()):
4348 asn1_type_name=self.asn1_type_name,
4349 obj_name=self.__class__.__name__,
4350 decode_path=decode_path,
4351 blob=self._value if self.ready else None,
4352 optional=self.optional,
4353 default=self == self.default,
4354 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4355 expl=None if self._expl is None else tag_decode(self._expl),
4360 expl_offset=self.expl_offset if self.expled else None,
4361 expl_tlen=self.expl_tlen if self.expled else None,
4362 expl_llen=self.expl_llen if self.expled else None,
4363 expl_vlen=self.expl_vlen if self.expled else None,
4364 expl_lenindef=self.expl_lenindef,
4365 lenindef=self.lenindef,
4368 defined_by, defined = self.defined or (None, None)
4369 if defined_by is not None:
4371 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4373 for pp in self.pps_lenindef(decode_path):
4377 ########################################################################
4378 # ASN.1 constructed types
4379 ########################################################################
4381 def get_def_by_path(defines_by_path, sub_decode_path):
4382 """Get define by decode path
4384 for path, define in defines_by_path:
4385 if len(path) != len(sub_decode_path):
4387 for p1, p2 in zip(path, sub_decode_path):
4388 if (p1 != any) and (p1 != p2):
4394 def abs_decode_path(decode_path, rel_path):
4395 """Create an absolute decode path from current and relative ones
4397 :param decode_path: current decode path, starting point. Tuple of strings
4398 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4399 If first tuple's element is "/", then treat it as
4400 an absolute path, ignoring ``decode_path`` as
4401 starting point. Also this tuple can contain ".."
4402 elements, stripping the leading element from
4405 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4406 ("foo", "bar", "baz", "whatever")
4407 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4409 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4412 if rel_path[0] == "/":
4414 if rel_path[0] == "..":
4415 return abs_decode_path(decode_path[:-1], rel_path[1:])
4416 return decode_path + rel_path
4419 class Sequence(Obj):
4420 """``SEQUENCE`` structure type
4422 You have to make specification of sequence::
4424 class Extension(Sequence):
4426 ("extnID", ObjectIdentifier()),
4427 ("critical", Boolean(default=False)),
4428 ("extnValue", OctetString()),
4431 Then, you can work with it as with dictionary.
4433 >>> ext = Extension()
4434 >>> Extension().specs
4436 ('extnID', OBJECT IDENTIFIER),
4437 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4438 ('extnValue', OCTET STRING),
4440 >>> ext["extnID"] = "1.2.3"
4441 Traceback (most recent call last):
4442 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4443 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4445 You can determine if sequence is ready to be encoded:
4450 Traceback (most recent call last):
4451 pyderasn.ObjNotReady: object is not ready: extnValue
4452 >>> ext["extnValue"] = OctetString(b"foobar")
4456 Value you want to assign, must have the same **type** as in
4457 corresponding specification, but it can have different tags,
4458 optional/default attributes -- they will be taken from specification
4461 class TBSCertificate(Sequence):
4463 ("version", Version(expl=tag_ctxc(0), default="v1")),
4466 >>> tbs = TBSCertificate()
4467 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4469 Assign ``None`` to remove value from sequence.
4471 You can set values in Sequence during its initialization:
4473 >>> AlgorithmIdentifier((
4474 ("algorithm", ObjectIdentifier("1.2.3")),
4475 ("parameters", Any(Null()))
4477 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4479 You can determine if value exists/set in the sequence and take its value:
4481 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4484 OBJECT IDENTIFIER 1.2.3
4486 But pay attention that if value has default, then it won't be (not
4487 in) in the sequence (because ``DEFAULT`` must not be encoded in
4488 DER), but you can read its value:
4490 >>> "critical" in ext, ext["critical"]
4491 (False, BOOLEAN False)
4492 >>> ext["critical"] = Boolean(True)
4493 >>> "critical" in ext, ext["critical"]
4494 (True, BOOLEAN True)
4496 All defaulted values are always optional.
4498 .. _allow_default_values_ctx:
4500 DER prohibits default value encoding and will raise an error if
4501 default value is unexpectedly met during decode.
4502 If :ref:`bered <bered_ctx>` context option is set, then no error
4503 will be raised, but ``bered`` attribute set. You can disable strict
4504 defaulted values existence validation by setting
4505 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4507 Two sequences are equal if they have equal specification (schema),
4508 implicit/explicit tagging and the same values.
4510 __slots__ = ("specs",)
4511 tag_default = tag_encode(form=TagFormConstructed, num=16)
4512 asn1_type_name = "SEQUENCE"
4524 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4526 schema = getattr(self, "schema", ())
4528 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4531 if value is not None:
4532 if issubclass(value.__class__, Sequence):
4533 self._value = value._value
4534 elif hasattr(value, "__iter__"):
4535 for seq_key, seq_value in value:
4536 self[seq_key] = seq_value
4538 raise InvalidValueType((Sequence,))
4539 if default is not None:
4540 if not issubclass(default.__class__, Sequence):
4541 raise InvalidValueType((Sequence,))
4542 default_value = default._value
4543 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4544 default_obj.specs = self.specs
4545 default_obj._value = default_value
4546 self.default = default_obj
4548 self._value = default_obj.copy()._value
4552 for name, spec in self.specs.items():
4553 value = self._value.get(name)
4565 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4567 return any(value.bered for value in self._value.values())
4570 obj = self.__class__(schema=self.specs)
4572 obj._expl = self._expl
4573 obj.default = self.default
4574 obj.optional = self.optional
4575 obj.offset = self.offset
4576 obj.llen = self.llen
4577 obj.vlen = self.vlen
4578 obj.expl_lenindef = self.expl_lenindef
4579 obj.lenindef = self.lenindef
4580 obj.ber_encoded = self.ber_encoded
4581 obj._value = {k: v.copy() for k, v in self._value.items()}
4584 def __eq__(self, their):
4585 if not isinstance(their, self.__class__):
4588 self.specs == their.specs and
4589 self.tag == their.tag and
4590 self._expl == their._expl and
4591 self._value == their._value
4602 return self.__class__(
4605 impl=self.tag if impl is None else impl,
4606 expl=self._expl if expl is None else expl,
4607 default=self.default if default is None else default,
4608 optional=self.optional if optional is None else optional,
4611 def __contains__(self, key):
4612 return key in self._value
4614 def __setitem__(self, key, value):
4615 spec = self.specs.get(key)
4617 raise ObjUnknown(key)
4619 self._value.pop(key, None)
4621 if not isinstance(value, spec.__class__):
4622 raise InvalidValueType((spec.__class__,))
4623 value = spec(value=value)
4624 if spec.default is not None and value == spec.default:
4625 self._value.pop(key, None)
4627 self._value[key] = value
4629 def __getitem__(self, key):
4630 value = self._value.get(key)
4631 if value is not None:
4633 spec = self.specs.get(key)
4635 raise ObjUnknown(key)
4636 if spec.default is not None:
4640 def _encoded_values(self):
4642 for name, spec in self.specs.items():
4643 value = self._value.get(name)
4647 raise ObjNotReady(name)
4648 raws.append(value.encode())
4652 v = b"".join(self._encoded_values())
4653 return b"".join((self.tag, len_encode(len(v)), v))
4655 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4657 t, tlen, lv = tag_strip(tlv)
4658 except DecodeError as err:
4659 raise err.__class__(
4661 klass=self.__class__,
4662 decode_path=decode_path,
4667 klass=self.__class__,
4668 decode_path=decode_path,
4671 if tag_only: # pragma: no cover
4674 ctx_bered = ctx.get("bered", False)
4676 l, llen, v = len_decode(lv)
4677 except LenIndefForm as err:
4679 raise err.__class__(
4681 klass=self.__class__,
4682 decode_path=decode_path,
4685 l, llen, v = 0, 1, lv[1:]
4687 except DecodeError as err:
4688 raise err.__class__(
4690 klass=self.__class__,
4691 decode_path=decode_path,
4695 raise NotEnoughData(
4696 "encoded length is longer than data",
4697 klass=self.__class__,
4698 decode_path=decode_path,
4702 v, tail = v[:l], v[l:]
4704 sub_offset = offset + tlen + llen
4707 ctx_allow_default_values = ctx.get("allow_default_values", False)
4708 for name, spec in self.specs.items():
4709 if spec.optional and (
4710 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4714 sub_decode_path = decode_path + (name,)
4716 value, v_tail = spec.decode(
4720 decode_path=sub_decode_path,
4722 _ctx_immutable=False,
4729 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4730 if defined is not None:
4731 defined_by, defined_spec = defined
4732 if issubclass(value.__class__, SequenceOf):
4733 for i, _value in enumerate(value):
4734 sub_sub_decode_path = sub_decode_path + (
4736 DecodePathDefBy(defined_by),
4738 defined_value, defined_tail = defined_spec.decode(
4739 memoryview(bytes(_value)),
4741 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4742 if value.expled else (value.tlen + value.llen)
4745 decode_path=sub_sub_decode_path,
4747 _ctx_immutable=False,
4749 if len(defined_tail) > 0:
4752 klass=self.__class__,
4753 decode_path=sub_sub_decode_path,
4756 _value.defined = (defined_by, defined_value)
4758 defined_value, defined_tail = defined_spec.decode(
4759 memoryview(bytes(value)),
4761 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4762 if value.expled else (value.tlen + value.llen)
4765 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4767 _ctx_immutable=False,
4769 if len(defined_tail) > 0:
4772 klass=self.__class__,
4773 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4776 value.defined = (defined_by, defined_value)
4778 value_len = value.fulllen
4780 sub_offset += value_len
4782 if spec.default is not None and value == spec.default:
4783 if ctx_bered or ctx_allow_default_values:
4787 "DEFAULT value met",
4788 klass=self.__class__,
4789 decode_path=sub_decode_path,
4792 values[name] = value
4794 spec_defines = getattr(spec, "defines", ())
4795 if len(spec_defines) == 0:
4796 defines_by_path = ctx.get("defines_by_path", ())
4797 if len(defines_by_path) > 0:
4798 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4799 if spec_defines is not None and len(spec_defines) > 0:
4800 for rel_path, schema in spec_defines:
4801 defined = schema.get(value, None)
4802 if defined is not None:
4803 ctx.setdefault("_defines", []).append((
4804 abs_decode_path(sub_decode_path[:-1], rel_path),
4808 if v[:EOC_LEN].tobytes() != EOC:
4811 klass=self.__class__,
4812 decode_path=decode_path,
4820 klass=self.__class__,
4821 decode_path=decode_path,
4824 obj = self.__class__(
4828 default=self.default,
4829 optional=self.optional,
4830 _decoded=(offset, llen, vlen),
4833 obj.lenindef = lenindef
4834 obj.ber_encoded = ber_encoded
4838 value = pp_console_row(next(self.pps()))
4840 for name in self.specs:
4841 _value = self._value.get(name)
4844 cols.append("%s: %s" % (name, repr(_value)))
4845 return "%s[%s]" % (value, "; ".join(cols))
4847 def pps(self, decode_path=()):
4850 asn1_type_name=self.asn1_type_name,
4851 obj_name=self.__class__.__name__,
4852 decode_path=decode_path,
4853 optional=self.optional,
4854 default=self == self.default,
4855 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4856 expl=None if self._expl is None else tag_decode(self._expl),
4861 expl_offset=self.expl_offset if self.expled else None,
4862 expl_tlen=self.expl_tlen if self.expled else None,
4863 expl_llen=self.expl_llen if self.expled else None,
4864 expl_vlen=self.expl_vlen if self.expled else None,
4865 expl_lenindef=self.expl_lenindef,
4866 lenindef=self.lenindef,
4867 ber_encoded=self.ber_encoded,
4870 for name in self.specs:
4871 value = self._value.get(name)
4874 yield value.pps(decode_path=decode_path + (name,))
4875 for pp in self.pps_lenindef(decode_path):
4879 class Set(Sequence):
4880 """``SET`` structure type
4882 Its usage is identical to :py:class:`pyderasn.Sequence`.
4884 .. _allow_unordered_set_ctx:
4886 DER prohibits unordered values encoding and will raise an error
4887 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4888 then no error will occure. Also you can disable strict values
4889 ordering check by setting ``"allow_unordered_set": True``
4890 :ref:`context <ctx>` option.
4893 tag_default = tag_encode(form=TagFormConstructed, num=17)
4894 asn1_type_name = "SET"
4897 raws = self._encoded_values()
4900 return b"".join((self.tag, len_encode(len(v)), v))
4902 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4904 t, tlen, lv = tag_strip(tlv)
4905 except DecodeError as err:
4906 raise err.__class__(
4908 klass=self.__class__,
4909 decode_path=decode_path,
4914 klass=self.__class__,
4915 decode_path=decode_path,
4921 ctx_bered = ctx.get("bered", False)
4923 l, llen, v = len_decode(lv)
4924 except LenIndefForm as err:
4926 raise err.__class__(
4928 klass=self.__class__,
4929 decode_path=decode_path,
4932 l, llen, v = 0, 1, lv[1:]
4934 except DecodeError as err:
4935 raise err.__class__(
4937 klass=self.__class__,
4938 decode_path=decode_path,
4942 raise NotEnoughData(
4943 "encoded length is longer than data",
4944 klass=self.__class__,
4948 v, tail = v[:l], v[l:]
4950 sub_offset = offset + tlen + llen
4953 ctx_allow_default_values = ctx.get("allow_default_values", False)
4954 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4955 value_prev = memoryview(v[:0])
4956 specs_items = self.specs.items
4958 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4960 for name, spec in specs_items():
4961 sub_decode_path = decode_path + (name,)
4967 decode_path=sub_decode_path,
4970 _ctx_immutable=False,
4977 klass=self.__class__,
4978 decode_path=decode_path,
4981 value, v_tail = spec.decode(
4985 decode_path=sub_decode_path,
4987 _ctx_immutable=False,
4989 value_len = value.fulllen
4990 if value_prev.tobytes() > v[:value_len].tobytes():
4991 if ctx_bered or ctx_allow_unordered_set:
4995 "unordered " + self.asn1_type_name,
4996 klass=self.__class__,
4997 decode_path=sub_decode_path,
5000 if spec.default is None or value != spec.default:
5002 elif ctx_bered or ctx_allow_default_values:
5006 "DEFAULT value met",
5007 klass=self.__class__,
5008 decode_path=sub_decode_path,
5011 values[name] = value
5012 value_prev = v[:value_len]
5013 sub_offset += value_len
5016 obj = self.__class__(
5020 default=self.default,
5021 optional=self.optional,
5022 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5025 if v[:EOC_LEN].tobytes() != EOC:
5028 klass=self.__class__,
5029 decode_path=decode_path,
5037 "not all values are ready",
5038 klass=self.__class__,
5039 decode_path=decode_path,
5042 obj.ber_encoded = ber_encoded
5046 class SequenceOf(Obj):
5047 """``SEQUENCE OF`` sequence type
5049 For that kind of type you must specify the object it will carry on
5050 (bounds are for example here, not required)::
5052 class Ints(SequenceOf):
5057 >>> ints.append(Integer(123))
5058 >>> ints.append(Integer(234))
5060 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5061 >>> [int(i) for i in ints]
5063 >>> ints.append(Integer(345))
5064 Traceback (most recent call last):
5065 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5068 >>> ints[1] = Integer(345)
5070 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5072 Also you can initialize sequence with preinitialized values:
5074 >>> ints = Ints([Integer(123), Integer(234)])
5076 __slots__ = ("spec", "_bound_min", "_bound_max")
5077 tag_default = tag_encode(form=TagFormConstructed, num=16)
5078 asn1_type_name = "SEQUENCE OF"
5091 super(SequenceOf, self).__init__(
5099 schema = getattr(self, "schema", None)
5101 raise ValueError("schema must be specified")
5103 self._bound_min, self._bound_max = getattr(
5107 ) if bounds is None else bounds
5109 if value is not None:
5110 self._value = self._value_sanitize(value)
5111 if default is not None:
5112 default_value = self._value_sanitize(default)
5113 default_obj = self.__class__(
5118 default_obj._value = default_value
5119 self.default = default_obj
5121 self._value = default_obj.copy()._value
5123 def _value_sanitize(self, value):
5124 if issubclass(value.__class__, SequenceOf):
5125 value = value._value
5126 elif hasattr(value, "__iter__"):
5129 raise InvalidValueType((self.__class__, iter))
5130 if not self._bound_min <= len(value) <= self._bound_max:
5131 raise BoundsError(self._bound_min, len(value), self._bound_max)
5133 if not isinstance(v, self.spec.__class__):
5134 raise InvalidValueType((self.spec.__class__,))
5139 return all(v.ready for v in self._value)
5143 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5145 return any(v.bered for v in self._value)
5148 obj = self.__class__(schema=self.spec)
5149 obj._bound_min = self._bound_min
5150 obj._bound_max = self._bound_max
5152 obj._expl = self._expl
5153 obj.default = self.default
5154 obj.optional = self.optional
5155 obj.offset = self.offset
5156 obj.llen = self.llen
5157 obj.vlen = self.vlen
5158 obj.expl_lenindef = self.expl_lenindef
5159 obj.lenindef = self.lenindef
5160 obj.ber_encoded = self.ber_encoded
5161 obj._value = [v.copy() for v in self._value]
5164 def __eq__(self, their):
5165 if isinstance(their, self.__class__):
5167 self.spec == their.spec and
5168 self.tag == their.tag and
5169 self._expl == their._expl and
5170 self._value == their._value
5172 if hasattr(their, "__iter__"):
5173 return self._value == list(their)
5185 return self.__class__(
5189 (self._bound_min, self._bound_max)
5190 if bounds is None else bounds
5192 impl=self.tag if impl is None else impl,
5193 expl=self._expl if expl is None else expl,
5194 default=self.default if default is None else default,
5195 optional=self.optional if optional is None else optional,
5198 def __contains__(self, key):
5199 return key in self._value
5201 def append(self, value):
5202 if not isinstance(value, self.spec.__class__):
5203 raise InvalidValueType((self.spec.__class__,))
5204 if len(self._value) + 1 > self._bound_max:
5207 len(self._value) + 1,
5210 self._value.append(value)
5213 self._assert_ready()
5214 return iter(self._value)
5217 self._assert_ready()
5218 return len(self._value)
5220 def __setitem__(self, key, value):
5221 if not isinstance(value, self.spec.__class__):
5222 raise InvalidValueType((self.spec.__class__,))
5223 self._value[key] = self.spec(value=value)
5225 def __getitem__(self, key):
5226 return self._value[key]
5228 def _encoded_values(self):
5229 return [v.encode() for v in self._value]
5232 v = b"".join(self._encoded_values())
5233 return b"".join((self.tag, len_encode(len(v)), v))
5235 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5237 t, tlen, lv = tag_strip(tlv)
5238 except DecodeError as err:
5239 raise err.__class__(
5241 klass=self.__class__,
5242 decode_path=decode_path,
5247 klass=self.__class__,
5248 decode_path=decode_path,
5254 ctx_bered = ctx.get("bered", False)
5256 l, llen, v = len_decode(lv)
5257 except LenIndefForm as err:
5259 raise err.__class__(
5261 klass=self.__class__,
5262 decode_path=decode_path,
5265 l, llen, v = 0, 1, lv[1:]
5267 except DecodeError as err:
5268 raise err.__class__(
5270 klass=self.__class__,
5271 decode_path=decode_path,
5275 raise NotEnoughData(
5276 "encoded length is longer than data",
5277 klass=self.__class__,
5278 decode_path=decode_path,
5282 v, tail = v[:l], v[l:]
5284 sub_offset = offset + tlen + llen
5286 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5287 value_prev = memoryview(v[:0])
5291 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5293 sub_decode_path = decode_path + (str(len(_value)),)
5294 value, v_tail = spec.decode(
5298 decode_path=sub_decode_path,
5300 _ctx_immutable=False,
5302 value_len = value.fulllen
5304 if value_prev.tobytes() > v[:value_len].tobytes():
5305 if ctx_bered or ctx_allow_unordered_set:
5309 "unordered " + self.asn1_type_name,
5310 klass=self.__class__,
5311 decode_path=sub_decode_path,
5314 value_prev = v[:value_len]
5315 _value.append(value)
5316 sub_offset += value_len
5320 obj = self.__class__(
5323 bounds=(self._bound_min, self._bound_max),
5326 default=self.default,
5327 optional=self.optional,
5328 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5330 except BoundsError as err:
5333 klass=self.__class__,
5334 decode_path=decode_path,
5338 if v[:EOC_LEN].tobytes() != EOC:
5341 klass=self.__class__,
5342 decode_path=decode_path,
5347 obj.ber_encoded = ber_encoded
5352 pp_console_row(next(self.pps())),
5353 ", ".join(repr(v) for v in self._value),
5356 def pps(self, decode_path=()):
5359 asn1_type_name=self.asn1_type_name,
5360 obj_name=self.__class__.__name__,
5361 decode_path=decode_path,
5362 optional=self.optional,
5363 default=self == self.default,
5364 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5365 expl=None if self._expl is None else tag_decode(self._expl),
5370 expl_offset=self.expl_offset if self.expled else None,
5371 expl_tlen=self.expl_tlen if self.expled else None,
5372 expl_llen=self.expl_llen if self.expled else None,
5373 expl_vlen=self.expl_vlen if self.expled else None,
5374 expl_lenindef=self.expl_lenindef,
5375 lenindef=self.lenindef,
5376 ber_encoded=self.ber_encoded,
5379 for i, value in enumerate(self._value):
5380 yield value.pps(decode_path=decode_path + (str(i),))
5381 for pp in self.pps_lenindef(decode_path):
5385 class SetOf(SequenceOf):
5386 """``SET OF`` sequence type
5388 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5391 tag_default = tag_encode(form=TagFormConstructed, num=17)
5392 asn1_type_name = "SET OF"
5395 raws = self._encoded_values()
5398 return b"".join((self.tag, len_encode(len(v)), v))
5400 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5401 return super(SetOf, self)._decode(
5407 ordering_check=True,
5411 def obj_by_path(pypath): # pragma: no cover
5412 """Import object specified as string Python path
5414 Modules must be separated from classes/functions with ``:``.
5416 >>> obj_by_path("foo.bar:Baz")
5417 <class 'foo.bar.Baz'>
5418 >>> obj_by_path("foo.bar:Baz.boo")
5419 <classmethod 'foo.bar.Baz.boo'>
5421 mod, objs = pypath.rsplit(":", 1)
5422 from importlib import import_module
5423 obj = import_module(mod)
5424 for obj_name in objs.split("."):
5425 obj = getattr(obj, obj_name)
5429 def generic_decoder(): # pragma: no cover
5430 # All of this below is a big hack with self references
5431 choice = PrimitiveTypes()
5432 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5433 choice.specs["SetOf"] = SetOf(schema=choice)
5434 for i in six_xrange(31):
5435 choice.specs["SequenceOf%d" % i] = SequenceOf(
5439 choice.specs["Any"] = Any()
5441 # Class name equals to type name, to omit it from output
5442 class SEQUENCEOF(SequenceOf):
5450 with_decode_path=False,
5451 decode_path_only=(),
5453 def _pprint_pps(pps):
5455 if hasattr(pp, "_fields"):
5457 decode_path_only != () and
5458 pp.decode_path[:len(decode_path_only)] != decode_path_only
5461 if pp.asn1_type_name == Choice.asn1_type_name:
5463 pp_kwargs = pp._asdict()
5464 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5465 pp = _pp(**pp_kwargs)
5466 yield pp_console_row(
5471 with_colours=with_colours,
5472 with_decode_path=with_decode_path,
5473 decode_path_len_decrease=len(decode_path_only),
5475 for row in pp_console_blob(
5477 decode_path_len_decrease=len(decode_path_only),
5481 for row in _pprint_pps(pp):
5483 return "\n".join(_pprint_pps(obj.pps()))
5484 return SEQUENCEOF(), pprint_any
5487 def main(): # pragma: no cover
5489 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5490 parser.add_argument(
5494 help="Skip that number of bytes from the beginning",
5496 parser.add_argument(
5498 help="Python path to dictionary with OIDs",
5500 parser.add_argument(
5502 help="Python path to schema definition to use",
5504 parser.add_argument(
5505 "--defines-by-path",
5506 help="Python path to decoder's defines_by_path",
5508 parser.add_argument(
5510 action="store_true",
5511 help="Disallow BER encoding",
5513 parser.add_argument(
5514 "--print-decode-path",
5515 action="store_true",
5516 help="Print decode paths",
5518 parser.add_argument(
5519 "--decode-path-only",
5520 help="Print only specified decode path",
5522 parser.add_argument(
5524 action="store_true",
5525 help="Allow explicit tag out-of-bound",
5527 parser.add_argument(
5529 type=argparse.FileType("rb"),
5530 help="Path to DER file you want to decode",
5532 args = parser.parse_args()
5533 args.DERFile.seek(args.skip)
5534 der = memoryview(args.DERFile.read())
5535 args.DERFile.close()
5536 oids = obj_by_path(args.oids) if args.oids else {}
5538 schema = obj_by_path(args.schema)
5539 from functools import partial
5540 pprinter = partial(pprint, big_blobs=True)
5542 schema, pprinter = generic_decoder()
5544 "bered": not args.nobered,
5545 "allow_expl_oob": args.allow_expl_oob,
5547 if args.defines_by_path is not None:
5548 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5549 obj, tail = schema().decode(der, ctx=ctx)
5553 with_colours=True if environ.get("NO_COLOR") is None else False,
5554 with_decode_path=args.print_decode_path,
5556 () if args.decode_path_only is None else
5557 tuple(args.decode_path_only.split(":"))
5561 print("\nTrailing data: %s" % hexenc(tail))
5564 if __name__ == "__main__":