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 range(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 range(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
1574 def __nonzero__(self):
1575 self._assert_ready()
1579 self._assert_ready()
1582 def __eq__(self, their):
1583 if isinstance(their, bool):
1584 return self._value == their
1585 if not issubclass(their.__class__, Boolean):
1588 self._value == their._value and
1589 self.tag == their.tag and
1590 self._expl == their._expl
1601 return self.__class__(
1603 impl=self.tag if impl is None else impl,
1604 expl=self._expl if expl is None else expl,
1605 default=self.default if default is None else default,
1606 optional=self.optional if optional is None else optional,
1610 self._assert_ready()
1614 (b"\xFF" if self._value else b"\x00"),
1617 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1619 t, _, lv = tag_strip(tlv)
1620 except DecodeError as err:
1621 raise err.__class__(
1623 klass=self.__class__,
1624 decode_path=decode_path,
1629 klass=self.__class__,
1630 decode_path=decode_path,
1636 l, _, v = len_decode(lv)
1637 except DecodeError as err:
1638 raise err.__class__(
1640 klass=self.__class__,
1641 decode_path=decode_path,
1645 raise InvalidLength(
1646 "Boolean's length must be equal to 1",
1647 klass=self.__class__,
1648 decode_path=decode_path,
1652 raise NotEnoughData(
1653 "encoded length is longer than data",
1654 klass=self.__class__,
1655 decode_path=decode_path,
1658 first_octet = byte2int(v)
1660 if first_octet == 0:
1662 elif first_octet == 0xFF:
1664 elif ctx.get("bered", False):
1669 "unacceptable Boolean value",
1670 klass=self.__class__,
1671 decode_path=decode_path,
1674 obj = self.__class__(
1678 default=self.default,
1679 optional=self.optional,
1680 _decoded=(offset, 1, 1),
1682 obj.ber_encoded = ber_encoded
1686 return pp_console_row(next(self.pps()))
1688 def pps(self, decode_path=()):
1691 asn1_type_name=self.asn1_type_name,
1692 obj_name=self.__class__.__name__,
1693 decode_path=decode_path,
1694 value=str(self._value) if self.ready else None,
1695 optional=self.optional,
1696 default=self == self.default,
1697 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1698 expl=None if self._expl is None else tag_decode(self._expl),
1703 expl_offset=self.expl_offset if self.expled else None,
1704 expl_tlen=self.expl_tlen if self.expled else None,
1705 expl_llen=self.expl_llen if self.expled else None,
1706 expl_vlen=self.expl_vlen if self.expled else None,
1707 expl_lenindef=self.expl_lenindef,
1708 ber_encoded=self.ber_encoded,
1711 for pp in self.pps_lenindef(decode_path):
1716 """``INTEGER`` integer type
1718 >>> b = Integer(-123)
1720 >>> b == Integer(-123)
1725 >>> Integer(2, bounds=(1, 3))
1727 >>> Integer(5, bounds=(1, 3))
1728 Traceback (most recent call last):
1729 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1733 class Version(Integer):
1740 >>> v = Version("v1")
1747 {'v3': 2, 'v1': 0, 'v2': 1}
1749 __slots__ = ("specs", "_bound_min", "_bound_max")
1750 tag_default = tag_encode(2)
1751 asn1_type_name = "INTEGER"
1765 :param value: set the value. Either integer type, named value
1766 (if ``schema`` is specified in the class), or
1767 :py:class:`pyderasn.Integer` object
1768 :param bounds: set ``(MIN, MAX)`` value constraint.
1769 (-inf, +inf) by default
1770 :param bytes impl: override default tag with ``IMPLICIT`` one
1771 :param bytes expl: override default tag with ``EXPLICIT`` one
1772 :param default: set default value. Type same as in ``value``
1773 :param bool optional: is object ``OPTIONAL`` in sequence
1775 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1777 specs = getattr(self, "schema", {}) if _specs is None else _specs
1778 self.specs = specs if isinstance(specs, dict) else dict(specs)
1779 self._bound_min, self._bound_max = getattr(
1782 (float("-inf"), float("+inf")),
1783 ) if bounds is None else bounds
1784 if value is not None:
1785 self._value = self._value_sanitize(value)
1786 if default is not None:
1787 default = self._value_sanitize(default)
1788 self.default = self.__class__(
1794 if self._value is None:
1795 self._value = default
1797 def _value_sanitize(self, value):
1798 if issubclass(value.__class__, Integer):
1799 value = value._value
1800 elif isinstance(value, integer_types):
1802 elif isinstance(value, str):
1803 value = self.specs.get(value)
1805 raise ObjUnknown("integer value: %s" % value)
1807 raise InvalidValueType((self.__class__, int, str))
1808 if not self._bound_min <= value <= self._bound_max:
1809 raise BoundsError(self._bound_min, value, self._bound_max)
1814 return self._value is not None
1817 obj = self.__class__(_specs=self.specs)
1818 obj._value = self._value
1819 obj._bound_min = self._bound_min
1820 obj._bound_max = self._bound_max
1822 obj._expl = self._expl
1823 obj.default = self.default
1824 obj.optional = self.optional
1825 obj.offset = self.offset
1826 obj.llen = self.llen
1827 obj.vlen = self.vlen
1831 self._assert_ready()
1832 return int(self._value)
1835 self._assert_ready()
1838 bytes(self._expl or b"") +
1839 str(self._value).encode("ascii"),
1842 def __eq__(self, their):
1843 if isinstance(their, integer_types):
1844 return self._value == their
1845 if not issubclass(their.__class__, Integer):
1848 self._value == their._value and
1849 self.tag == their.tag and
1850 self._expl == their._expl
1853 def __lt__(self, their):
1854 return self._value < their._value
1858 for name, value in self.specs.items():
1859 if value == self._value:
1871 return self.__class__(
1874 (self._bound_min, self._bound_max)
1875 if bounds is None else bounds
1877 impl=self.tag if impl is None else impl,
1878 expl=self._expl if expl is None else expl,
1879 default=self.default if default is None else default,
1880 optional=self.optional if optional is None else optional,
1885 self._assert_ready()
1889 octets = bytearray([0])
1893 octets = bytearray()
1895 octets.append((value & 0xFF) ^ 0xFF)
1897 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1900 octets = bytearray()
1902 octets.append(value & 0xFF)
1904 if octets[-1] & 0x80 > 0:
1907 octets = bytes(octets)
1909 bytes_len = ceil(value.bit_length() / 8) or 1
1912 octets = value.to_bytes(
1917 except OverflowError:
1921 return b"".join((self.tag, len_encode(len(octets)), octets))
1923 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1925 t, _, lv = tag_strip(tlv)
1926 except DecodeError as err:
1927 raise err.__class__(
1929 klass=self.__class__,
1930 decode_path=decode_path,
1935 klass=self.__class__,
1936 decode_path=decode_path,
1942 l, llen, v = len_decode(lv)
1943 except DecodeError as err:
1944 raise err.__class__(
1946 klass=self.__class__,
1947 decode_path=decode_path,
1951 raise NotEnoughData(
1952 "encoded length is longer than data",
1953 klass=self.__class__,
1954 decode_path=decode_path,
1958 raise NotEnoughData(
1960 klass=self.__class__,
1961 decode_path=decode_path,
1964 v, tail = v[:l], v[l:]
1965 first_octet = byte2int(v)
1967 second_octet = byte2int(v[1:])
1969 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1970 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1973 "non normalized integer",
1974 klass=self.__class__,
1975 decode_path=decode_path,
1980 if first_octet & 0x80 > 0:
1981 octets = bytearray()
1982 for octet in bytearray(v):
1983 octets.append(octet ^ 0xFF)
1984 for octet in octets:
1985 value = (value << 8) | octet
1989 for octet in bytearray(v):
1990 value = (value << 8) | octet
1992 value = int.from_bytes(v, byteorder="big", signed=True)
1994 obj = self.__class__(
1996 bounds=(self._bound_min, self._bound_max),
1999 default=self.default,
2000 optional=self.optional,
2002 _decoded=(offset, llen, l),
2004 except BoundsError as err:
2007 klass=self.__class__,
2008 decode_path=decode_path,
2014 return pp_console_row(next(self.pps()))
2016 def pps(self, decode_path=()):
2019 asn1_type_name=self.asn1_type_name,
2020 obj_name=self.__class__.__name__,
2021 decode_path=decode_path,
2022 value=(self.named or str(self._value)) if self.ready else None,
2023 optional=self.optional,
2024 default=self == self.default,
2025 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2026 expl=None if self._expl is None else tag_decode(self._expl),
2031 expl_offset=self.expl_offset if self.expled else None,
2032 expl_tlen=self.expl_tlen if self.expled else None,
2033 expl_llen=self.expl_llen if self.expled else None,
2034 expl_vlen=self.expl_vlen if self.expled else None,
2035 expl_lenindef=self.expl_lenindef,
2038 for pp in self.pps_lenindef(decode_path):
2042 class BitString(Obj):
2043 """``BIT STRING`` bit string type
2045 >>> BitString(b"hello world")
2046 BIT STRING 88 bits 68656c6c6f20776f726c64
2049 >>> b == b"hello world"
2054 >>> BitString("'0A3B5F291CD'H")
2055 BIT STRING 44 bits 0a3b5f291cd0
2056 >>> b = BitString("'010110000000'B")
2057 BIT STRING 12 bits 5800
2060 >>> b[0], b[1], b[2], b[3]
2061 (False, True, False, True)
2065 [False, True, False, True, True, False, False, False, False, False, False, False]
2069 class KeyUsage(BitString):
2071 ("digitalSignature", 0),
2072 ("nonRepudiation", 1),
2073 ("keyEncipherment", 2),
2076 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2077 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2079 ['nonRepudiation', 'keyEncipherment']
2081 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2085 Pay attention that BIT STRING can be encoded both in primitive
2086 and constructed forms. Decoder always checks constructed form tag
2087 additionally to specified primitive one. If BER decoding is
2088 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2089 of DER restrictions.
2091 __slots__ = ("tag_constructed", "specs", "defined")
2092 tag_default = tag_encode(3)
2093 asn1_type_name = "BIT STRING"
2106 :param value: set the value. Either binary type, tuple of named
2107 values (if ``schema`` is specified in the class),
2108 string in ``'XXX...'B`` form, or
2109 :py:class:`pyderasn.BitString` object
2110 :param bytes impl: override default tag with ``IMPLICIT`` one
2111 :param bytes expl: override default tag with ``EXPLICIT`` one
2112 :param default: set default value. Type same as in ``value``
2113 :param bool optional: is object ``OPTIONAL`` in sequence
2115 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2116 specs = getattr(self, "schema", {}) if _specs is None else _specs
2117 self.specs = specs if isinstance(specs, dict) else dict(specs)
2118 self._value = None if value is None else self._value_sanitize(value)
2119 if default is not None:
2120 default = self._value_sanitize(default)
2121 self.default = self.__class__(
2127 self._value = default
2129 tag_klass, _, tag_num = tag_decode(self.tag)
2130 self.tag_constructed = tag_encode(
2132 form=TagFormConstructed,
2136 def _bits2octets(self, bits):
2137 if len(self.specs) > 0:
2138 bits = bits.rstrip("0")
2140 bits += "0" * ((8 - (bit_len % 8)) % 8)
2141 octets = bytearray(len(bits) // 8)
2142 for i in six_xrange(len(octets)):
2143 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2144 return bit_len, bytes(octets)
2146 def _value_sanitize(self, value):
2147 if issubclass(value.__class__, BitString):
2149 if isinstance(value, (string_types, binary_type)):
2151 isinstance(value, string_types) and
2152 value.startswith("'")
2154 if value.endswith("'B"):
2156 if not set(value) <= set(("0", "1")):
2157 raise ValueError("B's coding contains unacceptable chars")
2158 return self._bits2octets(value)
2159 elif value.endswith("'H"):
2163 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2165 if isinstance(value, binary_type):
2166 return (len(value) * 8, value)
2168 raise InvalidValueType((self.__class__, string_types, binary_type))
2169 if isinstance(value, tuple):
2172 isinstance(value[0], integer_types) and
2173 isinstance(value[1], binary_type)
2178 bit = self.specs.get(name)
2180 raise ObjUnknown("BitString value: %s" % name)
2183 return self._bits2octets("")
2185 return self._bits2octets("".join(
2186 ("1" if bit in bits else "0")
2187 for bit in six_xrange(max(bits) + 1)
2189 raise InvalidValueType((self.__class__, binary_type, string_types))
2193 return self._value is not None
2196 obj = self.__class__(_specs=self.specs)
2198 if value is not None:
2199 value = (value[0], value[1])
2202 obj._expl = self._expl
2203 obj.default = self.default
2204 obj.optional = self.optional
2205 obj.offset = self.offset
2206 obj.llen = self.llen
2207 obj.vlen = self.vlen
2211 self._assert_ready()
2212 for i in six_xrange(self._value[0]):
2217 self._assert_ready()
2218 return self._value[0]
2220 def __bytes__(self):
2221 self._assert_ready()
2222 return self._value[1]
2224 def __eq__(self, their):
2225 if isinstance(their, bytes):
2226 return self._value[1] == their
2227 if not issubclass(their.__class__, BitString):
2230 self._value == their._value and
2231 self.tag == their.tag and
2232 self._expl == their._expl
2237 return [name for name, bit in self.specs.items() if self[bit]]
2247 return self.__class__(
2249 impl=self.tag if impl is None else impl,
2250 expl=self._expl if expl is None else expl,
2251 default=self.default if default is None else default,
2252 optional=self.optional if optional is None else optional,
2256 def __getitem__(self, key):
2257 if isinstance(key, int):
2258 bit_len, octets = self._value
2262 byte2int(memoryview(octets)[key // 8:]) >>
2265 if isinstance(key, string_types):
2266 value = self.specs.get(key)
2268 raise ObjUnknown("BitString value: %s" % key)
2270 raise InvalidValueType((int, str))
2273 self._assert_ready()
2274 bit_len, octets = self._value
2277 len_encode(len(octets) + 1),
2278 int2byte((8 - bit_len % 8) % 8),
2282 def _decode_chunk(self, lv, offset, decode_path, ctx):
2284 l, llen, v = len_decode(lv)
2285 except DecodeError as err:
2286 raise err.__class__(
2288 klass=self.__class__,
2289 decode_path=decode_path,
2293 raise NotEnoughData(
2294 "encoded length is longer than data",
2295 klass=self.__class__,
2296 decode_path=decode_path,
2300 raise NotEnoughData(
2302 klass=self.__class__,
2303 decode_path=decode_path,
2306 pad_size = byte2int(v)
2307 if l == 1 and pad_size != 0:
2309 "invalid empty value",
2310 klass=self.__class__,
2311 decode_path=decode_path,
2317 klass=self.__class__,
2318 decode_path=decode_path,
2321 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2324 klass=self.__class__,
2325 decode_path=decode_path,
2328 v, tail = v[:l], v[l:]
2329 obj = self.__class__(
2330 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2333 default=self.default,
2334 optional=self.optional,
2336 _decoded=(offset, llen, l),
2340 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2342 t, tlen, lv = tag_strip(tlv)
2343 except DecodeError as err:
2344 raise err.__class__(
2346 klass=self.__class__,
2347 decode_path=decode_path,
2351 if tag_only: # pragma: no cover
2353 return self._decode_chunk(lv, offset, decode_path, ctx)
2354 if t == self.tag_constructed:
2355 if not ctx.get("bered", False):
2357 "unallowed BER constructed encoding",
2358 klass=self.__class__,
2359 decode_path=decode_path,
2362 if tag_only: # pragma: no cover
2366 l, llen, v = len_decode(lv)
2367 except LenIndefForm:
2368 llen, l, v = 1, 0, lv[1:]
2370 except DecodeError as err:
2371 raise err.__class__(
2373 klass=self.__class__,
2374 decode_path=decode_path,
2378 raise NotEnoughData(
2379 "encoded length is longer than data",
2380 klass=self.__class__,
2381 decode_path=decode_path,
2384 if not lenindef and l == 0:
2385 raise NotEnoughData(
2387 klass=self.__class__,
2388 decode_path=decode_path,
2392 sub_offset = offset + tlen + llen
2396 if v[:EOC_LEN].tobytes() == EOC:
2403 "chunk out of bounds",
2404 klass=self.__class__,
2405 decode_path=decode_path + (str(len(chunks) - 1),),
2406 offset=chunks[-1].offset,
2408 sub_decode_path = decode_path + (str(len(chunks)),)
2410 chunk, v_tail = BitString().decode(
2413 decode_path=sub_decode_path,
2416 _ctx_immutable=False,
2420 "expected BitString encoded chunk",
2421 klass=self.__class__,
2422 decode_path=sub_decode_path,
2425 chunks.append(chunk)
2426 sub_offset += chunk.tlvlen
2427 vlen += chunk.tlvlen
2429 if len(chunks) == 0:
2432 klass=self.__class__,
2433 decode_path=decode_path,
2438 for chunk_i, chunk in enumerate(chunks[:-1]):
2439 if chunk.bit_len % 8 != 0:
2441 "BitString chunk is not multiple of 8 bits",
2442 klass=self.__class__,
2443 decode_path=decode_path + (str(chunk_i),),
2444 offset=chunk.offset,
2446 values.append(bytes(chunk))
2447 bit_len += chunk.bit_len
2448 chunk_last = chunks[-1]
2449 values.append(bytes(chunk_last))
2450 bit_len += chunk_last.bit_len
2451 obj = self.__class__(
2452 value=(bit_len, b"".join(values)),
2455 default=self.default,
2456 optional=self.optional,
2458 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2460 obj.lenindef = lenindef
2461 obj.ber_encoded = True
2462 return obj, (v[EOC_LEN:] if lenindef else v)
2464 klass=self.__class__,
2465 decode_path=decode_path,
2470 return pp_console_row(next(self.pps()))
2472 def pps(self, decode_path=()):
2476 bit_len, blob = self._value
2477 value = "%d bits" % bit_len
2478 if len(self.specs) > 0:
2479 blob = tuple(self.named)
2482 asn1_type_name=self.asn1_type_name,
2483 obj_name=self.__class__.__name__,
2484 decode_path=decode_path,
2487 optional=self.optional,
2488 default=self == self.default,
2489 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2490 expl=None if self._expl is None else tag_decode(self._expl),
2495 expl_offset=self.expl_offset if self.expled else None,
2496 expl_tlen=self.expl_tlen if self.expled else None,
2497 expl_llen=self.expl_llen if self.expled else None,
2498 expl_vlen=self.expl_vlen if self.expled else None,
2499 expl_lenindef=self.expl_lenindef,
2500 lenindef=self.lenindef,
2501 ber_encoded=self.ber_encoded,
2504 defined_by, defined = self.defined or (None, None)
2505 if defined_by is not None:
2507 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2509 for pp in self.pps_lenindef(decode_path):
2513 class OctetString(Obj):
2514 """``OCTET STRING`` binary string type
2516 >>> s = OctetString(b"hello world")
2517 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2518 >>> s == OctetString(b"hello world")
2523 >>> OctetString(b"hello", bounds=(4, 4))
2524 Traceback (most recent call last):
2525 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2526 >>> OctetString(b"hell", bounds=(4, 4))
2527 OCTET STRING 4 bytes 68656c6c
2531 Pay attention that OCTET STRING can be encoded both in primitive
2532 and constructed forms. Decoder always checks constructed form tag
2533 additionally to specified primitive one. If BER decoding is
2534 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2535 of DER restrictions.
2537 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2538 tag_default = tag_encode(4)
2539 asn1_type_name = "OCTET STRING"
2552 :param value: set the value. Either binary type, or
2553 :py:class:`pyderasn.OctetString` object
2554 :param bounds: set ``(MIN, MAX)`` value size constraint.
2555 (-inf, +inf) by default
2556 :param bytes impl: override default tag with ``IMPLICIT`` one
2557 :param bytes expl: override default tag with ``EXPLICIT`` one
2558 :param default: set default value. Type same as in ``value``
2559 :param bool optional: is object ``OPTIONAL`` in sequence
2561 super(OctetString, self).__init__(
2569 self._bound_min, self._bound_max = getattr(
2573 ) if bounds is None else bounds
2574 if value is not None:
2575 self._value = self._value_sanitize(value)
2576 if default is not None:
2577 default = self._value_sanitize(default)
2578 self.default = self.__class__(
2583 if self._value is None:
2584 self._value = default
2586 tag_klass, _, tag_num = tag_decode(self.tag)
2587 self.tag_constructed = tag_encode(
2589 form=TagFormConstructed,
2593 def _value_sanitize(self, value):
2594 if issubclass(value.__class__, OctetString):
2595 value = value._value
2596 elif isinstance(value, binary_type):
2599 raise InvalidValueType((self.__class__, bytes))
2600 if not self._bound_min <= len(value) <= self._bound_max:
2601 raise BoundsError(self._bound_min, len(value), self._bound_max)
2606 return self._value is not None
2609 obj = self.__class__()
2610 obj._value = self._value
2611 obj._bound_min = self._bound_min
2612 obj._bound_max = self._bound_max
2614 obj._expl = self._expl
2615 obj.default = self.default
2616 obj.optional = self.optional
2617 obj.offset = self.offset
2618 obj.llen = self.llen
2619 obj.vlen = self.vlen
2622 def __bytes__(self):
2623 self._assert_ready()
2626 def __eq__(self, their):
2627 if isinstance(their, binary_type):
2628 return self._value == their
2629 if not issubclass(their.__class__, OctetString):
2632 self._value == their._value and
2633 self.tag == their.tag and
2634 self._expl == their._expl
2637 def __lt__(self, their):
2638 return self._value < their._value
2649 return self.__class__(
2652 (self._bound_min, self._bound_max)
2653 if bounds is None else bounds
2655 impl=self.tag if impl is None else impl,
2656 expl=self._expl if expl is None else expl,
2657 default=self.default if default is None else default,
2658 optional=self.optional if optional is None else optional,
2662 self._assert_ready()
2665 len_encode(len(self._value)),
2669 def _decode_chunk(self, lv, offset, decode_path, ctx):
2671 l, llen, v = len_decode(lv)
2672 except DecodeError as err:
2673 raise err.__class__(
2675 klass=self.__class__,
2676 decode_path=decode_path,
2680 raise NotEnoughData(
2681 "encoded length is longer than data",
2682 klass=self.__class__,
2683 decode_path=decode_path,
2686 v, tail = v[:l], v[l:]
2688 obj = self.__class__(
2690 bounds=(self._bound_min, self._bound_max),
2693 default=self.default,
2694 optional=self.optional,
2695 _decoded=(offset, llen, l),
2697 except DecodeError as err:
2700 klass=self.__class__,
2701 decode_path=decode_path,
2704 except BoundsError as err:
2707 klass=self.__class__,
2708 decode_path=decode_path,
2713 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2715 t, tlen, lv = tag_strip(tlv)
2716 except DecodeError as err:
2717 raise err.__class__(
2719 klass=self.__class__,
2720 decode_path=decode_path,
2726 return self._decode_chunk(lv, offset, decode_path, ctx)
2727 if t == self.tag_constructed:
2728 if not ctx.get("bered", False):
2730 "unallowed BER constructed encoding",
2731 klass=self.__class__,
2732 decode_path=decode_path,
2739 l, llen, v = len_decode(lv)
2740 except LenIndefForm:
2741 llen, l, v = 1, 0, lv[1:]
2743 except DecodeError as err:
2744 raise err.__class__(
2746 klass=self.__class__,
2747 decode_path=decode_path,
2751 raise NotEnoughData(
2752 "encoded length is longer than data",
2753 klass=self.__class__,
2754 decode_path=decode_path,
2758 sub_offset = offset + tlen + llen
2762 if v[:EOC_LEN].tobytes() == EOC:
2769 "chunk out of bounds",
2770 klass=self.__class__,
2771 decode_path=decode_path + (str(len(chunks) - 1),),
2772 offset=chunks[-1].offset,
2774 sub_decode_path = decode_path + (str(len(chunks)),)
2776 chunk, v_tail = OctetString().decode(
2779 decode_path=sub_decode_path,
2782 _ctx_immutable=False,
2786 "expected OctetString encoded chunk",
2787 klass=self.__class__,
2788 decode_path=sub_decode_path,
2791 chunks.append(chunk)
2792 sub_offset += chunk.tlvlen
2793 vlen += chunk.tlvlen
2796 obj = self.__class__(
2797 value=b"".join(bytes(chunk) for chunk in chunks),
2798 bounds=(self._bound_min, self._bound_max),
2801 default=self.default,
2802 optional=self.optional,
2803 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2805 except DecodeError as err:
2808 klass=self.__class__,
2809 decode_path=decode_path,
2812 except BoundsError as err:
2815 klass=self.__class__,
2816 decode_path=decode_path,
2819 obj.lenindef = lenindef
2820 obj.ber_encoded = True
2821 return obj, (v[EOC_LEN:] if lenindef else v)
2823 klass=self.__class__,
2824 decode_path=decode_path,
2829 return pp_console_row(next(self.pps()))
2831 def pps(self, decode_path=()):
2834 asn1_type_name=self.asn1_type_name,
2835 obj_name=self.__class__.__name__,
2836 decode_path=decode_path,
2837 value=("%d bytes" % len(self._value)) if self.ready else None,
2838 blob=self._value if self.ready else None,
2839 optional=self.optional,
2840 default=self == self.default,
2841 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2842 expl=None if self._expl is None else tag_decode(self._expl),
2847 expl_offset=self.expl_offset if self.expled else None,
2848 expl_tlen=self.expl_tlen if self.expled else None,
2849 expl_llen=self.expl_llen if self.expled else None,
2850 expl_vlen=self.expl_vlen if self.expled else None,
2851 expl_lenindef=self.expl_lenindef,
2852 lenindef=self.lenindef,
2853 ber_encoded=self.ber_encoded,
2856 defined_by, defined = self.defined or (None, None)
2857 if defined_by is not None:
2859 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2861 for pp in self.pps_lenindef(decode_path):
2866 """``NULL`` null object
2874 tag_default = tag_encode(5)
2875 asn1_type_name = "NULL"
2879 value=None, # unused, but Sequence passes it
2886 :param bytes impl: override default tag with ``IMPLICIT`` one
2887 :param bytes expl: override default tag with ``EXPLICIT`` one
2888 :param bool optional: is object ``OPTIONAL`` in sequence
2890 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2898 obj = self.__class__()
2900 obj._expl = self._expl
2901 obj.default = self.default
2902 obj.optional = self.optional
2903 obj.offset = self.offset
2904 obj.llen = self.llen
2905 obj.vlen = self.vlen
2908 def __eq__(self, their):
2909 if not issubclass(their.__class__, Null):
2912 self.tag == their.tag and
2913 self._expl == their._expl
2923 return self.__class__(
2924 impl=self.tag if impl is None else impl,
2925 expl=self._expl if expl is None else expl,
2926 optional=self.optional if optional is None else optional,
2930 return self.tag + len_encode(0)
2932 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2934 t, _, lv = tag_strip(tlv)
2935 except DecodeError as err:
2936 raise err.__class__(
2938 klass=self.__class__,
2939 decode_path=decode_path,
2944 klass=self.__class__,
2945 decode_path=decode_path,
2948 if tag_only: # pragma: no cover
2951 l, _, v = len_decode(lv)
2952 except DecodeError as err:
2953 raise err.__class__(
2955 klass=self.__class__,
2956 decode_path=decode_path,
2960 raise InvalidLength(
2961 "Null must have zero length",
2962 klass=self.__class__,
2963 decode_path=decode_path,
2966 obj = self.__class__(
2969 optional=self.optional,
2970 _decoded=(offset, 1, 0),
2975 return pp_console_row(next(self.pps()))
2977 def pps(self, decode_path=()):
2980 asn1_type_name=self.asn1_type_name,
2981 obj_name=self.__class__.__name__,
2982 decode_path=decode_path,
2983 optional=self.optional,
2984 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2985 expl=None if self._expl is None else tag_decode(self._expl),
2990 expl_offset=self.expl_offset if self.expled else None,
2991 expl_tlen=self.expl_tlen if self.expled else None,
2992 expl_llen=self.expl_llen if self.expled else None,
2993 expl_vlen=self.expl_vlen if self.expled else None,
2994 expl_lenindef=self.expl_lenindef,
2997 for pp in self.pps_lenindef(decode_path):
3001 class ObjectIdentifier(Obj):
3002 """``OBJECT IDENTIFIER`` OID type
3004 >>> oid = ObjectIdentifier((1, 2, 3))
3005 OBJECT IDENTIFIER 1.2.3
3006 >>> oid == ObjectIdentifier("1.2.3")
3012 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3013 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3015 >>> str(ObjectIdentifier((3, 1)))
3016 Traceback (most recent call last):
3017 pyderasn.InvalidOID: unacceptable first arc value
3019 __slots__ = ("defines",)
3020 tag_default = tag_encode(6)
3021 asn1_type_name = "OBJECT IDENTIFIER"
3034 :param value: set the value. Either tuples of integers,
3035 string of "."-concatenated integers, or
3036 :py:class:`pyderasn.ObjectIdentifier` object
3037 :param defines: sequence of tuples. Each tuple has two elements.
3038 First one is relative to current one decode
3039 path, aiming to the field defined by that OID.
3040 Read about relative path in
3041 :py:func:`pyderasn.abs_decode_path`. Second
3042 tuple element is ``{OID: pyderasn.Obj()}``
3043 dictionary, mapping between current OID value
3044 and structure applied to defined field.
3045 :ref:`Read about DEFINED BY <definedby>`
3046 :param bytes impl: override default tag with ``IMPLICIT`` one
3047 :param bytes expl: override default tag with ``EXPLICIT`` one
3048 :param default: set default value. Type same as in ``value``
3049 :param bool optional: is object ``OPTIONAL`` in sequence
3051 super(ObjectIdentifier, self).__init__(
3059 if value is not None:
3060 self._value = self._value_sanitize(value)
3061 if default is not None:
3062 default = self._value_sanitize(default)
3063 self.default = self.__class__(
3068 if self._value is None:
3069 self._value = default
3070 self.defines = defines
3072 def __add__(self, their):
3073 if isinstance(their, self.__class__):
3074 return self.__class__(self._value + their._value)
3075 if isinstance(their, tuple):
3076 return self.__class__(self._value + their)
3077 raise InvalidValueType((self.__class__, tuple))
3079 def _value_sanitize(self, value):
3080 if issubclass(value.__class__, ObjectIdentifier):
3082 if isinstance(value, string_types):
3084 value = tuple(int(arc) for arc in value.split("."))
3086 raise InvalidOID("unacceptable arcs values")
3087 if isinstance(value, tuple):
3089 raise InvalidOID("less than 2 arcs")
3090 first_arc = value[0]
3091 if first_arc in (0, 1):
3092 if not (0 <= value[1] <= 39):
3093 raise InvalidOID("second arc is too wide")
3094 elif first_arc == 2:
3097 raise InvalidOID("unacceptable first arc value")
3099 raise InvalidValueType((self.__class__, str, tuple))
3103 return self._value is not None
3106 obj = self.__class__()
3107 obj._value = self._value
3108 obj.defines = self.defines
3110 obj._expl = self._expl
3111 obj.default = self.default
3112 obj.optional = self.optional
3113 obj.offset = self.offset
3114 obj.llen = self.llen
3115 obj.vlen = self.vlen
3119 self._assert_ready()
3120 return iter(self._value)
3123 return ".".join(str(arc) for arc in self._value or ())
3126 self._assert_ready()
3129 bytes(self._expl or b"") +
3130 str(self._value).encode("ascii"),
3133 def __eq__(self, their):
3134 if isinstance(their, tuple):
3135 return self._value == their
3136 if not issubclass(their.__class__, ObjectIdentifier):
3139 self.tag == their.tag and
3140 self._expl == their._expl and
3141 self._value == their._value
3144 def __lt__(self, their):
3145 return self._value < their._value
3156 return self.__class__(
3158 defines=self.defines if defines is None else defines,
3159 impl=self.tag if impl is None else impl,
3160 expl=self._expl if expl is None else expl,
3161 default=self.default if default is None else default,
3162 optional=self.optional if optional is None else optional,
3166 self._assert_ready()
3168 first_value = value[1]
3169 first_arc = value[0]
3172 elif first_arc == 1:
3174 elif first_arc == 2:
3176 else: # pragma: no cover
3177 raise RuntimeError("invalid arc is stored")
3178 octets = [zero_ended_encode(first_value)]
3179 for arc in value[2:]:
3180 octets.append(zero_ended_encode(arc))
3181 v = b"".join(octets)
3182 return b"".join((self.tag, len_encode(len(v)), v))
3184 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3186 t, _, lv = tag_strip(tlv)
3187 except DecodeError as err:
3188 raise err.__class__(
3190 klass=self.__class__,
3191 decode_path=decode_path,
3196 klass=self.__class__,
3197 decode_path=decode_path,
3200 if tag_only: # pragma: no cover
3203 l, llen, v = len_decode(lv)
3204 except DecodeError as err:
3205 raise err.__class__(
3207 klass=self.__class__,
3208 decode_path=decode_path,
3212 raise NotEnoughData(
3213 "encoded length is longer than data",
3214 klass=self.__class__,
3215 decode_path=decode_path,
3219 raise NotEnoughData(
3221 klass=self.__class__,
3222 decode_path=decode_path,
3225 v, tail = v[:l], v[l:]
3232 octet = indexbytes(v, i)
3233 if i == 0 and octet == 0x80:
3234 if ctx.get("bered", False):
3237 raise DecodeError("non normalized arc encoding")
3238 arc = (arc << 7) | (octet & 0x7F)
3239 if octet & 0x80 == 0:
3247 klass=self.__class__,
3248 decode_path=decode_path,
3252 second_arc = arcs[0]
3253 if 0 <= second_arc <= 39:
3255 elif 40 <= second_arc <= 79:
3261 obj = self.__class__(
3262 value=tuple([first_arc, second_arc] + arcs[1:]),
3265 default=self.default,
3266 optional=self.optional,
3267 _decoded=(offset, llen, l),
3270 obj.ber_encoded = True
3274 return pp_console_row(next(self.pps()))
3276 def pps(self, decode_path=()):
3279 asn1_type_name=self.asn1_type_name,
3280 obj_name=self.__class__.__name__,
3281 decode_path=decode_path,
3282 value=str(self) if self.ready else None,
3283 optional=self.optional,
3284 default=self == self.default,
3285 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3286 expl=None if self._expl is None else tag_decode(self._expl),
3291 expl_offset=self.expl_offset if self.expled else None,
3292 expl_tlen=self.expl_tlen if self.expled else None,
3293 expl_llen=self.expl_llen if self.expled else None,
3294 expl_vlen=self.expl_vlen if self.expled else None,
3295 expl_lenindef=self.expl_lenindef,
3296 ber_encoded=self.ber_encoded,
3299 for pp in self.pps_lenindef(decode_path):
3303 class Enumerated(Integer):
3304 """``ENUMERATED`` integer type
3306 This type is identical to :py:class:`pyderasn.Integer`, but requires
3307 schema to be specified and does not accept values missing from it.
3310 tag_default = tag_encode(10)
3311 asn1_type_name = "ENUMERATED"
3322 bounds=None, # dummy argument, workability for Integer.decode
3324 super(Enumerated, self).__init__(
3333 if len(self.specs) == 0:
3334 raise ValueError("schema must be specified")
3336 def _value_sanitize(self, value):
3337 if isinstance(value, self.__class__):
3338 value = value._value
3339 elif isinstance(value, integer_types):
3340 if value not in list(self.specs.values()):
3342 "unknown integer value: %s" % value,
3343 klass=self.__class__,
3345 elif isinstance(value, string_types):
3346 value = self.specs.get(value)
3348 raise ObjUnknown("integer value: %s" % value)
3350 raise InvalidValueType((self.__class__, int, str))
3354 obj = self.__class__(_specs=self.specs)
3355 obj._value = self._value
3356 obj._bound_min = self._bound_min
3357 obj._bound_max = self._bound_max
3359 obj._expl = self._expl
3360 obj.default = self.default
3361 obj.optional = self.optional
3362 obj.offset = self.offset
3363 obj.llen = self.llen
3364 obj.vlen = self.vlen
3376 return self.__class__(
3378 impl=self.tag if impl is None else impl,
3379 expl=self._expl if expl is None else expl,
3380 default=self.default if default is None else default,
3381 optional=self.optional if optional is None else optional,
3386 class CommonString(OctetString):
3387 """Common class for all strings
3389 Everything resembles :py:class:`pyderasn.OctetString`, except
3390 ability to deal with unicode text strings.
3392 >>> hexenc("привет мир".encode("utf-8"))
3393 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3394 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3396 >>> s = UTF8String("привет мир")
3397 UTF8String UTF8String привет мир
3399 'привет мир'
3400 >>> hexenc(bytes(s))
3401 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3403 >>> PrintableString("привет мир")
3404 Traceback (most recent call last):
3405 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3407 >>> BMPString("ада", bounds=(2, 2))
3408 Traceback (most recent call last):
3409 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3410 >>> s = BMPString("ад", bounds=(2, 2))
3413 >>> hexenc(bytes(s))
3421 * - :py:class:`pyderasn.UTF8String`
3423 * - :py:class:`pyderasn.NumericString`
3425 * - :py:class:`pyderasn.PrintableString`
3427 * - :py:class:`pyderasn.TeletexString`
3429 * - :py:class:`pyderasn.T61String`
3431 * - :py:class:`pyderasn.VideotexString`
3433 * - :py:class:`pyderasn.IA5String`
3435 * - :py:class:`pyderasn.GraphicString`
3437 * - :py:class:`pyderasn.VisibleString`
3439 * - :py:class:`pyderasn.ISO646String`
3441 * - :py:class:`pyderasn.GeneralString`
3443 * - :py:class:`pyderasn.UniversalString`
3445 * - :py:class:`pyderasn.BMPString`
3448 __slots__ = ("encoding",)
3450 def _value_sanitize(self, value):
3452 value_decoded = None
3453 if isinstance(value, self.__class__):
3454 value_raw = value._value
3455 elif isinstance(value, text_type):
3456 value_decoded = value
3457 elif isinstance(value, binary_type):
3460 raise InvalidValueType((self.__class__, text_type, binary_type))
3463 value_decoded.encode(self.encoding)
3464 if value_raw is None else value_raw
3467 value_raw.decode(self.encoding)
3468 if value_decoded is None else value_decoded
3470 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3471 raise DecodeError(str(err))
3472 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3480 def __eq__(self, their):
3481 if isinstance(their, binary_type):
3482 return self._value == their
3483 if isinstance(their, text_type):
3484 return self._value == their.encode(self.encoding)
3485 if not isinstance(their, self.__class__):
3488 self._value == their._value and
3489 self.tag == their.tag and
3490 self._expl == their._expl
3493 def __unicode__(self):
3495 return self._value.decode(self.encoding)
3496 return text_type(self._value)
3499 return pp_console_row(next(self.pps(no_unicode=PY2)))
3501 def pps(self, decode_path=(), no_unicode=False):
3504 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3507 asn1_type_name=self.asn1_type_name,
3508 obj_name=self.__class__.__name__,
3509 decode_path=decode_path,
3511 optional=self.optional,
3512 default=self == self.default,
3513 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3514 expl=None if self._expl is None else tag_decode(self._expl),
3519 expl_offset=self.expl_offset if self.expled else None,
3520 expl_tlen=self.expl_tlen if self.expled else None,
3521 expl_llen=self.expl_llen if self.expled else None,
3522 expl_vlen=self.expl_vlen if self.expled else None,
3523 expl_lenindef=self.expl_lenindef,
3524 ber_encoded=self.ber_encoded,
3527 for pp in self.pps_lenindef(decode_path):
3531 class UTF8String(CommonString):
3533 tag_default = tag_encode(12)
3535 asn1_type_name = "UTF8String"
3538 class AllowableCharsMixin(object):
3540 def allowable_chars(self):
3542 return self._allowable_chars
3543 return set(six_unichr(c) for c in self._allowable_chars)
3546 class NumericString(AllowableCharsMixin, CommonString):
3549 Its value is properly sanitized: only ASCII digits with spaces can
3552 >>> NumericString().allowable_chars
3553 set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3556 tag_default = tag_encode(18)
3558 asn1_type_name = "NumericString"
3559 _allowable_chars = set(digits.encode("ascii") + b" ")
3561 def _value_sanitize(self, value):
3562 value = super(NumericString, self)._value_sanitize(value)
3563 if not set(value) <= self._allowable_chars:
3564 raise DecodeError("non-numeric value")
3568 class PrintableString(AllowableCharsMixin, CommonString):
3571 Its value is properly sanitized: see X.680 41.4 table 10.
3573 >>> PrintableString().allowable_chars
3574 >>> set([' ', "'", ..., 'z'])
3577 tag_default = tag_encode(19)
3579 asn1_type_name = "PrintableString"
3580 _allowable_chars = set(
3581 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3584 def _value_sanitize(self, value):
3585 value = super(PrintableString, self)._value_sanitize(value)
3586 if not set(value) <= self._allowable_chars:
3587 raise DecodeError("non-printable value")
3591 class TeletexString(CommonString):
3593 tag_default = tag_encode(20)
3595 asn1_type_name = "TeletexString"
3598 class T61String(TeletexString):
3600 asn1_type_name = "T61String"
3603 class VideotexString(CommonString):
3605 tag_default = tag_encode(21)
3606 encoding = "iso-8859-1"
3607 asn1_type_name = "VideotexString"
3610 class IA5String(CommonString):
3612 tag_default = tag_encode(22)
3614 asn1_type_name = "IA5"
3617 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3618 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3619 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3622 class UTCTime(CommonString):
3623 """``UTCTime`` datetime type
3625 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3626 UTCTime UTCTime 2017-09-30T22:07:50
3632 datetime.datetime(2017, 9, 30, 22, 7, 50)
3633 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3634 datetime.datetime(1957, 9, 30, 22, 7, 50)
3637 tag_default = tag_encode(23)
3639 asn1_type_name = "UTCTime"
3641 fmt = "%y%m%d%H%M%SZ"
3651 bounds=None, # dummy argument, workability for OctetString.decode
3654 :param value: set the value. Either datetime type, or
3655 :py:class:`pyderasn.UTCTime` object
3656 :param bytes impl: override default tag with ``IMPLICIT`` one
3657 :param bytes expl: override default tag with ``EXPLICIT`` one
3658 :param default: set default value. Type same as in ``value``
3659 :param bool optional: is object ``OPTIONAL`` in sequence
3661 super(UTCTime, self).__init__(
3669 if value is not None:
3670 self._value = self._value_sanitize(value)
3671 if default is not None:
3672 default = self._value_sanitize(default)
3673 self.default = self.__class__(
3678 if self._value is None:
3679 self._value = default
3681 def _value_sanitize(self, value):
3682 if isinstance(value, self.__class__):
3684 if isinstance(value, datetime):
3685 return value.strftime(self.fmt).encode("ascii")
3686 if isinstance(value, binary_type):
3688 value_decoded = value.decode("ascii")
3689 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3690 raise DecodeError("invalid UTCTime encoding")
3691 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3693 datetime.strptime(value_decoded, self.fmt)
3694 except (TypeError, ValueError):
3695 raise DecodeError("invalid UTCTime format")
3698 raise DecodeError("invalid UTCTime length")
3699 raise InvalidValueType((self.__class__, datetime))
3701 def __eq__(self, their):
3702 if isinstance(their, binary_type):
3703 return self._value == their
3704 if isinstance(their, datetime):
3705 return self.todatetime() == their
3706 if not isinstance(their, self.__class__):
3709 self._value == their._value and
3710 self.tag == their.tag and
3711 self._expl == their._expl
3714 def todatetime(self):
3715 """Convert to datetime
3719 Pay attention that UTCTime can not hold full year, so all years
3720 having < 50 years are treated as 20xx, 19xx otherwise, according
3721 to X.509 recomendation.
3723 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3724 year = value.year % 100
3726 year=(2000 + year) if year < 50 else (1900 + year),
3730 minute=value.minute,
3731 second=value.second,
3735 return pp_console_row(next(self.pps()))
3737 def pps(self, decode_path=()):
3740 asn1_type_name=self.asn1_type_name,
3741 obj_name=self.__class__.__name__,
3742 decode_path=decode_path,
3743 value=self.todatetime().isoformat() if self.ready else None,
3744 optional=self.optional,
3745 default=self == self.default,
3746 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3747 expl=None if self._expl is None else tag_decode(self._expl),
3752 expl_offset=self.expl_offset if self.expled else None,
3753 expl_tlen=self.expl_tlen if self.expled else None,
3754 expl_llen=self.expl_llen if self.expled else None,
3755 expl_vlen=self.expl_vlen if self.expled else None,
3756 expl_lenindef=self.expl_lenindef,
3757 ber_encoded=self.ber_encoded,
3760 for pp in self.pps_lenindef(decode_path):
3764 class GeneralizedTime(UTCTime):
3765 """``GeneralizedTime`` datetime type
3767 This type is similar to :py:class:`pyderasn.UTCTime`.
3769 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3770 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3772 '20170930220750.000123Z'
3773 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3774 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3777 tag_default = tag_encode(24)
3778 asn1_type_name = "GeneralizedTime"
3780 fmt = "%Y%m%d%H%M%SZ"
3781 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3783 def _value_sanitize(self, value):
3784 if isinstance(value, self.__class__):
3786 if isinstance(value, datetime):
3787 return value.strftime(
3788 self.fmt_ms if value.microsecond > 0 else self.fmt
3790 if isinstance(value, binary_type):
3792 value_decoded = value.decode("ascii")
3793 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3794 raise DecodeError("invalid GeneralizedTime encoding")
3795 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3797 datetime.strptime(value_decoded, self.fmt)
3798 except (TypeError, ValueError):
3800 "invalid GeneralizedTime (without ms) format",
3803 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3805 datetime.strptime(value_decoded, self.fmt_ms)
3806 except (TypeError, ValueError):
3808 "invalid GeneralizedTime (with ms) format",
3813 "invalid GeneralizedTime length",
3814 klass=self.__class__,
3816 raise InvalidValueType((self.__class__, datetime))
3818 def todatetime(self):
3819 value = self._value.decode("ascii")
3820 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3821 return datetime.strptime(value, self.fmt)
3822 return datetime.strptime(value, self.fmt_ms)
3825 class GraphicString(CommonString):
3827 tag_default = tag_encode(25)
3828 encoding = "iso-8859-1"
3829 asn1_type_name = "GraphicString"
3832 class VisibleString(CommonString):
3834 tag_default = tag_encode(26)
3836 asn1_type_name = "VisibleString"
3839 class ISO646String(VisibleString):
3841 asn1_type_name = "ISO646String"
3844 class GeneralString(CommonString):
3846 tag_default = tag_encode(27)
3847 encoding = "iso-8859-1"
3848 asn1_type_name = "GeneralString"
3851 class UniversalString(CommonString):
3853 tag_default = tag_encode(28)
3854 encoding = "utf-32-be"
3855 asn1_type_name = "UniversalString"
3858 class BMPString(CommonString):
3860 tag_default = tag_encode(30)
3861 encoding = "utf-16-be"
3862 asn1_type_name = "BMPString"
3866 """``CHOICE`` special type
3870 class GeneralName(Choice):
3872 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3873 ("dNSName", IA5String(impl=tag_ctxp(2))),
3876 >>> gn = GeneralName()
3878 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3879 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3880 >>> gn["dNSName"] = IA5String("bar.baz")
3881 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3882 >>> gn["rfc822Name"]
3885 [2] IA5String IA5 bar.baz
3888 >>> gn.value == gn["dNSName"]
3891 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3893 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3894 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3896 __slots__ = ("specs",)
3898 asn1_type_name = "CHOICE"
3911 :param value: set the value. Either ``(choice, value)`` tuple, or
3912 :py:class:`pyderasn.Choice` object
3913 :param bytes impl: can not be set, do **not** use it
3914 :param bytes expl: override default tag with ``EXPLICIT`` one
3915 :param default: set default value. Type same as in ``value``
3916 :param bool optional: is object ``OPTIONAL`` in sequence
3918 if impl is not None:
3919 raise ValueError("no implicit tag allowed for CHOICE")
3920 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3922 schema = getattr(self, "schema", ())
3923 if len(schema) == 0:
3924 raise ValueError("schema must be specified")
3926 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3929 if value is not None:
3930 self._value = self._value_sanitize(value)
3931 if default is not None:
3932 default_value = self._value_sanitize(default)
3933 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3934 default_obj.specs = self.specs
3935 default_obj._value = default_value
3936 self.default = default_obj
3938 self._value = default_obj.copy()._value
3940 def _value_sanitize(self, value):
3941 if isinstance(value, self.__class__):
3943 if isinstance(value, tuple) and len(value) == 2:
3945 spec = self.specs.get(choice)
3947 raise ObjUnknown(choice)
3948 if not isinstance(obj, spec.__class__):
3949 raise InvalidValueType((spec,))
3950 return (choice, spec(obj))
3951 raise InvalidValueType((self.__class__, tuple))
3955 return self._value is not None and self._value[1].ready
3959 return self.expl_lenindef or (
3960 (self._value is not None) and
3961 self._value[1].bered
3965 obj = self.__class__(schema=self.specs)
3966 obj._expl = self._expl
3967 obj.default = self.default
3968 obj.optional = self.optional
3969 obj.offset = self.offset
3970 obj.llen = self.llen
3971 obj.vlen = self.vlen
3973 if value is not None:
3974 obj._value = (value[0], value[1].copy())
3977 def __eq__(self, their):
3978 if isinstance(their, tuple) and len(their) == 2:
3979 return self._value == their
3980 if not isinstance(their, self.__class__):
3983 self.specs == their.specs and
3984 self._value == their._value
3994 return self.__class__(
3997 expl=self._expl if expl is None else expl,
3998 default=self.default if default is None else default,
3999 optional=self.optional if optional is None else optional,
4004 self._assert_ready()
4005 return self._value[0]
4009 self._assert_ready()
4010 return self._value[1]
4012 def __getitem__(self, key):
4013 if key not in self.specs:
4014 raise ObjUnknown(key)
4015 if self._value is None:
4017 choice, value = self._value
4022 def __setitem__(self, key, value):
4023 spec = self.specs.get(key)
4025 raise ObjUnknown(key)
4026 if not isinstance(value, spec.__class__):
4027 raise InvalidValueType((spec.__class__,))
4028 self._value = (key, spec(value))
4036 return self._value[1].decoded if self.ready else False
4039 self._assert_ready()
4040 return self._value[1].encode()
4042 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4043 for choice, spec in self.specs.items():
4044 sub_decode_path = decode_path + (choice,)
4050 decode_path=sub_decode_path,
4053 _ctx_immutable=False,
4060 klass=self.__class__,
4061 decode_path=decode_path,
4064 if tag_only: # pragma: no cover
4066 value, tail = spec.decode(
4070 decode_path=sub_decode_path,
4072 _ctx_immutable=False,
4074 obj = self.__class__(
4077 default=self.default,
4078 optional=self.optional,
4079 _decoded=(offset, 0, value.fulllen),
4081 obj._value = (choice, value)
4085 value = pp_console_row(next(self.pps()))
4087 value = "%s[%r]" % (value, self.value)
4090 def pps(self, decode_path=()):
4093 asn1_type_name=self.asn1_type_name,
4094 obj_name=self.__class__.__name__,
4095 decode_path=decode_path,
4096 value=self.choice if self.ready else None,
4097 optional=self.optional,
4098 default=self == self.default,
4099 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4100 expl=None if self._expl is None else tag_decode(self._expl),
4105 expl_lenindef=self.expl_lenindef,
4109 yield self.value.pps(decode_path=decode_path + (self.choice,))
4110 for pp in self.pps_lenindef(decode_path):
4114 class PrimitiveTypes(Choice):
4115 """Predefined ``CHOICE`` for all generic primitive types
4117 It could be useful for general decoding of some unspecified values:
4119 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4120 OCTET STRING 3 bytes 666f6f
4121 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4125 schema = tuple((klass.__name__, klass()) for klass in (
4150 """``ANY`` special type
4152 >>> Any(Integer(-123))
4154 >>> a = Any(OctetString(b"hello world").encode())
4155 ANY 040b68656c6c6f20776f726c64
4156 >>> hexenc(bytes(a))
4157 b'0x040x0bhello world'
4159 __slots__ = ("defined",)
4160 tag_default = tag_encode(0)
4161 asn1_type_name = "ANY"
4171 :param value: set the value. Either any kind of pyderasn's
4172 **ready** object, or bytes. Pay attention that
4173 **no** validation is performed is raw binary value
4175 :param bytes expl: override default tag with ``EXPLICIT`` one
4176 :param bool optional: is object ``OPTIONAL`` in sequence
4178 super(Any, self).__init__(None, expl, None, optional, _decoded)
4179 self._value = None if value is None else self._value_sanitize(value)
4182 def _value_sanitize(self, value):
4183 if isinstance(value, self.__class__):
4185 if isinstance(value, Obj):
4186 return value.encode()
4187 if isinstance(value, binary_type):
4189 raise InvalidValueType((self.__class__, Obj, binary_type))
4193 return self._value is not None
4197 if self.expl_lenindef or self.lenindef:
4199 if self.defined is None:
4201 return self.defined[1].bered
4204 obj = self.__class__()
4205 obj._value = self._value
4207 obj._expl = self._expl
4208 obj.optional = self.optional
4209 obj.offset = self.offset
4210 obj.llen = self.llen
4211 obj.vlen = self.vlen
4214 def __eq__(self, their):
4215 if isinstance(their, binary_type):
4216 return self._value == their
4217 if issubclass(their.__class__, Any):
4218 return self._value == their._value
4227 return self.__class__(
4229 expl=self._expl if expl is None else expl,
4230 optional=self.optional if optional is None else optional,
4233 def __bytes__(self):
4234 self._assert_ready()
4242 self._assert_ready()
4245 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4247 t, tlen, lv = tag_strip(tlv)
4248 except DecodeError as err:
4249 raise err.__class__(
4251 klass=self.__class__,
4252 decode_path=decode_path,
4256 l, llen, v = len_decode(lv)
4257 except LenIndefForm as err:
4258 if not ctx.get("bered", False):
4259 raise err.__class__(
4261 klass=self.__class__,
4262 decode_path=decode_path,
4265 llen, vlen, v = 1, 0, lv[1:]
4266 sub_offset = offset + tlen + llen
4268 while v[:EOC_LEN].tobytes() != EOC:
4269 chunk, v = Any().decode(
4272 decode_path=decode_path + (str(chunk_i),),
4275 _ctx_immutable=False,
4277 vlen += chunk.tlvlen
4278 sub_offset += chunk.tlvlen
4280 tlvlen = tlen + llen + vlen + EOC_LEN
4281 obj = self.__class__(
4282 value=tlv[:tlvlen].tobytes(),
4284 optional=self.optional,
4285 _decoded=(offset, 0, tlvlen),
4289 return obj, v[EOC_LEN:]
4290 except DecodeError as err:
4291 raise err.__class__(
4293 klass=self.__class__,
4294 decode_path=decode_path,
4298 raise NotEnoughData(
4299 "encoded length is longer than data",
4300 klass=self.__class__,
4301 decode_path=decode_path,
4304 tlvlen = tlen + llen + l
4305 v, tail = tlv[:tlvlen], v[l:]
4306 obj = self.__class__(
4309 optional=self.optional,
4310 _decoded=(offset, 0, tlvlen),
4316 return pp_console_row(next(self.pps()))
4318 def pps(self, decode_path=()):
4321 asn1_type_name=self.asn1_type_name,
4322 obj_name=self.__class__.__name__,
4323 decode_path=decode_path,
4324 blob=self._value if self.ready else None,
4325 optional=self.optional,
4326 default=self == self.default,
4327 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4328 expl=None if self._expl is None else tag_decode(self._expl),
4333 expl_offset=self.expl_offset if self.expled else None,
4334 expl_tlen=self.expl_tlen if self.expled else None,
4335 expl_llen=self.expl_llen if self.expled else None,
4336 expl_vlen=self.expl_vlen if self.expled else None,
4337 expl_lenindef=self.expl_lenindef,
4338 lenindef=self.lenindef,
4341 defined_by, defined = self.defined or (None, None)
4342 if defined_by is not None:
4344 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4346 for pp in self.pps_lenindef(decode_path):
4350 ########################################################################
4351 # ASN.1 constructed types
4352 ########################################################################
4354 def get_def_by_path(defines_by_path, sub_decode_path):
4355 """Get define by decode path
4357 for path, define in defines_by_path:
4358 if len(path) != len(sub_decode_path):
4360 for p1, p2 in zip(path, sub_decode_path):
4361 if (p1 != any) and (p1 != p2):
4367 def abs_decode_path(decode_path, rel_path):
4368 """Create an absolute decode path from current and relative ones
4370 :param decode_path: current decode path, starting point. Tuple of strings
4371 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4372 If first tuple's element is "/", then treat it as
4373 an absolute path, ignoring ``decode_path`` as
4374 starting point. Also this tuple can contain ".."
4375 elements, stripping the leading element from
4378 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4379 ("foo", "bar", "baz", "whatever")
4380 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4382 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4385 if rel_path[0] == "/":
4387 if rel_path[0] == "..":
4388 return abs_decode_path(decode_path[:-1], rel_path[1:])
4389 return decode_path + rel_path
4392 class Sequence(Obj):
4393 """``SEQUENCE`` structure type
4395 You have to make specification of sequence::
4397 class Extension(Sequence):
4399 ("extnID", ObjectIdentifier()),
4400 ("critical", Boolean(default=False)),
4401 ("extnValue", OctetString()),
4404 Then, you can work with it as with dictionary.
4406 >>> ext = Extension()
4407 >>> Extension().specs
4409 ('extnID', OBJECT IDENTIFIER),
4410 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4411 ('extnValue', OCTET STRING),
4413 >>> ext["extnID"] = "1.2.3"
4414 Traceback (most recent call last):
4415 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4416 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4418 You can determine if sequence is ready to be encoded:
4423 Traceback (most recent call last):
4424 pyderasn.ObjNotReady: object is not ready: extnValue
4425 >>> ext["extnValue"] = OctetString(b"foobar")
4429 Value you want to assign, must have the same **type** as in
4430 corresponding specification, but it can have different tags,
4431 optional/default attributes -- they will be taken from specification
4434 class TBSCertificate(Sequence):
4436 ("version", Version(expl=tag_ctxc(0), default="v1")),
4439 >>> tbs = TBSCertificate()
4440 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4442 Assign ``None`` to remove value from sequence.
4444 You can set values in Sequence during its initialization:
4446 >>> AlgorithmIdentifier((
4447 ("algorithm", ObjectIdentifier("1.2.3")),
4448 ("parameters", Any(Null()))
4450 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4452 You can determine if value exists/set in the sequence and take its value:
4454 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4457 OBJECT IDENTIFIER 1.2.3
4459 But pay attention that if value has default, then it won't be (not
4460 in) in the sequence (because ``DEFAULT`` must not be encoded in
4461 DER), but you can read its value:
4463 >>> "critical" in ext, ext["critical"]
4464 (False, BOOLEAN False)
4465 >>> ext["critical"] = Boolean(True)
4466 >>> "critical" in ext, ext["critical"]
4467 (True, BOOLEAN True)
4469 All defaulted values are always optional.
4471 .. _allow_default_values_ctx:
4473 DER prohibits default value encoding and will raise an error if
4474 default value is unexpectedly met during decode.
4475 If :ref:`bered <bered_ctx>` context option is set, then no error
4476 will be raised, but ``bered`` attribute set. You can disable strict
4477 defaulted values existence validation by setting
4478 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4480 Two sequences are equal if they have equal specification (schema),
4481 implicit/explicit tagging and the same values.
4483 __slots__ = ("specs",)
4484 tag_default = tag_encode(form=TagFormConstructed, num=16)
4485 asn1_type_name = "SEQUENCE"
4497 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4499 schema = getattr(self, "schema", ())
4501 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4504 if value is not None:
4505 if issubclass(value.__class__, Sequence):
4506 self._value = value._value
4507 elif hasattr(value, "__iter__"):
4508 for seq_key, seq_value in value:
4509 self[seq_key] = seq_value
4511 raise InvalidValueType((Sequence,))
4512 if default is not None:
4513 if not issubclass(default.__class__, Sequence):
4514 raise InvalidValueType((Sequence,))
4515 default_value = default._value
4516 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4517 default_obj.specs = self.specs
4518 default_obj._value = default_value
4519 self.default = default_obj
4521 self._value = default_obj.copy()._value
4525 for name, spec in self.specs.items():
4526 value = self._value.get(name)
4538 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4540 return any(value.bered for value in self._value.values())
4543 obj = self.__class__(schema=self.specs)
4545 obj._expl = self._expl
4546 obj.default = self.default
4547 obj.optional = self.optional
4548 obj.offset = self.offset
4549 obj.llen = self.llen
4550 obj.vlen = self.vlen
4551 obj._value = {k: v.copy() for k, v in self._value.items()}
4554 def __eq__(self, their):
4555 if not isinstance(their, self.__class__):
4558 self.specs == their.specs and
4559 self.tag == their.tag and
4560 self._expl == their._expl and
4561 self._value == their._value
4572 return self.__class__(
4575 impl=self.tag if impl is None else impl,
4576 expl=self._expl if expl is None else expl,
4577 default=self.default if default is None else default,
4578 optional=self.optional if optional is None else optional,
4581 def __contains__(self, key):
4582 return key in self._value
4584 def __setitem__(self, key, value):
4585 spec = self.specs.get(key)
4587 raise ObjUnknown(key)
4589 self._value.pop(key, None)
4591 if not isinstance(value, spec.__class__):
4592 raise InvalidValueType((spec.__class__,))
4593 value = spec(value=value)
4594 if spec.default is not None and value == spec.default:
4595 self._value.pop(key, None)
4597 self._value[key] = value
4599 def __getitem__(self, key):
4600 value = self._value.get(key)
4601 if value is not None:
4603 spec = self.specs.get(key)
4605 raise ObjUnknown(key)
4606 if spec.default is not None:
4610 def _encoded_values(self):
4612 for name, spec in self.specs.items():
4613 value = self._value.get(name)
4617 raise ObjNotReady(name)
4618 raws.append(value.encode())
4622 v = b"".join(self._encoded_values())
4623 return b"".join((self.tag, len_encode(len(v)), v))
4625 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4627 t, tlen, lv = tag_strip(tlv)
4628 except DecodeError as err:
4629 raise err.__class__(
4631 klass=self.__class__,
4632 decode_path=decode_path,
4637 klass=self.__class__,
4638 decode_path=decode_path,
4641 if tag_only: # pragma: no cover
4644 ctx_bered = ctx.get("bered", False)
4646 l, llen, v = len_decode(lv)
4647 except LenIndefForm as err:
4649 raise err.__class__(
4651 klass=self.__class__,
4652 decode_path=decode_path,
4655 l, llen, v = 0, 1, lv[1:]
4657 except DecodeError as err:
4658 raise err.__class__(
4660 klass=self.__class__,
4661 decode_path=decode_path,
4665 raise NotEnoughData(
4666 "encoded length is longer than data",
4667 klass=self.__class__,
4668 decode_path=decode_path,
4672 v, tail = v[:l], v[l:]
4674 sub_offset = offset + tlen + llen
4677 ctx_allow_default_values = ctx.get("allow_default_values", False)
4678 for name, spec in self.specs.items():
4679 if spec.optional and (
4680 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4684 sub_decode_path = decode_path + (name,)
4686 value, v_tail = spec.decode(
4690 decode_path=sub_decode_path,
4692 _ctx_immutable=False,
4699 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4700 if defined is not None:
4701 defined_by, defined_spec = defined
4702 if issubclass(value.__class__, SequenceOf):
4703 for i, _value in enumerate(value):
4704 sub_sub_decode_path = sub_decode_path + (
4706 DecodePathDefBy(defined_by),
4708 defined_value, defined_tail = defined_spec.decode(
4709 memoryview(bytes(_value)),
4711 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4712 if value.expled else (value.tlen + value.llen)
4715 decode_path=sub_sub_decode_path,
4717 _ctx_immutable=False,
4719 if len(defined_tail) > 0:
4722 klass=self.__class__,
4723 decode_path=sub_sub_decode_path,
4726 _value.defined = (defined_by, defined_value)
4728 defined_value, defined_tail = defined_spec.decode(
4729 memoryview(bytes(value)),
4731 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4732 if value.expled else (value.tlen + value.llen)
4735 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4737 _ctx_immutable=False,
4739 if len(defined_tail) > 0:
4742 klass=self.__class__,
4743 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4746 value.defined = (defined_by, defined_value)
4748 value_len = value.fulllen
4750 sub_offset += value_len
4752 if spec.default is not None and value == spec.default:
4753 if ctx_bered or ctx_allow_default_values:
4757 "DEFAULT value met",
4758 klass=self.__class__,
4759 decode_path=sub_decode_path,
4762 values[name] = value
4764 spec_defines = getattr(spec, "defines", ())
4765 if len(spec_defines) == 0:
4766 defines_by_path = ctx.get("defines_by_path", ())
4767 if len(defines_by_path) > 0:
4768 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4769 if spec_defines is not None and len(spec_defines) > 0:
4770 for rel_path, schema in spec_defines:
4771 defined = schema.get(value, None)
4772 if defined is not None:
4773 ctx.setdefault("_defines", []).append((
4774 abs_decode_path(sub_decode_path[:-1], rel_path),
4778 if v[:EOC_LEN].tobytes() != EOC:
4781 klass=self.__class__,
4782 decode_path=decode_path,
4790 klass=self.__class__,
4791 decode_path=decode_path,
4794 obj = self.__class__(
4798 default=self.default,
4799 optional=self.optional,
4800 _decoded=(offset, llen, vlen),
4803 obj.lenindef = lenindef
4804 obj.ber_encoded = ber_encoded
4808 value = pp_console_row(next(self.pps()))
4810 for name in self.specs:
4811 _value = self._value.get(name)
4814 cols.append("%s: %s" % (name, repr(_value)))
4815 return "%s[%s]" % (value, "; ".join(cols))
4817 def pps(self, decode_path=()):
4820 asn1_type_name=self.asn1_type_name,
4821 obj_name=self.__class__.__name__,
4822 decode_path=decode_path,
4823 optional=self.optional,
4824 default=self == self.default,
4825 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4826 expl=None if self._expl is None else tag_decode(self._expl),
4831 expl_offset=self.expl_offset if self.expled else None,
4832 expl_tlen=self.expl_tlen if self.expled else None,
4833 expl_llen=self.expl_llen if self.expled else None,
4834 expl_vlen=self.expl_vlen if self.expled else None,
4835 expl_lenindef=self.expl_lenindef,
4836 lenindef=self.lenindef,
4837 ber_encoded=self.ber_encoded,
4840 for name in self.specs:
4841 value = self._value.get(name)
4844 yield value.pps(decode_path=decode_path + (name,))
4845 for pp in self.pps_lenindef(decode_path):
4849 class Set(Sequence):
4850 """``SET`` structure type
4852 Its usage is identical to :py:class:`pyderasn.Sequence`.
4854 .. _allow_unordered_set_ctx:
4856 DER prohibits unordered values encoding and will raise an error
4857 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4858 then no error will occure. Also you can disable strict values
4859 ordering check by setting ``"allow_unordered_set": True``
4860 :ref:`context <ctx>` option.
4863 tag_default = tag_encode(form=TagFormConstructed, num=17)
4864 asn1_type_name = "SET"
4867 raws = self._encoded_values()
4870 return b"".join((self.tag, len_encode(len(v)), v))
4872 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4874 t, tlen, lv = tag_strip(tlv)
4875 except DecodeError as err:
4876 raise err.__class__(
4878 klass=self.__class__,
4879 decode_path=decode_path,
4884 klass=self.__class__,
4885 decode_path=decode_path,
4891 ctx_bered = ctx.get("bered", False)
4893 l, llen, v = len_decode(lv)
4894 except LenIndefForm as err:
4896 raise err.__class__(
4898 klass=self.__class__,
4899 decode_path=decode_path,
4902 l, llen, v = 0, 1, lv[1:]
4904 except DecodeError as err:
4905 raise err.__class__(
4907 klass=self.__class__,
4908 decode_path=decode_path,
4912 raise NotEnoughData(
4913 "encoded length is longer than data",
4914 klass=self.__class__,
4918 v, tail = v[:l], v[l:]
4920 sub_offset = offset + tlen + llen
4923 ctx_allow_default_values = ctx.get("allow_default_values", False)
4924 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4925 value_prev = memoryview(v[:0])
4926 specs_items = self.specs.items
4928 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4930 for name, spec in specs_items():
4931 sub_decode_path = decode_path + (name,)
4937 decode_path=sub_decode_path,
4940 _ctx_immutable=False,
4947 klass=self.__class__,
4948 decode_path=decode_path,
4951 value, v_tail = spec.decode(
4955 decode_path=sub_decode_path,
4957 _ctx_immutable=False,
4959 value_len = value.fulllen
4960 if value_prev.tobytes() > v[:value_len].tobytes():
4961 if ctx_bered or ctx_allow_unordered_set:
4965 "unordered " + self.asn1_type_name,
4966 klass=self.__class__,
4967 decode_path=sub_decode_path,
4970 if spec.default is None or value != spec.default:
4972 elif ctx_bered or ctx_allow_default_values:
4976 "DEFAULT value met",
4977 klass=self.__class__,
4978 decode_path=sub_decode_path,
4981 values[name] = value
4982 value_prev = v[:value_len]
4983 sub_offset += value_len
4986 obj = self.__class__(
4990 default=self.default,
4991 optional=self.optional,
4992 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4995 if v[:EOC_LEN].tobytes() != EOC:
4998 klass=self.__class__,
4999 decode_path=decode_path,
5007 "not all values are ready",
5008 klass=self.__class__,
5009 decode_path=decode_path,
5012 obj.ber_encoded = ber_encoded
5016 class SequenceOf(Obj):
5017 """``SEQUENCE OF`` sequence type
5019 For that kind of type you must specify the object it will carry on
5020 (bounds are for example here, not required)::
5022 class Ints(SequenceOf):
5027 >>> ints.append(Integer(123))
5028 >>> ints.append(Integer(234))
5030 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5031 >>> [int(i) for i in ints]
5033 >>> ints.append(Integer(345))
5034 Traceback (most recent call last):
5035 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5038 >>> ints[1] = Integer(345)
5040 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5042 Also you can initialize sequence with preinitialized values:
5044 >>> ints = Ints([Integer(123), Integer(234)])
5046 __slots__ = ("spec", "_bound_min", "_bound_max")
5047 tag_default = tag_encode(form=TagFormConstructed, num=16)
5048 asn1_type_name = "SEQUENCE OF"
5061 super(SequenceOf, self).__init__(
5069 schema = getattr(self, "schema", None)
5071 raise ValueError("schema must be specified")
5073 self._bound_min, self._bound_max = getattr(
5077 ) if bounds is None else bounds
5079 if value is not None:
5080 self._value = self._value_sanitize(value)
5081 if default is not None:
5082 default_value = self._value_sanitize(default)
5083 default_obj = self.__class__(
5088 default_obj._value = default_value
5089 self.default = default_obj
5091 self._value = default_obj.copy()._value
5093 def _value_sanitize(self, value):
5094 if issubclass(value.__class__, SequenceOf):
5095 value = value._value
5096 elif hasattr(value, "__iter__"):
5099 raise InvalidValueType((self.__class__, iter))
5100 if not self._bound_min <= len(value) <= self._bound_max:
5101 raise BoundsError(self._bound_min, len(value), self._bound_max)
5103 if not isinstance(v, self.spec.__class__):
5104 raise InvalidValueType((self.spec.__class__,))
5109 return all(v.ready for v in self._value)
5113 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5115 return any(v.bered for v in self._value)
5118 obj = self.__class__(schema=self.spec)
5119 obj._bound_min = self._bound_min
5120 obj._bound_max = self._bound_max
5122 obj._expl = self._expl
5123 obj.default = self.default
5124 obj.optional = self.optional
5125 obj.offset = self.offset
5126 obj.llen = self.llen
5127 obj.vlen = self.vlen
5128 obj._value = [v.copy() for v in self._value]
5131 def __eq__(self, their):
5132 if isinstance(their, self.__class__):
5134 self.spec == their.spec and
5135 self.tag == their.tag and
5136 self._expl == their._expl and
5137 self._value == their._value
5139 if hasattr(their, "__iter__"):
5140 return self._value == list(their)
5152 return self.__class__(
5156 (self._bound_min, self._bound_max)
5157 if bounds is None else bounds
5159 impl=self.tag if impl is None else impl,
5160 expl=self._expl if expl is None else expl,
5161 default=self.default if default is None else default,
5162 optional=self.optional if optional is None else optional,
5165 def __contains__(self, key):
5166 return key in self._value
5168 def append(self, value):
5169 if not isinstance(value, self.spec.__class__):
5170 raise InvalidValueType((self.spec.__class__,))
5171 if len(self._value) + 1 > self._bound_max:
5174 len(self._value) + 1,
5177 self._value.append(value)
5180 self._assert_ready()
5181 return iter(self._value)
5184 self._assert_ready()
5185 return len(self._value)
5187 def __setitem__(self, key, value):
5188 if not isinstance(value, self.spec.__class__):
5189 raise InvalidValueType((self.spec.__class__,))
5190 self._value[key] = self.spec(value=value)
5192 def __getitem__(self, key):
5193 return self._value[key]
5195 def _encoded_values(self):
5196 return [v.encode() for v in self._value]
5199 v = b"".join(self._encoded_values())
5200 return b"".join((self.tag, len_encode(len(v)), v))
5202 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5204 t, tlen, lv = tag_strip(tlv)
5205 except DecodeError as err:
5206 raise err.__class__(
5208 klass=self.__class__,
5209 decode_path=decode_path,
5214 klass=self.__class__,
5215 decode_path=decode_path,
5221 ctx_bered = ctx.get("bered", False)
5223 l, llen, v = len_decode(lv)
5224 except LenIndefForm as err:
5226 raise err.__class__(
5228 klass=self.__class__,
5229 decode_path=decode_path,
5232 l, llen, v = 0, 1, lv[1:]
5234 except DecodeError as err:
5235 raise err.__class__(
5237 klass=self.__class__,
5238 decode_path=decode_path,
5242 raise NotEnoughData(
5243 "encoded length is longer than data",
5244 klass=self.__class__,
5245 decode_path=decode_path,
5249 v, tail = v[:l], v[l:]
5251 sub_offset = offset + tlen + llen
5253 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5254 value_prev = memoryview(v[:0])
5258 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5260 sub_decode_path = decode_path + (str(len(_value)),)
5261 value, v_tail = spec.decode(
5265 decode_path=sub_decode_path,
5267 _ctx_immutable=False,
5269 value_len = value.fulllen
5271 if value_prev.tobytes() > v[:value_len].tobytes():
5272 if ctx_bered or ctx_allow_unordered_set:
5276 "unordered " + self.asn1_type_name,
5277 klass=self.__class__,
5278 decode_path=sub_decode_path,
5281 value_prev = v[:value_len]
5282 _value.append(value)
5283 sub_offset += value_len
5287 obj = self.__class__(
5290 bounds=(self._bound_min, self._bound_max),
5293 default=self.default,
5294 optional=self.optional,
5295 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5297 except BoundsError as err:
5300 klass=self.__class__,
5301 decode_path=decode_path,
5305 if v[:EOC_LEN].tobytes() != EOC:
5308 klass=self.__class__,
5309 decode_path=decode_path,
5314 obj.ber_encoded = ber_encoded
5319 pp_console_row(next(self.pps())),
5320 ", ".join(repr(v) for v in self._value),
5323 def pps(self, decode_path=()):
5326 asn1_type_name=self.asn1_type_name,
5327 obj_name=self.__class__.__name__,
5328 decode_path=decode_path,
5329 optional=self.optional,
5330 default=self == self.default,
5331 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5332 expl=None if self._expl is None else tag_decode(self._expl),
5337 expl_offset=self.expl_offset if self.expled else None,
5338 expl_tlen=self.expl_tlen if self.expled else None,
5339 expl_llen=self.expl_llen if self.expled else None,
5340 expl_vlen=self.expl_vlen if self.expled else None,
5341 expl_lenindef=self.expl_lenindef,
5342 lenindef=self.lenindef,
5343 ber_encoded=self.ber_encoded,
5346 for i, value in enumerate(self._value):
5347 yield value.pps(decode_path=decode_path + (str(i),))
5348 for pp in self.pps_lenindef(decode_path):
5352 class SetOf(SequenceOf):
5353 """``SET OF`` sequence type
5355 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5358 tag_default = tag_encode(form=TagFormConstructed, num=17)
5359 asn1_type_name = "SET OF"
5362 raws = self._encoded_values()
5365 return b"".join((self.tag, len_encode(len(v)), v))
5367 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5368 return super(SetOf, self)._decode(
5374 ordering_check=True,
5378 def obj_by_path(pypath): # pragma: no cover
5379 """Import object specified as string Python path
5381 Modules must be separated from classes/functions with ``:``.
5383 >>> obj_by_path("foo.bar:Baz")
5384 <class 'foo.bar.Baz'>
5385 >>> obj_by_path("foo.bar:Baz.boo")
5386 <classmethod 'foo.bar.Baz.boo'>
5388 mod, objs = pypath.rsplit(":", 1)
5389 from importlib import import_module
5390 obj = import_module(mod)
5391 for obj_name in objs.split("."):
5392 obj = getattr(obj, obj_name)
5396 def generic_decoder(): # pragma: no cover
5397 # All of this below is a big hack with self references
5398 choice = PrimitiveTypes()
5399 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5400 choice.specs["SetOf"] = SetOf(schema=choice)
5402 choice.specs["SequenceOf%d" % i] = SequenceOf(
5406 choice.specs["Any"] = Any()
5408 # Class name equals to type name, to omit it from output
5409 class SEQUENCEOF(SequenceOf):
5417 with_decode_path=False,
5418 decode_path_only=(),
5420 def _pprint_pps(pps):
5422 if hasattr(pp, "_fields"):
5424 decode_path_only != () and
5425 pp.decode_path[:len(decode_path_only)] != decode_path_only
5428 if pp.asn1_type_name == Choice.asn1_type_name:
5430 pp_kwargs = pp._asdict()
5431 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5432 pp = _pp(**pp_kwargs)
5433 yield pp_console_row(
5438 with_colours=with_colours,
5439 with_decode_path=with_decode_path,
5440 decode_path_len_decrease=len(decode_path_only),
5442 for row in pp_console_blob(
5444 decode_path_len_decrease=len(decode_path_only),
5448 for row in _pprint_pps(pp):
5450 return "\n".join(_pprint_pps(obj.pps()))
5451 return SEQUENCEOF(), pprint_any
5454 def main(): # pragma: no cover
5456 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5457 parser.add_argument(
5461 help="Skip that number of bytes from the beginning",
5463 parser.add_argument(
5465 help="Python path to dictionary with OIDs",
5467 parser.add_argument(
5469 help="Python path to schema definition to use",
5471 parser.add_argument(
5472 "--defines-by-path",
5473 help="Python path to decoder's defines_by_path",
5475 parser.add_argument(
5477 action="store_true",
5478 help="Disallow BER encoding",
5480 parser.add_argument(
5481 "--print-decode-path",
5482 action="store_true",
5483 help="Print decode paths",
5485 parser.add_argument(
5486 "--decode-path-only",
5487 help="Print only specified decode path",
5489 parser.add_argument(
5491 action="store_true",
5492 help="Allow explicit tag out-of-bound",
5494 parser.add_argument(
5496 type=argparse.FileType("rb"),
5497 help="Path to DER file you want to decode",
5499 args = parser.parse_args()
5500 args.DERFile.seek(args.skip)
5501 der = memoryview(args.DERFile.read())
5502 args.DERFile.close()
5503 oids = obj_by_path(args.oids) if args.oids else {}
5505 schema = obj_by_path(args.schema)
5506 from functools import partial
5507 pprinter = partial(pprint, big_blobs=True)
5509 schema, pprinter = generic_decoder()
5511 "bered": not args.nobered,
5512 "allow_expl_oob": args.allow_expl_oob,
5514 if args.defines_by_path is not None:
5515 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5516 obj, tail = schema().decode(der, ctx=ctx)
5520 with_colours=True if environ.get("NO_COLOR") is None else False,
5521 with_decode_path=args.print_decode_path,
5523 () if args.decode_path_only is None else
5524 tuple(args.decode_path_only.split(":"))
5528 print("\nTrailing data: %s" % hexenc(tail))
5531 if __name__ == "__main__":