3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2018 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``, ``SEQUENCE``, ``SET``, ``SET OF`` can contain it.
388 * If object has an indefinite length encoding, then its ``lenindef``
389 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
390 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
392 * If object has an indefinite length encoded explicit tag, then
393 ``expl_lenindef`` is set to True.
394 * If object has either any of BER-related encoding (explicit tag
395 indefinite length, object's indefinite length, BER-encoding) or any
396 underlying component has that kind of encoding, then ``bered``
397 attribute is set to True. For example SignedData CMS can have
398 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
399 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
401 EOC (end-of-contents) token's length is taken in advance in object's
404 .. _allow_expl_oob_ctx:
406 Allow explicit tag out-of-bound
407 -------------------------------
409 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
410 one value, more than one object. If you set ``allow_expl_oob`` context
411 option to True, then no error will be raised and that invalid encoding
412 will be silently further processed. But pay attention that offsets and
413 lengths will be invalid in that case.
417 This option should be used only for skipping some decode errors, just
418 to see the decoded structure somehow.
425 .. autoclass:: pyderasn.Boolean
430 .. autoclass:: pyderasn.Integer
435 .. autoclass:: pyderasn.BitString
440 .. autoclass:: pyderasn.OctetString
445 .. autoclass:: pyderasn.Null
450 .. autoclass:: pyderasn.ObjectIdentifier
455 .. autoclass:: pyderasn.Enumerated
459 .. autoclass:: pyderasn.CommonString
463 .. autoclass:: pyderasn.NumericString
467 .. autoclass:: pyderasn.UTCTime
468 :members: __init__, todatetime
472 .. autoclass:: pyderasn.GeneralizedTime
479 .. autoclass:: pyderasn.Choice
484 .. autoclass:: PrimitiveTypes
488 .. autoclass:: pyderasn.Any
496 .. autoclass:: pyderasn.Sequence
501 .. autoclass:: pyderasn.Set
506 .. autoclass:: pyderasn.SequenceOf
511 .. autoclass:: pyderasn.SetOf
517 .. autofunction:: pyderasn.abs_decode_path
518 .. autofunction:: pyderasn.hexenc
519 .. autofunction:: pyderasn.hexdec
520 .. autofunction:: pyderasn.tag_encode
521 .. autofunction:: pyderasn.tag_decode
522 .. autofunction:: pyderasn.tag_ctxp
523 .. autofunction:: pyderasn.tag_ctxc
524 .. autoclass:: pyderasn.Obj
525 .. autoclass:: pyderasn.DecodeError
527 .. autoclass:: pyderasn.NotEnoughData
528 .. autoclass:: pyderasn.LenIndefForm
529 .. autoclass:: pyderasn.TagMismatch
530 .. autoclass:: pyderasn.InvalidLength
531 .. autoclass:: pyderasn.InvalidOID
532 .. autoclass:: pyderasn.ObjUnknown
533 .. autoclass:: pyderasn.ObjNotReady
534 .. autoclass:: pyderasn.InvalidValueType
535 .. autoclass:: pyderasn.BoundsError
538 from codecs import getdecoder
539 from codecs import getencoder
540 from collections import namedtuple
541 from collections import OrderedDict
542 from datetime import datetime
543 from math import ceil
544 from os import environ
545 from string import digits
547 from six import add_metaclass
548 from six import binary_type
549 from six import byte2int
550 from six import indexbytes
551 from six import int2byte
552 from six import integer_types
553 from six import iterbytes
555 from six import string_types
556 from six import text_type
557 from six.moves import xrange as six_xrange
561 from termcolor import colored
563 def colored(what, *args):
607 "TagClassApplication",
611 "TagFormConstructed",
622 TagClassUniversal = 0
623 TagClassApplication = 1 << 6
624 TagClassContext = 1 << 7
625 TagClassPrivate = 1 << 6 | 1 << 7
627 TagFormConstructed = 1 << 5
630 TagClassApplication: "APPLICATION ",
631 TagClassPrivate: "PRIVATE ",
632 TagClassUniversal: "UNIV ",
636 LENINDEF = b"\x80" # length indefinite mark
637 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
640 ########################################################################
642 ########################################################################
644 class DecodeError(Exception):
645 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
647 :param str msg: reason of decode failing
648 :param klass: optional exact DecodeError inherited class (like
649 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
650 :py:exc:`InvalidLength`)
651 :param decode_path: tuple of strings. It contains human
652 readable names of the fields through which
653 decoding process has passed
654 :param int offset: binary offset where failure happened
656 super(DecodeError, self).__init__()
659 self.decode_path = decode_path
665 "" if self.klass is None else self.klass.__name__,
667 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
668 if len(self.decode_path) > 0 else ""
670 ("(at %d)" % self.offset) if self.offset > 0 else "",
676 return "%s(%s)" % (self.__class__.__name__, self)
679 class NotEnoughData(DecodeError):
683 class LenIndefForm(DecodeError):
687 class TagMismatch(DecodeError):
691 class InvalidLength(DecodeError):
695 class InvalidOID(DecodeError):
699 class ObjUnknown(ValueError):
700 def __init__(self, name):
701 super(ObjUnknown, self).__init__()
705 return "object is unknown: %s" % self.name
708 return "%s(%s)" % (self.__class__.__name__, self)
711 class ObjNotReady(ValueError):
712 def __init__(self, name):
713 super(ObjNotReady, self).__init__()
717 return "object is not ready: %s" % self.name
720 return "%s(%s)" % (self.__class__.__name__, self)
723 class InvalidValueType(ValueError):
724 def __init__(self, expected_types):
725 super(InvalidValueType, self).__init__()
726 self.expected_types = expected_types
729 return "invalid value type, expected: %s" % ", ".join(
730 [repr(t) for t in self.expected_types]
734 return "%s(%s)" % (self.__class__.__name__, self)
737 class BoundsError(ValueError):
738 def __init__(self, bound_min, value, bound_max):
739 super(BoundsError, self).__init__()
740 self.bound_min = bound_min
742 self.bound_max = bound_max
745 return "unsatisfied bounds: %s <= %s <= %s" % (
752 return "%s(%s)" % (self.__class__.__name__, self)
755 ########################################################################
757 ########################################################################
759 _hexdecoder = getdecoder("hex")
760 _hexencoder = getencoder("hex")
764 """Binary data to hexadecimal string convert
766 return _hexdecoder(data)[0]
770 """Hexadecimal string to binary data convert
772 return _hexencoder(data)[0].decode("ascii")
775 def int_bytes_len(num, byte_len=8):
778 return int(ceil(float(num.bit_length()) / byte_len))
781 def zero_ended_encode(num):
782 octets = bytearray(int_bytes_len(num, 7))
784 octets[i] = num & 0x7F
788 octets[i] = 0x80 | (num & 0x7F)
794 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
795 """Encode tag to binary form
797 :param int num: tag's number
798 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
799 :py:data:`pyderasn.TagClassContext`,
800 :py:data:`pyderasn.TagClassApplication`,
801 :py:data:`pyderasn.TagClassPrivate`)
802 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
803 :py:data:`pyderasn.TagFormConstructed`)
807 return int2byte(klass | form | num)
808 # [XX|X|11111][1.......][1.......] ... [0.......]
809 return int2byte(klass | form | 31) + zero_ended_encode(num)
813 """Decode tag from binary form
817 No validation is performed, assuming that it has already passed.
819 It returns tuple with three integers, as
820 :py:func:`pyderasn.tag_encode` accepts.
822 first_octet = byte2int(tag)
823 klass = first_octet & 0xC0
824 form = first_octet & 0x20
825 if first_octet & 0x1F < 0x1F:
826 return (klass, form, first_octet & 0x1F)
828 for octet in iterbytes(tag[1:]):
831 return (klass, form, num)
835 """Create CONTEXT PRIMITIVE tag
837 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
841 """Create CONTEXT CONSTRUCTED tag
843 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
847 """Take off tag from the data
849 :returns: (encoded tag, tag length, remaining data)
852 raise NotEnoughData("no data at all")
853 if byte2int(data) & 0x1F < 31:
854 return data[:1], 1, data[1:]
859 raise DecodeError("unfinished tag")
860 if indexbytes(data, i) & 0x80 == 0:
863 return data[:i], i, data[i:]
869 octets = bytearray(int_bytes_len(l) + 1)
870 octets[0] = 0x80 | (len(octets) - 1)
871 for i in six_xrange(len(octets) - 1, 0, -1):
877 def len_decode(data):
880 :returns: (decoded length, length's length, remaining data)
881 :raises LenIndefForm: if indefinite form encoding is met
884 raise NotEnoughData("no data at all")
885 first_octet = byte2int(data)
886 if first_octet & 0x80 == 0:
887 return first_octet, 1, data[1:]
888 octets_num = first_octet & 0x7F
889 if octets_num + 1 > len(data):
890 raise NotEnoughData("encoded length is longer than data")
893 if byte2int(data[1:]) == 0:
894 raise DecodeError("leading zeros")
896 for v in iterbytes(data[1:1 + octets_num]):
899 raise DecodeError("long form instead of short one")
900 return l, 1 + octets_num, data[1 + octets_num:]
903 ########################################################################
905 ########################################################################
907 class AutoAddSlots(type):
908 def __new__(mcs, name, bases, _dict):
909 _dict["__slots__"] = _dict.get("__slots__", ())
910 return type.__new__(mcs, name, bases, _dict)
913 @add_metaclass(AutoAddSlots)
915 """Common ASN.1 object class
917 All ASN.1 types are inherited from it. It has metaclass that
918 automatically adds ``__slots__`` to all inherited classes.
942 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
943 self._expl = getattr(self, "expl", None) if expl is None else expl
944 if self.tag != self.tag_default and self._expl is not None:
945 raise ValueError("implicit and explicit tags can not be set simultaneously")
946 if default is not None:
948 self.optional = optional
949 self.offset, self.llen, self.vlen = _decoded
951 self.expl_lenindef = False
952 self.lenindef = False
953 self.ber_encoded = False
956 def ready(self): # pragma: no cover
957 """Is object ready to be encoded?
959 raise NotImplementedError()
963 """Is either object or any elements inside is BER encoded?
965 return self.expl_lenindef or self.lenindef or self.ber_encoded
967 def _assert_ready(self):
969 raise ObjNotReady(self.__class__.__name__)
973 """Is object decoded?
975 return (self.llen + self.vlen) > 0
977 def copy(self): # pragma: no cover
978 """Make a copy of object, safe to be mutated
980 raise NotImplementedError()
988 return self.tlen + self.llen + self.vlen
990 def __str__(self): # pragma: no cover
991 return self.__bytes__() if PY2 else self.__unicode__()
993 def __ne__(self, their):
994 return not(self == their)
996 def __gt__(self, their): # pragma: no cover
997 return not(self < their)
999 def __le__(self, their): # pragma: no cover
1000 return (self == their) or (self < their)
1002 def __ge__(self, their): # pragma: no cover
1003 return (self == their) or (self > their)
1005 def _encode(self): # pragma: no cover
1006 raise NotImplementedError()
1008 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1009 raise NotImplementedError()
1012 raw = self._encode()
1013 if self._expl is None:
1015 return b"".join((self._expl, len_encode(len(raw)), raw))
1028 :param data: either binary or memoryview
1029 :param int offset: initial data's offset
1030 :param bool leavemm: do we need to leave memoryview of remaining
1031 data as is, or convert it to bytes otherwise
1032 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1033 :param tag_only: decode only the tag, without length and contents
1034 (used only in Choice and Set structures, trying to
1035 determine if tag satisfies the scheme)
1036 :returns: (Obj, remaining data)
1040 tlv = memoryview(data)
1041 if self._expl is None:
1042 result = self._decode(
1045 decode_path=decode_path,
1054 t, tlen, lv = tag_strip(tlv)
1055 except DecodeError as err:
1056 raise err.__class__(
1058 klass=self.__class__,
1059 decode_path=decode_path,
1064 klass=self.__class__,
1065 decode_path=decode_path,
1069 l, llen, v = len_decode(lv)
1070 except LenIndefForm as err:
1071 if not ctx.get("bered", False):
1072 raise err.__class__(
1074 klass=self.__class__,
1075 decode_path=decode_path,
1079 offset += tlen + llen
1080 result = self._decode(
1083 decode_path=decode_path,
1090 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1091 if eoc_expected.tobytes() != EOC:
1094 klass=self.__class__,
1095 decode_path=decode_path,
1099 obj.expl_lenindef = True
1100 except DecodeError as err:
1101 raise err.__class__(
1103 klass=self.__class__,
1104 decode_path=decode_path,
1109 raise NotEnoughData(
1110 "encoded length is longer than data",
1111 klass=self.__class__,
1112 decode_path=decode_path,
1115 result = self._decode(
1117 offset=offset + tlen + llen,
1118 decode_path=decode_path,
1125 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1127 "explicit tag out-of-bound, longer than data",
1128 klass=self.__class__,
1129 decode_path=decode_path,
1132 return obj, (tail if leavemm else tail.tobytes())
1136 return self._expl is not None
1143 def expl_tlen(self):
1144 return len(self._expl)
1147 def expl_llen(self):
1148 if self.expl_lenindef:
1150 return len(len_encode(self.tlvlen))
1153 def expl_offset(self):
1154 return self.offset - self.expl_tlen - self.expl_llen
1157 def expl_vlen(self):
1161 def expl_tlvlen(self):
1162 return self.expl_tlen + self.expl_llen + self.expl_vlen
1165 def fulloffset(self):
1166 return self.expl_offset if self.expled else self.offset
1170 return self.expl_tlvlen if self.expled else self.tlvlen
1172 def pps_lenindef(self, decode_path):
1175 asn1_type_name="EOC",
1177 decode_path=decode_path,
1179 self.offset + self.tlvlen -
1180 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1188 if self.expl_lenindef:
1190 asn1_type_name="EOC",
1191 obj_name="EXPLICIT",
1192 decode_path=decode_path,
1193 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1202 class DecodePathDefBy(object):
1203 """DEFINED BY representation inside decode path
1205 __slots__ = ("defined_by",)
1207 def __init__(self, defined_by):
1208 self.defined_by = defined_by
1210 def __ne__(self, their):
1211 return not(self == their)
1213 def __eq__(self, their):
1214 if not isinstance(their, self.__class__):
1216 return self.defined_by == their.defined_by
1219 return "DEFINED BY " + str(self.defined_by)
1222 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1225 ########################################################################
1227 ########################################################################
1229 PP = namedtuple("PP", (
1255 asn1_type_name="unknown",
1272 expl_lenindef=False,
1302 def _colourize(what, colour, with_colours, attrs=("bold",)):
1303 return colored(what, colour, attrs=attrs) if with_colours else what
1312 with_decode_path=False,
1313 decode_path_len_decrease=0,
1320 " " if pp.expl_offset is None else
1321 ("-%d" % (pp.offset - pp.expl_offset))
1323 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1325 col = _colourize(col, "red", with_colours, ())
1326 col += _colourize("B", "red", with_colours) if pp.bered else " "
1328 col = "[%d,%d,%4d]%s" % (
1332 LENINDEF_PP_CHAR if pp.lenindef else " "
1334 col = _colourize(col, "green", with_colours, ())
1336 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1337 if decode_path_len > 0:
1338 cols.append(" ." * decode_path_len)
1339 ent = pp.decode_path[-1]
1340 if isinstance(ent, DecodePathDefBy):
1341 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1342 value = str(ent.defined_by)
1344 oids is not None and
1345 ent.defined_by.asn1_type_name ==
1346 ObjectIdentifier.asn1_type_name and
1349 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1351 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1353 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1354 if pp.expl is not None:
1355 klass, _, num = pp.expl
1356 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1357 cols.append(_colourize(col, "blue", with_colours))
1358 if pp.impl is not None:
1359 klass, _, num = pp.impl
1360 col = "[%s%d]" % (TagClassReprs[klass], num)
1361 cols.append(_colourize(col, "blue", with_colours))
1362 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1363 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1365 cols.append(_colourize("BER", "red", with_colours))
1366 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1367 if pp.value is not None:
1369 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1371 oids is not None and
1372 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1375 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1377 if isinstance(pp.blob, binary_type):
1378 cols.append(hexenc(pp.blob))
1379 elif isinstance(pp.blob, tuple):
1380 cols.append(", ".join(pp.blob))
1382 cols.append(_colourize("OPTIONAL", "red", with_colours))
1384 cols.append(_colourize("DEFAULT", "red", with_colours))
1385 if with_decode_path:
1386 cols.append(_colourize(
1387 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1391 return " ".join(cols)
1394 def pp_console_blob(pp, decode_path_len_decrease=0):
1395 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1396 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1397 if decode_path_len > 0:
1398 cols.append(" ." * (decode_path_len + 1))
1399 if isinstance(pp.blob, binary_type):
1400 blob = hexenc(pp.blob).upper()
1401 for i in range(0, len(blob), 32):
1402 chunk = blob[i:i + 32]
1403 yield " ".join(cols + [":".join(
1404 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1406 elif isinstance(pp.blob, tuple):
1407 yield " ".join(cols + [", ".join(pp.blob)])
1415 with_decode_path=False,
1416 decode_path_only=(),
1418 """Pretty print object
1420 :param Obj obj: object you want to pretty print
1421 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1422 from it is met, then its humand readable form is printed
1423 :param big_blobs: if large binary objects are met (like OctetString
1424 values), do we need to print them too, on separate
1426 :param with_colours: colourize output, if ``termcolor`` library
1428 :param with_decode_path: print decode path
1429 :param decode_path_only: print only that specified decode path
1431 def _pprint_pps(pps):
1433 if hasattr(pp, "_fields"):
1435 decode_path_only != () and
1437 str(p) for p in pp.decode_path[:len(decode_path_only)]
1438 ) != decode_path_only
1442 yield pp_console_row(
1447 with_colours=with_colours,
1448 with_decode_path=with_decode_path,
1449 decode_path_len_decrease=len(decode_path_only),
1451 for row in pp_console_blob(
1453 decode_path_len_decrease=len(decode_path_only),
1457 yield pp_console_row(
1462 with_colours=with_colours,
1463 with_decode_path=with_decode_path,
1464 decode_path_len_decrease=len(decode_path_only),
1467 for row in _pprint_pps(pp):
1469 return "\n".join(_pprint_pps(obj.pps()))
1472 ########################################################################
1473 # ASN.1 primitive types
1474 ########################################################################
1477 """``BOOLEAN`` boolean type
1479 >>> b = Boolean(True)
1481 >>> b == Boolean(True)
1487 tag_default = tag_encode(1)
1488 asn1_type_name = "BOOLEAN"
1500 :param value: set the value. Either boolean type, or
1501 :py:class:`pyderasn.Boolean` object
1502 :param bytes impl: override default tag with ``IMPLICIT`` one
1503 :param bytes expl: override default tag with ``EXPLICIT`` one
1504 :param default: set default value. Type same as in ``value``
1505 :param bool optional: is object ``OPTIONAL`` in sequence
1507 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1508 self._value = None if value is None else self._value_sanitize(value)
1509 if default is not None:
1510 default = self._value_sanitize(default)
1511 self.default = self.__class__(
1517 self._value = default
1519 def _value_sanitize(self, value):
1520 if issubclass(value.__class__, Boolean):
1522 if isinstance(value, bool):
1524 raise InvalidValueType((self.__class__, bool))
1528 return self._value is not None
1531 obj = self.__class__()
1532 obj._value = self._value
1534 obj._expl = self._expl
1535 obj.default = self.default
1536 obj.optional = self.optional
1537 obj.offset = self.offset
1538 obj.llen = self.llen
1539 obj.vlen = self.vlen
1542 def __nonzero__(self):
1543 self._assert_ready()
1547 self._assert_ready()
1550 def __eq__(self, their):
1551 if isinstance(their, bool):
1552 return self._value == their
1553 if not issubclass(their.__class__, Boolean):
1556 self._value == their._value and
1557 self.tag == their.tag and
1558 self._expl == their._expl
1569 return self.__class__(
1571 impl=self.tag if impl is None else impl,
1572 expl=self._expl if expl is None else expl,
1573 default=self.default if default is None else default,
1574 optional=self.optional if optional is None else optional,
1578 self._assert_ready()
1582 (b"\xFF" if self._value else b"\x00"),
1585 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1587 t, _, lv = tag_strip(tlv)
1588 except DecodeError as err:
1589 raise err.__class__(
1591 klass=self.__class__,
1592 decode_path=decode_path,
1597 klass=self.__class__,
1598 decode_path=decode_path,
1604 l, _, v = len_decode(lv)
1605 except DecodeError as err:
1606 raise err.__class__(
1608 klass=self.__class__,
1609 decode_path=decode_path,
1613 raise InvalidLength(
1614 "Boolean's length must be equal to 1",
1615 klass=self.__class__,
1616 decode_path=decode_path,
1620 raise NotEnoughData(
1621 "encoded length is longer than data",
1622 klass=self.__class__,
1623 decode_path=decode_path,
1626 first_octet = byte2int(v)
1628 if first_octet == 0:
1630 elif first_octet == 0xFF:
1632 elif ctx.get("bered", False):
1637 "unacceptable Boolean value",
1638 klass=self.__class__,
1639 decode_path=decode_path,
1642 obj = self.__class__(
1646 default=self.default,
1647 optional=self.optional,
1648 _decoded=(offset, 1, 1),
1650 obj.ber_encoded = ber_encoded
1654 return pp_console_row(next(self.pps()))
1656 def pps(self, decode_path=()):
1658 asn1_type_name=self.asn1_type_name,
1659 obj_name=self.__class__.__name__,
1660 decode_path=decode_path,
1661 value=str(self._value) if self.ready else None,
1662 optional=self.optional,
1663 default=self == self.default,
1664 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1665 expl=None if self._expl is None else tag_decode(self._expl),
1670 expl_offset=self.expl_offset if self.expled else None,
1671 expl_tlen=self.expl_tlen if self.expled else None,
1672 expl_llen=self.expl_llen if self.expled else None,
1673 expl_vlen=self.expl_vlen if self.expled else None,
1674 expl_lenindef=self.expl_lenindef,
1675 ber_encoded=self.ber_encoded,
1678 for pp in self.pps_lenindef(decode_path):
1683 """``INTEGER`` integer type
1685 >>> b = Integer(-123)
1687 >>> b == Integer(-123)
1692 >>> Integer(2, bounds=(1, 3))
1694 >>> Integer(5, bounds=(1, 3))
1695 Traceback (most recent call last):
1696 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1700 class Version(Integer):
1707 >>> v = Version("v1")
1714 {'v3': 2, 'v1': 0, 'v2': 1}
1716 __slots__ = ("specs", "_bound_min", "_bound_max")
1717 tag_default = tag_encode(2)
1718 asn1_type_name = "INTEGER"
1732 :param value: set the value. Either integer type, named value
1733 (if ``schema`` is specified in the class), or
1734 :py:class:`pyderasn.Integer` object
1735 :param bounds: set ``(MIN, MAX)`` value constraint.
1736 (-inf, +inf) by default
1737 :param bytes impl: override default tag with ``IMPLICIT`` one
1738 :param bytes expl: override default tag with ``EXPLICIT`` one
1739 :param default: set default value. Type same as in ``value``
1740 :param bool optional: is object ``OPTIONAL`` in sequence
1742 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1744 specs = getattr(self, "schema", {}) if _specs is None else _specs
1745 self.specs = specs if isinstance(specs, dict) else dict(specs)
1746 self._bound_min, self._bound_max = getattr(
1749 (float("-inf"), float("+inf")),
1750 ) if bounds is None else bounds
1751 if value is not None:
1752 self._value = self._value_sanitize(value)
1753 if default is not None:
1754 default = self._value_sanitize(default)
1755 self.default = self.__class__(
1761 if self._value is None:
1762 self._value = default
1764 def _value_sanitize(self, value):
1765 if issubclass(value.__class__, Integer):
1766 value = value._value
1767 elif isinstance(value, integer_types):
1769 elif isinstance(value, str):
1770 value = self.specs.get(value)
1772 raise ObjUnknown("integer value: %s" % value)
1774 raise InvalidValueType((self.__class__, int, str))
1775 if not self._bound_min <= value <= self._bound_max:
1776 raise BoundsError(self._bound_min, value, self._bound_max)
1781 return self._value is not None
1784 obj = self.__class__(_specs=self.specs)
1785 obj._value = self._value
1786 obj._bound_min = self._bound_min
1787 obj._bound_max = self._bound_max
1789 obj._expl = self._expl
1790 obj.default = self.default
1791 obj.optional = self.optional
1792 obj.offset = self.offset
1793 obj.llen = self.llen
1794 obj.vlen = self.vlen
1798 self._assert_ready()
1799 return int(self._value)
1802 self._assert_ready()
1805 bytes(self._expl or b"") +
1806 str(self._value).encode("ascii"),
1809 def __eq__(self, their):
1810 if isinstance(their, integer_types):
1811 return self._value == their
1812 if not issubclass(their.__class__, Integer):
1815 self._value == their._value and
1816 self.tag == their.tag and
1817 self._expl == their._expl
1820 def __lt__(self, their):
1821 return self._value < their._value
1825 for name, value in self.specs.items():
1826 if value == self._value:
1838 return self.__class__(
1841 (self._bound_min, self._bound_max)
1842 if bounds is None else bounds
1844 impl=self.tag if impl is None else impl,
1845 expl=self._expl if expl is None else expl,
1846 default=self.default if default is None else default,
1847 optional=self.optional if optional is None else optional,
1852 self._assert_ready()
1856 octets = bytearray([0])
1860 octets = bytearray()
1862 octets.append((value & 0xFF) ^ 0xFF)
1864 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1867 octets = bytearray()
1869 octets.append(value & 0xFF)
1871 if octets[-1] & 0x80 > 0:
1874 octets = bytes(octets)
1876 bytes_len = ceil(value.bit_length() / 8) or 1
1879 octets = value.to_bytes(
1884 except OverflowError:
1888 return b"".join((self.tag, len_encode(len(octets)), octets))
1890 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1892 t, _, lv = tag_strip(tlv)
1893 except DecodeError as err:
1894 raise err.__class__(
1896 klass=self.__class__,
1897 decode_path=decode_path,
1902 klass=self.__class__,
1903 decode_path=decode_path,
1909 l, llen, v = len_decode(lv)
1910 except DecodeError as err:
1911 raise err.__class__(
1913 klass=self.__class__,
1914 decode_path=decode_path,
1918 raise NotEnoughData(
1919 "encoded length is longer than data",
1920 klass=self.__class__,
1921 decode_path=decode_path,
1925 raise NotEnoughData(
1927 klass=self.__class__,
1928 decode_path=decode_path,
1931 v, tail = v[:l], v[l:]
1932 first_octet = byte2int(v)
1934 second_octet = byte2int(v[1:])
1936 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1937 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1940 "non normalized integer",
1941 klass=self.__class__,
1942 decode_path=decode_path,
1947 if first_octet & 0x80 > 0:
1948 octets = bytearray()
1949 for octet in bytearray(v):
1950 octets.append(octet ^ 0xFF)
1951 for octet in octets:
1952 value = (value << 8) | octet
1956 for octet in bytearray(v):
1957 value = (value << 8) | octet
1959 value = int.from_bytes(v, byteorder="big", signed=True)
1961 obj = self.__class__(
1963 bounds=(self._bound_min, self._bound_max),
1966 default=self.default,
1967 optional=self.optional,
1969 _decoded=(offset, llen, l),
1971 except BoundsError as err:
1974 klass=self.__class__,
1975 decode_path=decode_path,
1981 return pp_console_row(next(self.pps()))
1983 def pps(self, decode_path=()):
1985 asn1_type_name=self.asn1_type_name,
1986 obj_name=self.__class__.__name__,
1987 decode_path=decode_path,
1988 value=(self.named or str(self._value)) if self.ready else None,
1989 optional=self.optional,
1990 default=self == self.default,
1991 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1992 expl=None if self._expl is None else tag_decode(self._expl),
1997 expl_offset=self.expl_offset if self.expled else None,
1998 expl_tlen=self.expl_tlen if self.expled else None,
1999 expl_llen=self.expl_llen if self.expled else None,
2000 expl_vlen=self.expl_vlen if self.expled else None,
2001 expl_lenindef=self.expl_lenindef,
2004 for pp in self.pps_lenindef(decode_path):
2008 class BitString(Obj):
2009 """``BIT STRING`` bit string type
2011 >>> BitString(b"hello world")
2012 BIT STRING 88 bits 68656c6c6f20776f726c64
2015 >>> b == b"hello world"
2020 >>> BitString("'0A3B5F291CD'H")
2021 BIT STRING 44 bits 0a3b5f291cd0
2022 >>> b = BitString("'010110000000'B")
2023 BIT STRING 12 bits 5800
2026 >>> b[0], b[1], b[2], b[3]
2027 (False, True, False, True)
2031 [False, True, False, True, True, False, False, False, False, False, False, False]
2035 class KeyUsage(BitString):
2037 ("digitalSignature", 0),
2038 ("nonRepudiation", 1),
2039 ("keyEncipherment", 2),
2042 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2043 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2045 ['nonRepudiation', 'keyEncipherment']
2047 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2051 Pay attention that BIT STRING can be encoded both in primitive
2052 and constructed forms. Decoder always checks constructed form tag
2053 additionally to specified primitive one. If BER decoding is
2054 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2055 of DER restrictions.
2057 __slots__ = ("tag_constructed", "specs", "defined")
2058 tag_default = tag_encode(3)
2059 asn1_type_name = "BIT STRING"
2072 :param value: set the value. Either binary type, tuple of named
2073 values (if ``schema`` is specified in the class),
2074 string in ``'XXX...'B`` form, or
2075 :py:class:`pyderasn.BitString` object
2076 :param bytes impl: override default tag with ``IMPLICIT`` one
2077 :param bytes expl: override default tag with ``EXPLICIT`` one
2078 :param default: set default value. Type same as in ``value``
2079 :param bool optional: is object ``OPTIONAL`` in sequence
2081 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2082 specs = getattr(self, "schema", {}) if _specs is None else _specs
2083 self.specs = specs if isinstance(specs, dict) else dict(specs)
2084 self._value = None if value is None else self._value_sanitize(value)
2085 if default is not None:
2086 default = self._value_sanitize(default)
2087 self.default = self.__class__(
2093 self._value = default
2095 tag_klass, _, tag_num = tag_decode(self.tag)
2096 self.tag_constructed = tag_encode(
2098 form=TagFormConstructed,
2102 def _bits2octets(self, bits):
2103 if len(self.specs) > 0:
2104 bits = bits.rstrip("0")
2106 bits += "0" * ((8 - (bit_len % 8)) % 8)
2107 octets = bytearray(len(bits) // 8)
2108 for i in six_xrange(len(octets)):
2109 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2110 return bit_len, bytes(octets)
2112 def _value_sanitize(self, value):
2113 if issubclass(value.__class__, BitString):
2115 if isinstance(value, (string_types, binary_type)):
2117 isinstance(value, string_types) and
2118 value.startswith("'")
2120 if value.endswith("'B"):
2122 if not set(value) <= set(("0", "1")):
2123 raise ValueError("B's coding contains unacceptable chars")
2124 return self._bits2octets(value)
2125 elif value.endswith("'H"):
2129 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2131 if isinstance(value, binary_type):
2132 return (len(value) * 8, value)
2134 raise InvalidValueType((self.__class__, string_types, binary_type))
2135 if isinstance(value, tuple):
2138 isinstance(value[0], integer_types) and
2139 isinstance(value[1], binary_type)
2144 bit = self.specs.get(name)
2146 raise ObjUnknown("BitString value: %s" % name)
2149 return self._bits2octets("")
2151 return self._bits2octets("".join(
2152 ("1" if bit in bits else "0")
2153 for bit in six_xrange(max(bits) + 1)
2155 raise InvalidValueType((self.__class__, binary_type, string_types))
2159 return self._value is not None
2162 obj = self.__class__(_specs=self.specs)
2164 if value is not None:
2165 value = (value[0], value[1])
2168 obj._expl = self._expl
2169 obj.default = self.default
2170 obj.optional = self.optional
2171 obj.offset = self.offset
2172 obj.llen = self.llen
2173 obj.vlen = self.vlen
2177 self._assert_ready()
2178 for i in six_xrange(self._value[0]):
2183 self._assert_ready()
2184 return self._value[0]
2186 def __bytes__(self):
2187 self._assert_ready()
2188 return self._value[1]
2190 def __eq__(self, their):
2191 if isinstance(their, bytes):
2192 return self._value[1] == their
2193 if not issubclass(their.__class__, BitString):
2196 self._value == their._value and
2197 self.tag == their.tag and
2198 self._expl == their._expl
2203 return [name for name, bit in self.specs.items() if self[bit]]
2213 return self.__class__(
2215 impl=self.tag if impl is None else impl,
2216 expl=self._expl if expl is None else expl,
2217 default=self.default if default is None else default,
2218 optional=self.optional if optional is None else optional,
2222 def __getitem__(self, key):
2223 if isinstance(key, int):
2224 bit_len, octets = self._value
2228 byte2int(memoryview(octets)[key // 8:]) >>
2231 if isinstance(key, string_types):
2232 value = self.specs.get(key)
2234 raise ObjUnknown("BitString value: %s" % key)
2236 raise InvalidValueType((int, str))
2239 self._assert_ready()
2240 bit_len, octets = self._value
2243 len_encode(len(octets) + 1),
2244 int2byte((8 - bit_len % 8) % 8),
2248 def _decode_chunk(self, lv, offset, decode_path, ctx):
2250 l, llen, v = len_decode(lv)
2251 except DecodeError as err:
2252 raise err.__class__(
2254 klass=self.__class__,
2255 decode_path=decode_path,
2259 raise NotEnoughData(
2260 "encoded length is longer than data",
2261 klass=self.__class__,
2262 decode_path=decode_path,
2266 raise NotEnoughData(
2268 klass=self.__class__,
2269 decode_path=decode_path,
2272 pad_size = byte2int(v)
2273 if l == 1 and pad_size != 0:
2275 "invalid empty value",
2276 klass=self.__class__,
2277 decode_path=decode_path,
2283 klass=self.__class__,
2284 decode_path=decode_path,
2287 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2290 klass=self.__class__,
2291 decode_path=decode_path,
2294 v, tail = v[:l], v[l:]
2295 obj = self.__class__(
2296 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2299 default=self.default,
2300 optional=self.optional,
2302 _decoded=(offset, llen, l),
2306 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2308 t, tlen, lv = tag_strip(tlv)
2309 except DecodeError as err:
2310 raise err.__class__(
2312 klass=self.__class__,
2313 decode_path=decode_path,
2319 return self._decode_chunk(lv, offset, decode_path, ctx)
2320 if t == self.tag_constructed:
2321 if not ctx.get("bered", False):
2323 "unallowed BER constructed encoding",
2324 klass=self.__class__,
2325 decode_path=decode_path,
2332 l, llen, v = len_decode(lv)
2333 except LenIndefForm:
2334 llen, l, v = 1, 0, lv[1:]
2336 except DecodeError as err:
2337 raise err.__class__(
2339 klass=self.__class__,
2340 decode_path=decode_path,
2344 raise NotEnoughData(
2345 "encoded length is longer than data",
2346 klass=self.__class__,
2347 decode_path=decode_path,
2350 if not lenindef and l == 0:
2351 raise NotEnoughData(
2353 klass=self.__class__,
2354 decode_path=decode_path,
2358 sub_offset = offset + tlen + llen
2362 if v[:EOC_LEN].tobytes() == EOC:
2369 "chunk out of bounds",
2370 klass=self.__class__,
2371 decode_path=decode_path + (str(len(chunks) - 1),),
2372 offset=chunks[-1].offset,
2374 sub_decode_path = decode_path + (str(len(chunks)),)
2376 chunk, v_tail = BitString().decode(
2379 decode_path=sub_decode_path,
2385 "expected BitString encoded chunk",
2386 klass=self.__class__,
2387 decode_path=sub_decode_path,
2390 chunks.append(chunk)
2391 sub_offset += chunk.tlvlen
2392 vlen += chunk.tlvlen
2394 if len(chunks) == 0:
2397 klass=self.__class__,
2398 decode_path=decode_path,
2403 for chunk_i, chunk in enumerate(chunks[:-1]):
2404 if chunk.bit_len % 8 != 0:
2406 "BitString chunk is not multiple of 8 bits",
2407 klass=self.__class__,
2408 decode_path=decode_path + (str(chunk_i),),
2409 offset=chunk.offset,
2411 values.append(bytes(chunk))
2412 bit_len += chunk.bit_len
2413 chunk_last = chunks[-1]
2414 values.append(bytes(chunk_last))
2415 bit_len += chunk_last.bit_len
2416 obj = self.__class__(
2417 value=(bit_len, b"".join(values)),
2420 default=self.default,
2421 optional=self.optional,
2423 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2425 obj.lenindef = lenindef
2426 obj.ber_encoded = True
2427 return obj, (v[EOC_LEN:] if lenindef else v)
2429 klass=self.__class__,
2430 decode_path=decode_path,
2435 return pp_console_row(next(self.pps()))
2437 def pps(self, decode_path=()):
2441 bit_len, blob = self._value
2442 value = "%d bits" % bit_len
2443 if len(self.specs) > 0:
2444 blob = tuple(self.named)
2446 asn1_type_name=self.asn1_type_name,
2447 obj_name=self.__class__.__name__,
2448 decode_path=decode_path,
2451 optional=self.optional,
2452 default=self == self.default,
2453 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2454 expl=None if self._expl is None else tag_decode(self._expl),
2459 expl_offset=self.expl_offset if self.expled else None,
2460 expl_tlen=self.expl_tlen if self.expled else None,
2461 expl_llen=self.expl_llen if self.expled else None,
2462 expl_vlen=self.expl_vlen if self.expled else None,
2463 expl_lenindef=self.expl_lenindef,
2464 lenindef=self.lenindef,
2465 ber_encoded=self.ber_encoded,
2468 defined_by, defined = self.defined or (None, None)
2469 if defined_by is not None:
2471 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2473 for pp in self.pps_lenindef(decode_path):
2477 class OctetString(Obj):
2478 """``OCTET STRING`` binary string type
2480 >>> s = OctetString(b"hello world")
2481 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2482 >>> s == OctetString(b"hello world")
2487 >>> OctetString(b"hello", bounds=(4, 4))
2488 Traceback (most recent call last):
2489 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2490 >>> OctetString(b"hell", bounds=(4, 4))
2491 OCTET STRING 4 bytes 68656c6c
2495 Pay attention that OCTET STRING can be encoded both in primitive
2496 and constructed forms. Decoder always checks constructed form tag
2497 additionally to specified primitive one. If BER decoding is
2498 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2499 of DER restrictions.
2501 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2502 tag_default = tag_encode(4)
2503 asn1_type_name = "OCTET STRING"
2516 :param value: set the value. Either binary type, or
2517 :py:class:`pyderasn.OctetString` object
2518 :param bounds: set ``(MIN, MAX)`` value size constraint.
2519 (-inf, +inf) by default
2520 :param bytes impl: override default tag with ``IMPLICIT`` one
2521 :param bytes expl: override default tag with ``EXPLICIT`` one
2522 :param default: set default value. Type same as in ``value``
2523 :param bool optional: is object ``OPTIONAL`` in sequence
2525 super(OctetString, self).__init__(
2533 self._bound_min, self._bound_max = getattr(
2537 ) if bounds is None else bounds
2538 if value is not None:
2539 self._value = self._value_sanitize(value)
2540 if default is not None:
2541 default = self._value_sanitize(default)
2542 self.default = self.__class__(
2547 if self._value is None:
2548 self._value = default
2550 tag_klass, _, tag_num = tag_decode(self.tag)
2551 self.tag_constructed = tag_encode(
2553 form=TagFormConstructed,
2557 def _value_sanitize(self, value):
2558 if issubclass(value.__class__, OctetString):
2559 value = value._value
2560 elif isinstance(value, binary_type):
2563 raise InvalidValueType((self.__class__, bytes))
2564 if not self._bound_min <= len(value) <= self._bound_max:
2565 raise BoundsError(self._bound_min, len(value), self._bound_max)
2570 return self._value is not None
2573 obj = self.__class__()
2574 obj._value = self._value
2575 obj._bound_min = self._bound_min
2576 obj._bound_max = self._bound_max
2578 obj._expl = self._expl
2579 obj.default = self.default
2580 obj.optional = self.optional
2581 obj.offset = self.offset
2582 obj.llen = self.llen
2583 obj.vlen = self.vlen
2586 def __bytes__(self):
2587 self._assert_ready()
2590 def __eq__(self, their):
2591 if isinstance(their, binary_type):
2592 return self._value == their
2593 if not issubclass(their.__class__, OctetString):
2596 self._value == their._value and
2597 self.tag == their.tag and
2598 self._expl == their._expl
2601 def __lt__(self, their):
2602 return self._value < their._value
2613 return self.__class__(
2616 (self._bound_min, self._bound_max)
2617 if bounds is None else bounds
2619 impl=self.tag if impl is None else impl,
2620 expl=self._expl if expl is None else expl,
2621 default=self.default if default is None else default,
2622 optional=self.optional if optional is None else optional,
2626 self._assert_ready()
2629 len_encode(len(self._value)),
2633 def _decode_chunk(self, lv, offset, decode_path, ctx):
2635 l, llen, v = len_decode(lv)
2636 except DecodeError as err:
2637 raise err.__class__(
2639 klass=self.__class__,
2640 decode_path=decode_path,
2644 raise NotEnoughData(
2645 "encoded length is longer than data",
2646 klass=self.__class__,
2647 decode_path=decode_path,
2650 v, tail = v[:l], v[l:]
2652 obj = self.__class__(
2654 bounds=(self._bound_min, self._bound_max),
2657 default=self.default,
2658 optional=self.optional,
2659 _decoded=(offset, llen, l),
2661 except DecodeError as err:
2664 klass=self.__class__,
2665 decode_path=decode_path,
2668 except BoundsError as err:
2671 klass=self.__class__,
2672 decode_path=decode_path,
2677 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2679 t, tlen, lv = tag_strip(tlv)
2680 except DecodeError as err:
2681 raise err.__class__(
2683 klass=self.__class__,
2684 decode_path=decode_path,
2690 return self._decode_chunk(lv, offset, decode_path, ctx)
2691 if t == self.tag_constructed:
2692 if not ctx.get("bered", False):
2694 "unallowed BER constructed encoding",
2695 klass=self.__class__,
2696 decode_path=decode_path,
2703 l, llen, v = len_decode(lv)
2704 except LenIndefForm:
2705 llen, l, v = 1, 0, lv[1:]
2707 except DecodeError as err:
2708 raise err.__class__(
2710 klass=self.__class__,
2711 decode_path=decode_path,
2715 raise NotEnoughData(
2716 "encoded length is longer than data",
2717 klass=self.__class__,
2718 decode_path=decode_path,
2722 sub_offset = offset + tlen + llen
2726 if v[:EOC_LEN].tobytes() == EOC:
2733 "chunk out of bounds",
2734 klass=self.__class__,
2735 decode_path=decode_path + (str(len(chunks) - 1),),
2736 offset=chunks[-1].offset,
2738 sub_decode_path = decode_path + (str(len(chunks)),)
2740 chunk, v_tail = OctetString().decode(
2743 decode_path=sub_decode_path,
2749 "expected OctetString encoded chunk",
2750 klass=self.__class__,
2751 decode_path=sub_decode_path,
2754 chunks.append(chunk)
2755 sub_offset += chunk.tlvlen
2756 vlen += chunk.tlvlen
2759 obj = self.__class__(
2760 value=b"".join(bytes(chunk) for chunk in chunks),
2761 bounds=(self._bound_min, self._bound_max),
2764 default=self.default,
2765 optional=self.optional,
2766 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2768 except DecodeError as err:
2771 klass=self.__class__,
2772 decode_path=decode_path,
2775 except BoundsError as err:
2778 klass=self.__class__,
2779 decode_path=decode_path,
2782 obj.lenindef = lenindef
2783 obj.ber_encoded = True
2784 return obj, (v[EOC_LEN:] if lenindef else v)
2786 klass=self.__class__,
2787 decode_path=decode_path,
2792 return pp_console_row(next(self.pps()))
2794 def pps(self, decode_path=()):
2796 asn1_type_name=self.asn1_type_name,
2797 obj_name=self.__class__.__name__,
2798 decode_path=decode_path,
2799 value=("%d bytes" % len(self._value)) if self.ready else None,
2800 blob=self._value if self.ready else None,
2801 optional=self.optional,
2802 default=self == self.default,
2803 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2804 expl=None if self._expl is None else tag_decode(self._expl),
2809 expl_offset=self.expl_offset if self.expled else None,
2810 expl_tlen=self.expl_tlen if self.expled else None,
2811 expl_llen=self.expl_llen if self.expled else None,
2812 expl_vlen=self.expl_vlen if self.expled else None,
2813 expl_lenindef=self.expl_lenindef,
2814 lenindef=self.lenindef,
2815 ber_encoded=self.ber_encoded,
2818 defined_by, defined = self.defined or (None, None)
2819 if defined_by is not None:
2821 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2823 for pp in self.pps_lenindef(decode_path):
2828 """``NULL`` null object
2836 tag_default = tag_encode(5)
2837 asn1_type_name = "NULL"
2841 value=None, # unused, but Sequence passes it
2848 :param bytes impl: override default tag with ``IMPLICIT`` one
2849 :param bytes expl: override default tag with ``EXPLICIT`` one
2850 :param bool optional: is object ``OPTIONAL`` in sequence
2852 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2860 obj = self.__class__()
2862 obj._expl = self._expl
2863 obj.default = self.default
2864 obj.optional = self.optional
2865 obj.offset = self.offset
2866 obj.llen = self.llen
2867 obj.vlen = self.vlen
2870 def __eq__(self, their):
2871 if not issubclass(their.__class__, Null):
2874 self.tag == their.tag and
2875 self._expl == their._expl
2885 return self.__class__(
2886 impl=self.tag if impl is None else impl,
2887 expl=self._expl if expl is None else expl,
2888 optional=self.optional if optional is None else optional,
2892 return self.tag + len_encode(0)
2894 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2896 t, _, lv = tag_strip(tlv)
2897 except DecodeError as err:
2898 raise err.__class__(
2900 klass=self.__class__,
2901 decode_path=decode_path,
2906 klass=self.__class__,
2907 decode_path=decode_path,
2913 l, _, v = len_decode(lv)
2914 except DecodeError as err:
2915 raise err.__class__(
2917 klass=self.__class__,
2918 decode_path=decode_path,
2922 raise InvalidLength(
2923 "Null must have zero length",
2924 klass=self.__class__,
2925 decode_path=decode_path,
2928 obj = self.__class__(
2931 optional=self.optional,
2932 _decoded=(offset, 1, 0),
2937 return pp_console_row(next(self.pps()))
2939 def pps(self, decode_path=()):
2941 asn1_type_name=self.asn1_type_name,
2942 obj_name=self.__class__.__name__,
2943 decode_path=decode_path,
2944 optional=self.optional,
2945 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2946 expl=None if self._expl is None else tag_decode(self._expl),
2951 expl_offset=self.expl_offset if self.expled else None,
2952 expl_tlen=self.expl_tlen if self.expled else None,
2953 expl_llen=self.expl_llen if self.expled else None,
2954 expl_vlen=self.expl_vlen if self.expled else None,
2955 expl_lenindef=self.expl_lenindef,
2958 for pp in self.pps_lenindef(decode_path):
2962 class ObjectIdentifier(Obj):
2963 """``OBJECT IDENTIFIER`` OID type
2965 >>> oid = ObjectIdentifier((1, 2, 3))
2966 OBJECT IDENTIFIER 1.2.3
2967 >>> oid == ObjectIdentifier("1.2.3")
2973 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2974 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2976 >>> str(ObjectIdentifier((3, 1)))
2977 Traceback (most recent call last):
2978 pyderasn.InvalidOID: unacceptable first arc value
2980 __slots__ = ("defines",)
2981 tag_default = tag_encode(6)
2982 asn1_type_name = "OBJECT IDENTIFIER"
2995 :param value: set the value. Either tuples of integers,
2996 string of "."-concatenated integers, or
2997 :py:class:`pyderasn.ObjectIdentifier` object
2998 :param defines: sequence of tuples. Each tuple has two elements.
2999 First one is relative to current one decode
3000 path, aiming to the field defined by that OID.
3001 Read about relative path in
3002 :py:func:`pyderasn.abs_decode_path`. Second
3003 tuple element is ``{OID: pyderasn.Obj()}``
3004 dictionary, mapping between current OID value
3005 and structure applied to defined field.
3006 :ref:`Read about DEFINED BY <definedby>`
3007 :param bytes impl: override default tag with ``IMPLICIT`` one
3008 :param bytes expl: override default tag with ``EXPLICIT`` one
3009 :param default: set default value. Type same as in ``value``
3010 :param bool optional: is object ``OPTIONAL`` in sequence
3012 super(ObjectIdentifier, self).__init__(
3020 if value is not None:
3021 self._value = self._value_sanitize(value)
3022 if default is not None:
3023 default = self._value_sanitize(default)
3024 self.default = self.__class__(
3029 if self._value is None:
3030 self._value = default
3031 self.defines = defines
3033 def __add__(self, their):
3034 if isinstance(their, self.__class__):
3035 return self.__class__(self._value + their._value)
3036 if isinstance(their, tuple):
3037 return self.__class__(self._value + their)
3038 raise InvalidValueType((self.__class__, tuple))
3040 def _value_sanitize(self, value):
3041 if issubclass(value.__class__, ObjectIdentifier):
3043 if isinstance(value, string_types):
3045 value = tuple(int(arc) for arc in value.split("."))
3047 raise InvalidOID("unacceptable arcs values")
3048 if isinstance(value, tuple):
3050 raise InvalidOID("less than 2 arcs")
3051 first_arc = value[0]
3052 if first_arc in (0, 1):
3053 if not (0 <= value[1] <= 39):
3054 raise InvalidOID("second arc is too wide")
3055 elif first_arc == 2:
3058 raise InvalidOID("unacceptable first arc value")
3060 raise InvalidValueType((self.__class__, str, tuple))
3064 return self._value is not None
3067 obj = self.__class__()
3068 obj._value = self._value
3069 obj.defines = self.defines
3071 obj._expl = self._expl
3072 obj.default = self.default
3073 obj.optional = self.optional
3074 obj.offset = self.offset
3075 obj.llen = self.llen
3076 obj.vlen = self.vlen
3080 self._assert_ready()
3081 return iter(self._value)
3084 return ".".join(str(arc) for arc in self._value or ())
3087 self._assert_ready()
3090 bytes(self._expl or b"") +
3091 str(self._value).encode("ascii"),
3094 def __eq__(self, their):
3095 if isinstance(their, tuple):
3096 return self._value == their
3097 if not issubclass(their.__class__, ObjectIdentifier):
3100 self.tag == their.tag and
3101 self._expl == their._expl and
3102 self._value == their._value
3105 def __lt__(self, their):
3106 return self._value < their._value
3117 return self.__class__(
3119 defines=self.defines if defines is None else defines,
3120 impl=self.tag if impl is None else impl,
3121 expl=self._expl if expl is None else expl,
3122 default=self.default if default is None else default,
3123 optional=self.optional if optional is None else optional,
3127 self._assert_ready()
3129 first_value = value[1]
3130 first_arc = value[0]
3133 elif first_arc == 1:
3135 elif first_arc == 2:
3137 else: # pragma: no cover
3138 raise RuntimeError("invalid arc is stored")
3139 octets = [zero_ended_encode(first_value)]
3140 for arc in value[2:]:
3141 octets.append(zero_ended_encode(arc))
3142 v = b"".join(octets)
3143 return b"".join((self.tag, len_encode(len(v)), v))
3145 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3147 t, _, lv = tag_strip(tlv)
3148 except DecodeError as err:
3149 raise err.__class__(
3151 klass=self.__class__,
3152 decode_path=decode_path,
3157 klass=self.__class__,
3158 decode_path=decode_path,
3164 l, llen, v = len_decode(lv)
3165 except DecodeError as err:
3166 raise err.__class__(
3168 klass=self.__class__,
3169 decode_path=decode_path,
3173 raise NotEnoughData(
3174 "encoded length is longer than data",
3175 klass=self.__class__,
3176 decode_path=decode_path,
3180 raise NotEnoughData(
3182 klass=self.__class__,
3183 decode_path=decode_path,
3186 v, tail = v[:l], v[l:]
3192 octet = indexbytes(v, i)
3193 arc = (arc << 7) | (octet & 0x7F)
3194 if octet & 0x80 == 0:
3202 klass=self.__class__,
3203 decode_path=decode_path,
3207 second_arc = arcs[0]
3208 if 0 <= second_arc <= 39:
3210 elif 40 <= second_arc <= 79:
3216 obj = self.__class__(
3217 value=tuple([first_arc, second_arc] + arcs[1:]),
3220 default=self.default,
3221 optional=self.optional,
3222 _decoded=(offset, llen, l),
3227 return pp_console_row(next(self.pps()))
3229 def pps(self, decode_path=()):
3231 asn1_type_name=self.asn1_type_name,
3232 obj_name=self.__class__.__name__,
3233 decode_path=decode_path,
3234 value=str(self) if self.ready else None,
3235 optional=self.optional,
3236 default=self == self.default,
3237 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3238 expl=None if self._expl is None else tag_decode(self._expl),
3243 expl_offset=self.expl_offset if self.expled else None,
3244 expl_tlen=self.expl_tlen if self.expled else None,
3245 expl_llen=self.expl_llen if self.expled else None,
3246 expl_vlen=self.expl_vlen if self.expled else None,
3247 expl_lenindef=self.expl_lenindef,
3250 for pp in self.pps_lenindef(decode_path):
3254 class Enumerated(Integer):
3255 """``ENUMERATED`` integer type
3257 This type is identical to :py:class:`pyderasn.Integer`, but requires
3258 schema to be specified and does not accept values missing from it.
3261 tag_default = tag_encode(10)
3262 asn1_type_name = "ENUMERATED"
3273 bounds=None, # dummy argument, workability for Integer.decode
3275 super(Enumerated, self).__init__(
3284 if len(self.specs) == 0:
3285 raise ValueError("schema must be specified")
3287 def _value_sanitize(self, value):
3288 if isinstance(value, self.__class__):
3289 value = value._value
3290 elif isinstance(value, integer_types):
3291 if value not in list(self.specs.values()):
3293 "unknown integer value: %s" % value,
3294 klass=self.__class__,
3296 elif isinstance(value, string_types):
3297 value = self.specs.get(value)
3299 raise ObjUnknown("integer value: %s" % value)
3301 raise InvalidValueType((self.__class__, int, str))
3305 obj = self.__class__(_specs=self.specs)
3306 obj._value = self._value
3307 obj._bound_min = self._bound_min
3308 obj._bound_max = self._bound_max
3310 obj._expl = self._expl
3311 obj.default = self.default
3312 obj.optional = self.optional
3313 obj.offset = self.offset
3314 obj.llen = self.llen
3315 obj.vlen = self.vlen
3327 return self.__class__(
3329 impl=self.tag if impl is None else impl,
3330 expl=self._expl if expl is None else expl,
3331 default=self.default if default is None else default,
3332 optional=self.optional if optional is None else optional,
3337 class CommonString(OctetString):
3338 """Common class for all strings
3340 Everything resembles :py:class:`pyderasn.OctetString`, except
3341 ability to deal with unicode text strings.
3343 >>> hexenc("привет мир".encode("utf-8"))
3344 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3345 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3347 >>> s = UTF8String("привет мир")
3348 UTF8String UTF8String привет мир
3350 'привет мир'
3351 >>> hexenc(bytes(s))
3352 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3354 >>> PrintableString("привет мир")
3355 Traceback (most recent call last):
3356 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3358 >>> BMPString("ада", bounds=(2, 2))
3359 Traceback (most recent call last):
3360 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3361 >>> s = BMPString("ад", bounds=(2, 2))
3364 >>> hexenc(bytes(s))
3372 * - :py:class:`pyderasn.UTF8String`
3374 * - :py:class:`pyderasn.NumericString`
3376 * - :py:class:`pyderasn.PrintableString`
3378 * - :py:class:`pyderasn.TeletexString`
3380 * - :py:class:`pyderasn.T61String`
3382 * - :py:class:`pyderasn.VideotexString`
3384 * - :py:class:`pyderasn.IA5String`
3386 * - :py:class:`pyderasn.GraphicString`
3388 * - :py:class:`pyderasn.VisibleString`
3390 * - :py:class:`pyderasn.ISO646String`
3392 * - :py:class:`pyderasn.GeneralString`
3394 * - :py:class:`pyderasn.UniversalString`
3396 * - :py:class:`pyderasn.BMPString`
3399 __slots__ = ("encoding",)
3401 def _value_sanitize(self, value):
3403 value_decoded = None
3404 if isinstance(value, self.__class__):
3405 value_raw = value._value
3406 elif isinstance(value, text_type):
3407 value_decoded = value
3408 elif isinstance(value, binary_type):
3411 raise InvalidValueType((self.__class__, text_type, binary_type))
3414 value_decoded.encode(self.encoding)
3415 if value_raw is None else value_raw
3418 value_raw.decode(self.encoding)
3419 if value_decoded is None else value_decoded
3421 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3422 raise DecodeError(str(err))
3423 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3431 def __eq__(self, their):
3432 if isinstance(their, binary_type):
3433 return self._value == their
3434 if isinstance(their, text_type):
3435 return self._value == their.encode(self.encoding)
3436 if not isinstance(their, self.__class__):
3439 self._value == their._value and
3440 self.tag == their.tag and
3441 self._expl == their._expl
3444 def __unicode__(self):
3446 return self._value.decode(self.encoding)
3447 return text_type(self._value)
3450 return pp_console_row(next(self.pps(no_unicode=PY2)))
3452 def pps(self, decode_path=(), no_unicode=False):
3455 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3457 asn1_type_name=self.asn1_type_name,
3458 obj_name=self.__class__.__name__,
3459 decode_path=decode_path,
3461 optional=self.optional,
3462 default=self == self.default,
3463 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3464 expl=None if self._expl is None else tag_decode(self._expl),
3469 expl_offset=self.expl_offset if self.expled else None,
3470 expl_tlen=self.expl_tlen if self.expled else None,
3471 expl_llen=self.expl_llen if self.expled else None,
3472 expl_vlen=self.expl_vlen if self.expled else None,
3473 expl_lenindef=self.expl_lenindef,
3474 ber_encoded=self.ber_encoded,
3477 for pp in self.pps_lenindef(decode_path):
3481 class UTF8String(CommonString):
3483 tag_default = tag_encode(12)
3485 asn1_type_name = "UTF8String"
3488 class NumericString(CommonString):
3491 Its value is properly sanitized: only ASCII digits can be stored.
3494 tag_default = tag_encode(18)
3496 asn1_type_name = "NumericString"
3497 allowable_chars = set(digits.encode("ascii"))
3499 def _value_sanitize(self, value):
3500 value = super(NumericString, self)._value_sanitize(value)
3501 if not set(value) <= self.allowable_chars:
3502 raise DecodeError("non-numeric value")
3506 class PrintableString(CommonString):
3508 tag_default = tag_encode(19)
3510 asn1_type_name = "PrintableString"
3513 class TeletexString(CommonString):
3515 tag_default = tag_encode(20)
3517 asn1_type_name = "TeletexString"
3520 class T61String(TeletexString):
3522 asn1_type_name = "T61String"
3525 class VideotexString(CommonString):
3527 tag_default = tag_encode(21)
3528 encoding = "iso-8859-1"
3529 asn1_type_name = "VideotexString"
3532 class IA5String(CommonString):
3534 tag_default = tag_encode(22)
3536 asn1_type_name = "IA5"
3539 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3540 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3541 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3544 class UTCTime(CommonString):
3545 """``UTCTime`` datetime type
3547 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3548 UTCTime UTCTime 2017-09-30T22:07:50
3554 datetime.datetime(2017, 9, 30, 22, 7, 50)
3555 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3556 datetime.datetime(1957, 9, 30, 22, 7, 50)
3559 tag_default = tag_encode(23)
3561 asn1_type_name = "UTCTime"
3563 fmt = "%y%m%d%H%M%SZ"
3573 bounds=None, # dummy argument, workability for OctetString.decode
3576 :param value: set the value. Either datetime type, or
3577 :py:class:`pyderasn.UTCTime` object
3578 :param bytes impl: override default tag with ``IMPLICIT`` one
3579 :param bytes expl: override default tag with ``EXPLICIT`` one
3580 :param default: set default value. Type same as in ``value``
3581 :param bool optional: is object ``OPTIONAL`` in sequence
3583 super(UTCTime, self).__init__(
3591 if value is not None:
3592 self._value = self._value_sanitize(value)
3593 if default is not None:
3594 default = self._value_sanitize(default)
3595 self.default = self.__class__(
3600 if self._value is None:
3601 self._value = default
3603 def _value_sanitize(self, value):
3604 if isinstance(value, self.__class__):
3606 if isinstance(value, datetime):
3607 return value.strftime(self.fmt).encode("ascii")
3608 if isinstance(value, binary_type):
3610 value_decoded = value.decode("ascii")
3611 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3612 raise DecodeError("invalid UTCTime encoding")
3613 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3615 datetime.strptime(value_decoded, self.fmt)
3616 except (TypeError, ValueError):
3617 raise DecodeError("invalid UTCTime format")
3620 raise DecodeError("invalid UTCTime length")
3621 raise InvalidValueType((self.__class__, datetime))
3623 def __eq__(self, their):
3624 if isinstance(their, binary_type):
3625 return self._value == their
3626 if isinstance(their, datetime):
3627 return self.todatetime() == their
3628 if not isinstance(their, self.__class__):
3631 self._value == their._value and
3632 self.tag == their.tag and
3633 self._expl == their._expl
3636 def todatetime(self):
3637 """Convert to datetime
3641 Pay attention that UTCTime can not hold full year, so all years
3642 having < 50 years are treated as 20xx, 19xx otherwise, according
3643 to X.509 recomendation.
3645 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3646 year = value.year % 100
3648 year=(2000 + year) if year < 50 else (1900 + year),
3652 minute=value.minute,
3653 second=value.second,
3657 return pp_console_row(next(self.pps()))
3659 def pps(self, decode_path=()):
3661 asn1_type_name=self.asn1_type_name,
3662 obj_name=self.__class__.__name__,
3663 decode_path=decode_path,
3664 value=self.todatetime().isoformat() if self.ready else None,
3665 optional=self.optional,
3666 default=self == self.default,
3667 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3668 expl=None if self._expl is None else tag_decode(self._expl),
3673 expl_offset=self.expl_offset if self.expled else None,
3674 expl_tlen=self.expl_tlen if self.expled else None,
3675 expl_llen=self.expl_llen if self.expled else None,
3676 expl_vlen=self.expl_vlen if self.expled else None,
3677 expl_lenindef=self.expl_lenindef,
3678 ber_encoded=self.ber_encoded,
3681 for pp in self.pps_lenindef(decode_path):
3685 class GeneralizedTime(UTCTime):
3686 """``GeneralizedTime`` datetime type
3688 This type is similar to :py:class:`pyderasn.UTCTime`.
3690 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3691 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3693 '20170930220750.000123Z'
3694 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3695 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3698 tag_default = tag_encode(24)
3699 asn1_type_name = "GeneralizedTime"
3701 fmt = "%Y%m%d%H%M%SZ"
3702 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3704 def _value_sanitize(self, value):
3705 if isinstance(value, self.__class__):
3707 if isinstance(value, datetime):
3708 return value.strftime(
3709 self.fmt_ms if value.microsecond > 0 else self.fmt
3711 if isinstance(value, binary_type):
3713 value_decoded = value.decode("ascii")
3714 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3715 raise DecodeError("invalid GeneralizedTime encoding")
3716 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3718 datetime.strptime(value_decoded, self.fmt)
3719 except (TypeError, ValueError):
3721 "invalid GeneralizedTime (without ms) format",
3724 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3726 datetime.strptime(value_decoded, self.fmt_ms)
3727 except (TypeError, ValueError):
3729 "invalid GeneralizedTime (with ms) format",
3734 "invalid GeneralizedTime length",
3735 klass=self.__class__,
3737 raise InvalidValueType((self.__class__, datetime))
3739 def todatetime(self):
3740 value = self._value.decode("ascii")
3741 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3742 return datetime.strptime(value, self.fmt)
3743 return datetime.strptime(value, self.fmt_ms)
3746 class GraphicString(CommonString):
3748 tag_default = tag_encode(25)
3749 encoding = "iso-8859-1"
3750 asn1_type_name = "GraphicString"
3753 class VisibleString(CommonString):
3755 tag_default = tag_encode(26)
3757 asn1_type_name = "VisibleString"
3760 class ISO646String(VisibleString):
3762 asn1_type_name = "ISO646String"
3765 class GeneralString(CommonString):
3767 tag_default = tag_encode(27)
3768 encoding = "iso-8859-1"
3769 asn1_type_name = "GeneralString"
3772 class UniversalString(CommonString):
3774 tag_default = tag_encode(28)
3775 encoding = "utf-32-be"
3776 asn1_type_name = "UniversalString"
3779 class BMPString(CommonString):
3781 tag_default = tag_encode(30)
3782 encoding = "utf-16-be"
3783 asn1_type_name = "BMPString"
3787 """``CHOICE`` special type
3791 class GeneralName(Choice):
3793 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3794 ("dNSName", IA5String(impl=tag_ctxp(2))),
3797 >>> gn = GeneralName()
3799 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3800 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3801 >>> gn["dNSName"] = IA5String("bar.baz")
3802 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3803 >>> gn["rfc822Name"]
3806 [2] IA5String IA5 bar.baz
3809 >>> gn.value == gn["dNSName"]
3812 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3814 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3815 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3817 __slots__ = ("specs",)
3819 asn1_type_name = "CHOICE"
3832 :param value: set the value. Either ``(choice, value)`` tuple, or
3833 :py:class:`pyderasn.Choice` object
3834 :param bytes impl: can not be set, do **not** use it
3835 :param bytes expl: override default tag with ``EXPLICIT`` one
3836 :param default: set default value. Type same as in ``value``
3837 :param bool optional: is object ``OPTIONAL`` in sequence
3839 if impl is not None:
3840 raise ValueError("no implicit tag allowed for CHOICE")
3841 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3843 schema = getattr(self, "schema", ())
3844 if len(schema) == 0:
3845 raise ValueError("schema must be specified")
3847 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3850 if value is not None:
3851 self._value = self._value_sanitize(value)
3852 if default is not None:
3853 default_value = self._value_sanitize(default)
3854 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3855 default_obj.specs = self.specs
3856 default_obj._value = default_value
3857 self.default = default_obj
3859 self._value = default_obj.copy()._value
3861 def _value_sanitize(self, value):
3862 if isinstance(value, self.__class__):
3864 if isinstance(value, tuple) and len(value) == 2:
3866 spec = self.specs.get(choice)
3868 raise ObjUnknown(choice)
3869 if not isinstance(obj, spec.__class__):
3870 raise InvalidValueType((spec,))
3871 return (choice, spec(obj))
3872 raise InvalidValueType((self.__class__, tuple))
3876 return self._value is not None and self._value[1].ready
3880 return self.expl_lenindef or (
3881 (self._value is not None) and
3882 self._value[1].bered
3886 obj = self.__class__(schema=self.specs)
3887 obj._expl = self._expl
3888 obj.default = self.default
3889 obj.optional = self.optional
3890 obj.offset = self.offset
3891 obj.llen = self.llen
3892 obj.vlen = self.vlen
3894 if value is not None:
3895 obj._value = (value[0], value[1].copy())
3898 def __eq__(self, their):
3899 if isinstance(their, tuple) and len(their) == 2:
3900 return self._value == their
3901 if not isinstance(their, self.__class__):
3904 self.specs == their.specs and
3905 self._value == their._value
3915 return self.__class__(
3918 expl=self._expl if expl is None else expl,
3919 default=self.default if default is None else default,
3920 optional=self.optional if optional is None else optional,
3925 self._assert_ready()
3926 return self._value[0]
3930 self._assert_ready()
3931 return self._value[1]
3933 def __getitem__(self, key):
3934 if key not in self.specs:
3935 raise ObjUnknown(key)
3936 if self._value is None:
3938 choice, value = self._value
3943 def __setitem__(self, key, value):
3944 spec = self.specs.get(key)
3946 raise ObjUnknown(key)
3947 if not isinstance(value, spec.__class__):
3948 raise InvalidValueType((spec.__class__,))
3949 self._value = (key, spec(value))
3957 return self._value[1].decoded if self.ready else False
3960 self._assert_ready()
3961 return self._value[1].encode()
3963 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3964 for choice, spec in self.specs.items():
3965 sub_decode_path = decode_path + (choice,)
3971 decode_path=sub_decode_path,
3980 klass=self.__class__,
3981 decode_path=decode_path,
3986 value, tail = spec.decode(
3990 decode_path=sub_decode_path,
3993 obj = self.__class__(
3996 default=self.default,
3997 optional=self.optional,
3998 _decoded=(offset, 0, value.fulllen),
4000 obj._value = (choice, value)
4001 obj.lenindef = value.lenindef
4002 obj.ber_encoded = value.ber_encoded
4006 value = pp_console_row(next(self.pps()))
4008 value = "%s[%r]" % (value, self.value)
4011 def pps(self, decode_path=()):
4013 asn1_type_name=self.asn1_type_name,
4014 obj_name=self.__class__.__name__,
4015 decode_path=decode_path,
4016 value=self.choice if self.ready else None,
4017 optional=self.optional,
4018 default=self == self.default,
4019 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4020 expl=None if self._expl is None else tag_decode(self._expl),
4025 expl_lenindef=self.expl_lenindef,
4026 lenindef=self.lenindef,
4027 ber_encoded=self.ber_encoded,
4031 yield self.value.pps(decode_path=decode_path + (self.choice,))
4032 for pp in self.pps_lenindef(decode_path):
4036 class PrimitiveTypes(Choice):
4037 """Predefined ``CHOICE`` for all generic primitive types
4039 It could be useful for general decoding of some unspecified values:
4041 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4042 OCTET STRING 3 bytes 666f6f
4043 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4047 schema = tuple((klass.__name__, klass()) for klass in (
4072 """``ANY`` special type
4074 >>> Any(Integer(-123))
4076 >>> a = Any(OctetString(b"hello world").encode())
4077 ANY 040b68656c6c6f20776f726c64
4078 >>> hexenc(bytes(a))
4079 b'0x040x0bhello world'
4081 __slots__ = ("defined",)
4082 tag_default = tag_encode(0)
4083 asn1_type_name = "ANY"
4093 :param value: set the value. Either any kind of pyderasn's
4094 **ready** object, or bytes. Pay attention that
4095 **no** validation is performed is raw binary value
4097 :param bytes expl: override default tag with ``EXPLICIT`` one
4098 :param bool optional: is object ``OPTIONAL`` in sequence
4100 super(Any, self).__init__(None, expl, None, optional, _decoded)
4101 self._value = None if value is None else self._value_sanitize(value)
4104 def _value_sanitize(self, value):
4105 if isinstance(value, self.__class__):
4107 if isinstance(value, Obj):
4108 return value.encode()
4109 if isinstance(value, binary_type):
4111 raise InvalidValueType((self.__class__, Obj, binary_type))
4115 return self._value is not None
4119 if self.expl_lenindef or self.lenindef:
4121 if self.defined is None:
4123 return self.defined[1].bered
4126 obj = self.__class__()
4127 obj._value = self._value
4129 obj._expl = self._expl
4130 obj.optional = self.optional
4131 obj.offset = self.offset
4132 obj.llen = self.llen
4133 obj.vlen = self.vlen
4136 def __eq__(self, their):
4137 if isinstance(their, binary_type):
4138 return self._value == their
4139 if issubclass(their.__class__, Any):
4140 return self._value == their._value
4149 return self.__class__(
4151 expl=self._expl if expl is None else expl,
4152 optional=self.optional if optional is None else optional,
4155 def __bytes__(self):
4156 self._assert_ready()
4164 self._assert_ready()
4167 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4169 t, tlen, lv = tag_strip(tlv)
4170 except DecodeError as err:
4171 raise err.__class__(
4173 klass=self.__class__,
4174 decode_path=decode_path,
4178 l, llen, v = len_decode(lv)
4179 except LenIndefForm as err:
4180 if not ctx.get("bered", False):
4181 raise err.__class__(
4183 klass=self.__class__,
4184 decode_path=decode_path,
4187 llen, vlen, v = 1, 0, lv[1:]
4188 sub_offset = offset + tlen + llen
4190 while v[:EOC_LEN].tobytes() != EOC:
4191 chunk, v = Any().decode(
4194 decode_path=decode_path + (str(chunk_i),),
4198 vlen += chunk.tlvlen
4199 sub_offset += chunk.tlvlen
4201 tlvlen = tlen + llen + vlen + EOC_LEN
4202 obj = self.__class__(
4203 value=tlv[:tlvlen].tobytes(),
4205 optional=self.optional,
4206 _decoded=(offset, 0, tlvlen),
4210 return obj, v[EOC_LEN:]
4211 except DecodeError as err:
4212 raise err.__class__(
4214 klass=self.__class__,
4215 decode_path=decode_path,
4219 raise NotEnoughData(
4220 "encoded length is longer than data",
4221 klass=self.__class__,
4222 decode_path=decode_path,
4225 tlvlen = tlen + llen + l
4226 v, tail = tlv[:tlvlen], v[l:]
4227 obj = self.__class__(
4230 optional=self.optional,
4231 _decoded=(offset, 0, tlvlen),
4237 return pp_console_row(next(self.pps()))
4239 def pps(self, decode_path=()):
4241 asn1_type_name=self.asn1_type_name,
4242 obj_name=self.__class__.__name__,
4243 decode_path=decode_path,
4244 blob=self._value if self.ready else None,
4245 optional=self.optional,
4246 default=self == self.default,
4247 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4248 expl=None if self._expl is None else tag_decode(self._expl),
4253 expl_offset=self.expl_offset if self.expled else None,
4254 expl_tlen=self.expl_tlen if self.expled else None,
4255 expl_llen=self.expl_llen if self.expled else None,
4256 expl_vlen=self.expl_vlen if self.expled else None,
4257 expl_lenindef=self.expl_lenindef,
4258 lenindef=self.lenindef,
4261 defined_by, defined = self.defined or (None, None)
4262 if defined_by is not None:
4264 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4266 for pp in self.pps_lenindef(decode_path):
4270 ########################################################################
4271 # ASN.1 constructed types
4272 ########################################################################
4274 def get_def_by_path(defines_by_path, sub_decode_path):
4275 """Get define by decode path
4277 for path, define in defines_by_path:
4278 if len(path) != len(sub_decode_path):
4280 for p1, p2 in zip(path, sub_decode_path):
4281 if (p1 != any) and (p1 != p2):
4287 def abs_decode_path(decode_path, rel_path):
4288 """Create an absolute decode path from current and relative ones
4290 :param decode_path: current decode path, starting point.
4292 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4293 If first tuple's element is "/", then treat it as
4294 an absolute path, ignoring ``decode_path`` as
4295 starting point. Also this tuple can contain ".."
4296 elements, stripping the leading element from
4299 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4300 ("foo", "bar", "baz", "whatever")
4301 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4303 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4306 if rel_path[0] == "/":
4308 if rel_path[0] == "..":
4309 return abs_decode_path(decode_path[:-1], rel_path[1:])
4310 return decode_path + rel_path
4313 class Sequence(Obj):
4314 """``SEQUENCE`` structure type
4316 You have to make specification of sequence::
4318 class Extension(Sequence):
4320 ("extnID", ObjectIdentifier()),
4321 ("critical", Boolean(default=False)),
4322 ("extnValue", OctetString()),
4325 Then, you can work with it as with dictionary.
4327 >>> ext = Extension()
4328 >>> Extension().specs
4330 ('extnID', OBJECT IDENTIFIER),
4331 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4332 ('extnValue', OCTET STRING),
4334 >>> ext["extnID"] = "1.2.3"
4335 Traceback (most recent call last):
4336 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4337 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4339 You can determine if sequence is ready to be encoded:
4344 Traceback (most recent call last):
4345 pyderasn.ObjNotReady: object is not ready: extnValue
4346 >>> ext["extnValue"] = OctetString(b"foobar")
4350 Value you want to assign, must have the same **type** as in
4351 corresponding specification, but it can have different tags,
4352 optional/default attributes -- they will be taken from specification
4355 class TBSCertificate(Sequence):
4357 ("version", Version(expl=tag_ctxc(0), default="v1")),
4360 >>> tbs = TBSCertificate()
4361 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4363 Assign ``None`` to remove value from sequence.
4365 You can set values in Sequence during its initialization:
4367 >>> AlgorithmIdentifier((
4368 ("algorithm", ObjectIdentifier("1.2.3")),
4369 ("parameters", Any(Null()))
4371 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4373 You can determine if value exists/set in the sequence and take its value:
4375 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4378 OBJECT IDENTIFIER 1.2.3
4380 But pay attention that if value has default, then it won't be (not
4381 in) in the sequence (because ``DEFAULT`` must not be encoded in
4382 DER), but you can read its value:
4384 >>> "critical" in ext, ext["critical"]
4385 (False, BOOLEAN False)
4386 >>> ext["critical"] = Boolean(True)
4387 >>> "critical" in ext, ext["critical"]
4388 (True, BOOLEAN True)
4390 All defaulted values are always optional.
4392 .. _allow_default_values_ctx:
4394 DER prohibits default value encoding and will raise an error if
4395 default value is unexpectedly met during decode.
4396 If :ref:`bered <bered_ctx>` context option is set, then no error
4397 will be raised, but ``bered`` attribute set. You can disable strict
4398 defaulted values existence validation by setting
4399 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4401 Two sequences are equal if they have equal specification (schema),
4402 implicit/explicit tagging and the same values.
4404 __slots__ = ("specs",)
4405 tag_default = tag_encode(form=TagFormConstructed, num=16)
4406 asn1_type_name = "SEQUENCE"
4418 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4420 schema = getattr(self, "schema", ())
4422 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4425 if value is not None:
4426 if issubclass(value.__class__, Sequence):
4427 self._value = value._value
4428 elif hasattr(value, "__iter__"):
4429 for seq_key, seq_value in value:
4430 self[seq_key] = seq_value
4432 raise InvalidValueType((Sequence,))
4433 if default is not None:
4434 if not issubclass(default.__class__, Sequence):
4435 raise InvalidValueType((Sequence,))
4436 default_value = default._value
4437 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4438 default_obj.specs = self.specs
4439 default_obj._value = default_value
4440 self.default = default_obj
4442 self._value = default_obj.copy()._value
4446 for name, spec in self.specs.items():
4447 value = self._value.get(name)
4459 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4461 return any(value.bered for value in self._value.values())
4464 obj = self.__class__(schema=self.specs)
4466 obj._expl = self._expl
4467 obj.default = self.default
4468 obj.optional = self.optional
4469 obj.offset = self.offset
4470 obj.llen = self.llen
4471 obj.vlen = self.vlen
4472 obj._value = {k: v.copy() for k, v in self._value.items()}
4475 def __eq__(self, their):
4476 if not isinstance(their, self.__class__):
4479 self.specs == their.specs and
4480 self.tag == their.tag and
4481 self._expl == their._expl and
4482 self._value == their._value
4493 return self.__class__(
4496 impl=self.tag if impl is None else impl,
4497 expl=self._expl if expl is None else expl,
4498 default=self.default if default is None else default,
4499 optional=self.optional if optional is None else optional,
4502 def __contains__(self, key):
4503 return key in self._value
4505 def __setitem__(self, key, value):
4506 spec = self.specs.get(key)
4508 raise ObjUnknown(key)
4510 self._value.pop(key, None)
4512 if not isinstance(value, spec.__class__):
4513 raise InvalidValueType((spec.__class__,))
4514 value = spec(value=value)
4515 if spec.default is not None and value == spec.default:
4516 self._value.pop(key, None)
4518 self._value[key] = value
4520 def __getitem__(self, key):
4521 value = self._value.get(key)
4522 if value is not None:
4524 spec = self.specs.get(key)
4526 raise ObjUnknown(key)
4527 if spec.default is not None:
4531 def _encoded_values(self):
4533 for name, spec in self.specs.items():
4534 value = self._value.get(name)
4538 raise ObjNotReady(name)
4539 raws.append(value.encode())
4543 v = b"".join(self._encoded_values())
4544 return b"".join((self.tag, len_encode(len(v)), v))
4546 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4548 t, tlen, lv = tag_strip(tlv)
4549 except DecodeError as err:
4550 raise err.__class__(
4552 klass=self.__class__,
4553 decode_path=decode_path,
4558 klass=self.__class__,
4559 decode_path=decode_path,
4565 ctx_bered = ctx.get("bered", False)
4567 l, llen, v = len_decode(lv)
4568 except LenIndefForm as err:
4570 raise err.__class__(
4572 klass=self.__class__,
4573 decode_path=decode_path,
4576 l, llen, v = 0, 1, lv[1:]
4578 except DecodeError as err:
4579 raise err.__class__(
4581 klass=self.__class__,
4582 decode_path=decode_path,
4586 raise NotEnoughData(
4587 "encoded length is longer than data",
4588 klass=self.__class__,
4589 decode_path=decode_path,
4593 v, tail = v[:l], v[l:]
4595 sub_offset = offset + tlen + llen
4598 ctx_allow_default_values = ctx.get("allow_default_values", False)
4599 for name, spec in self.specs.items():
4600 if spec.optional and (
4601 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4605 sub_decode_path = decode_path + (name,)
4607 value, v_tail = spec.decode(
4611 decode_path=sub_decode_path,
4619 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4620 if defined is not None:
4621 defined_by, defined_spec = defined
4622 if issubclass(value.__class__, SequenceOf):
4623 for i, _value in enumerate(value):
4624 sub_sub_decode_path = sub_decode_path + (
4626 DecodePathDefBy(defined_by),
4628 defined_value, defined_tail = defined_spec.decode(
4629 memoryview(bytes(_value)),
4631 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4632 if value.expled else (value.tlen + value.llen)
4635 decode_path=sub_sub_decode_path,
4638 if len(defined_tail) > 0:
4641 klass=self.__class__,
4642 decode_path=sub_sub_decode_path,
4645 _value.defined = (defined_by, defined_value)
4647 defined_value, defined_tail = defined_spec.decode(
4648 memoryview(bytes(value)),
4650 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4651 if value.expled else (value.tlen + value.llen)
4654 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4657 if len(defined_tail) > 0:
4660 klass=self.__class__,
4661 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4664 value.defined = (defined_by, defined_value)
4666 value_len = value.fulllen
4668 sub_offset += value_len
4670 if spec.default is not None and value == spec.default:
4671 if ctx_bered or ctx_allow_default_values:
4675 "DEFAULT value met",
4676 klass=self.__class__,
4677 decode_path=sub_decode_path,
4680 values[name] = value
4682 spec_defines = getattr(spec, "defines", ())
4683 if len(spec_defines) == 0:
4684 defines_by_path = ctx.get("defines_by_path", ())
4685 if len(defines_by_path) > 0:
4686 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4687 if spec_defines is not None and len(spec_defines) > 0:
4688 for rel_path, schema in spec_defines:
4689 defined = schema.get(value, None)
4690 if defined is not None:
4691 ctx.setdefault("_defines", []).append((
4692 abs_decode_path(sub_decode_path[:-1], rel_path),
4696 if v[:EOC_LEN].tobytes() != EOC:
4699 klass=self.__class__,
4700 decode_path=decode_path,
4708 klass=self.__class__,
4709 decode_path=decode_path,
4712 obj = self.__class__(
4716 default=self.default,
4717 optional=self.optional,
4718 _decoded=(offset, llen, vlen),
4721 obj.lenindef = lenindef
4722 obj.ber_encoded = ber_encoded
4726 value = pp_console_row(next(self.pps()))
4728 for name in self.specs:
4729 _value = self._value.get(name)
4732 cols.append("%s: %s" % (name, repr(_value)))
4733 return "%s[%s]" % (value, "; ".join(cols))
4735 def pps(self, decode_path=()):
4737 asn1_type_name=self.asn1_type_name,
4738 obj_name=self.__class__.__name__,
4739 decode_path=decode_path,
4740 optional=self.optional,
4741 default=self == self.default,
4742 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4743 expl=None if self._expl is None else tag_decode(self._expl),
4748 expl_offset=self.expl_offset if self.expled else None,
4749 expl_tlen=self.expl_tlen if self.expled else None,
4750 expl_llen=self.expl_llen if self.expled else None,
4751 expl_vlen=self.expl_vlen if self.expled else None,
4752 expl_lenindef=self.expl_lenindef,
4753 lenindef=self.lenindef,
4754 ber_encoded=self.ber_encoded,
4757 for name in self.specs:
4758 value = self._value.get(name)
4761 yield value.pps(decode_path=decode_path + (name,))
4762 for pp in self.pps_lenindef(decode_path):
4766 class Set(Sequence):
4767 """``SET`` structure type
4769 Its usage is identical to :py:class:`pyderasn.Sequence`.
4771 .. _allow_unordered_set_ctx:
4773 DER prohibits unordered values encoding and will raise an error
4774 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4775 then no error will occure. Also you can disable strict values
4776 ordering check by setting ``"allow_unordered_set": True``
4777 :ref:`context <ctx>` option.
4780 tag_default = tag_encode(form=TagFormConstructed, num=17)
4781 asn1_type_name = "SET"
4784 raws = self._encoded_values()
4787 return b"".join((self.tag, len_encode(len(v)), v))
4789 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4791 t, tlen, lv = tag_strip(tlv)
4792 except DecodeError as err:
4793 raise err.__class__(
4795 klass=self.__class__,
4796 decode_path=decode_path,
4801 klass=self.__class__,
4802 decode_path=decode_path,
4808 ctx_bered = ctx.get("bered", False)
4810 l, llen, v = len_decode(lv)
4811 except LenIndefForm as err:
4813 raise err.__class__(
4815 klass=self.__class__,
4816 decode_path=decode_path,
4819 l, llen, v = 0, 1, lv[1:]
4821 except DecodeError as err:
4822 raise err.__class__(
4824 klass=self.__class__,
4825 decode_path=decode_path,
4829 raise NotEnoughData(
4830 "encoded length is longer than data",
4831 klass=self.__class__,
4835 v, tail = v[:l], v[l:]
4837 sub_offset = offset + tlen + llen
4840 ctx_allow_default_values = ctx.get("allow_default_values", False)
4841 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4842 value_prev = memoryview(v[:0])
4843 specs_items = self.specs.items
4845 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4847 for name, spec in specs_items():
4848 sub_decode_path = decode_path + (name,)
4854 decode_path=sub_decode_path,
4863 klass=self.__class__,
4864 decode_path=decode_path,
4867 value, v_tail = spec.decode(
4871 decode_path=sub_decode_path,
4874 value_len = value.fulllen
4875 if value_prev.tobytes() > v[:value_len].tobytes():
4876 if ctx_bered or ctx_allow_unordered_set:
4880 "unordered " + self.asn1_type_name,
4881 klass=self.__class__,
4882 decode_path=sub_decode_path,
4885 if spec.default is None or value != spec.default:
4887 elif ctx_bered or ctx_allow_default_values:
4891 "DEFAULT value met",
4892 klass=self.__class__,
4893 decode_path=sub_decode_path,
4896 values[name] = value
4897 value_prev = v[:value_len]
4898 sub_offset += value_len
4901 obj = self.__class__(
4905 default=self.default,
4906 optional=self.optional,
4907 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4910 if v[:EOC_LEN].tobytes() != EOC:
4913 klass=self.__class__,
4914 decode_path=decode_path,
4922 "not all values are ready",
4923 klass=self.__class__,
4924 decode_path=decode_path,
4927 obj.ber_encoded = ber_encoded
4931 class SequenceOf(Obj):
4932 """``SEQUENCE OF`` sequence type
4934 For that kind of type you must specify the object it will carry on
4935 (bounds are for example here, not required)::
4937 class Ints(SequenceOf):
4942 >>> ints.append(Integer(123))
4943 >>> ints.append(Integer(234))
4945 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4946 >>> [int(i) for i in ints]
4948 >>> ints.append(Integer(345))
4949 Traceback (most recent call last):
4950 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4953 >>> ints[1] = Integer(345)
4955 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4957 Also you can initialize sequence with preinitialized values:
4959 >>> ints = Ints([Integer(123), Integer(234)])
4961 __slots__ = ("spec", "_bound_min", "_bound_max")
4962 tag_default = tag_encode(form=TagFormConstructed, num=16)
4963 asn1_type_name = "SEQUENCE OF"
4976 super(SequenceOf, self).__init__(
4984 schema = getattr(self, "schema", None)
4986 raise ValueError("schema must be specified")
4988 self._bound_min, self._bound_max = getattr(
4992 ) if bounds is None else bounds
4994 if value is not None:
4995 self._value = self._value_sanitize(value)
4996 if default is not None:
4997 default_value = self._value_sanitize(default)
4998 default_obj = self.__class__(
5003 default_obj._value = default_value
5004 self.default = default_obj
5006 self._value = default_obj.copy()._value
5008 def _value_sanitize(self, value):
5009 if issubclass(value.__class__, SequenceOf):
5010 value = value._value
5011 elif hasattr(value, "__iter__"):
5014 raise InvalidValueType((self.__class__, iter))
5015 if not self._bound_min <= len(value) <= self._bound_max:
5016 raise BoundsError(self._bound_min, len(value), self._bound_max)
5018 if not isinstance(v, self.spec.__class__):
5019 raise InvalidValueType((self.spec.__class__,))
5024 return all(v.ready for v in self._value)
5028 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5030 return any(v.bered for v in self._value)
5033 obj = self.__class__(schema=self.spec)
5034 obj._bound_min = self._bound_min
5035 obj._bound_max = self._bound_max
5037 obj._expl = self._expl
5038 obj.default = self.default
5039 obj.optional = self.optional
5040 obj.offset = self.offset
5041 obj.llen = self.llen
5042 obj.vlen = self.vlen
5043 obj._value = [v.copy() for v in self._value]
5046 def __eq__(self, their):
5047 if isinstance(their, self.__class__):
5049 self.spec == their.spec and
5050 self.tag == their.tag and
5051 self._expl == their._expl and
5052 self._value == their._value
5054 if hasattr(their, "__iter__"):
5055 return self._value == list(their)
5067 return self.__class__(
5071 (self._bound_min, self._bound_max)
5072 if bounds is None else bounds
5074 impl=self.tag if impl is None else impl,
5075 expl=self._expl if expl is None else expl,
5076 default=self.default if default is None else default,
5077 optional=self.optional if optional is None else optional,
5080 def __contains__(self, key):
5081 return key in self._value
5083 def append(self, value):
5084 if not isinstance(value, self.spec.__class__):
5085 raise InvalidValueType((self.spec.__class__,))
5086 if len(self._value) + 1 > self._bound_max:
5089 len(self._value) + 1,
5092 self._value.append(value)
5095 self._assert_ready()
5096 return iter(self._value)
5099 self._assert_ready()
5100 return len(self._value)
5102 def __setitem__(self, key, value):
5103 if not isinstance(value, self.spec.__class__):
5104 raise InvalidValueType((self.spec.__class__,))
5105 self._value[key] = self.spec(value=value)
5107 def __getitem__(self, key):
5108 return self._value[key]
5110 def _encoded_values(self):
5111 return [v.encode() for v in self._value]
5114 v = b"".join(self._encoded_values())
5115 return b"".join((self.tag, len_encode(len(v)), v))
5117 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5119 t, tlen, lv = tag_strip(tlv)
5120 except DecodeError as err:
5121 raise err.__class__(
5123 klass=self.__class__,
5124 decode_path=decode_path,
5129 klass=self.__class__,
5130 decode_path=decode_path,
5136 ctx_bered = ctx.get("bered", False)
5138 l, llen, v = len_decode(lv)
5139 except LenIndefForm as err:
5141 raise err.__class__(
5143 klass=self.__class__,
5144 decode_path=decode_path,
5147 l, llen, v = 0, 1, lv[1:]
5149 except DecodeError as err:
5150 raise err.__class__(
5152 klass=self.__class__,
5153 decode_path=decode_path,
5157 raise NotEnoughData(
5158 "encoded length is longer than data",
5159 klass=self.__class__,
5160 decode_path=decode_path,
5164 v, tail = v[:l], v[l:]
5166 sub_offset = offset + tlen + llen
5168 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5169 value_prev = memoryview(v[:0])
5173 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5175 sub_decode_path = decode_path + (str(len(_value)),)
5176 value, v_tail = spec.decode(
5180 decode_path=sub_decode_path,
5183 value_len = value.fulllen
5185 if value_prev.tobytes() > v[:value_len].tobytes():
5186 if ctx_bered or ctx_allow_unordered_set:
5190 "unordered " + self.asn1_type_name,
5191 klass=self.__class__,
5192 decode_path=sub_decode_path,
5195 value_prev = v[:value_len]
5196 _value.append(value)
5197 sub_offset += value_len
5201 obj = self.__class__(
5204 bounds=(self._bound_min, self._bound_max),
5207 default=self.default,
5208 optional=self.optional,
5209 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5211 except BoundsError as err:
5214 klass=self.__class__,
5215 decode_path=decode_path,
5219 if v[:EOC_LEN].tobytes() != EOC:
5222 klass=self.__class__,
5223 decode_path=decode_path,
5228 obj.ber_encoded = ber_encoded
5233 pp_console_row(next(self.pps())),
5234 ", ".join(repr(v) for v in self._value),
5237 def pps(self, decode_path=()):
5239 asn1_type_name=self.asn1_type_name,
5240 obj_name=self.__class__.__name__,
5241 decode_path=decode_path,
5242 optional=self.optional,
5243 default=self == self.default,
5244 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5245 expl=None if self._expl is None else tag_decode(self._expl),
5250 expl_offset=self.expl_offset if self.expled else None,
5251 expl_tlen=self.expl_tlen if self.expled else None,
5252 expl_llen=self.expl_llen if self.expled else None,
5253 expl_vlen=self.expl_vlen if self.expled else None,
5254 expl_lenindef=self.expl_lenindef,
5255 lenindef=self.lenindef,
5256 ber_encoded=self.ber_encoded,
5259 for i, value in enumerate(self._value):
5260 yield value.pps(decode_path=decode_path + (str(i),))
5261 for pp in self.pps_lenindef(decode_path):
5265 class SetOf(SequenceOf):
5266 """``SET OF`` sequence type
5268 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5271 tag_default = tag_encode(form=TagFormConstructed, num=17)
5272 asn1_type_name = "SET OF"
5275 raws = self._encoded_values()
5278 return b"".join((self.tag, len_encode(len(v)), v))
5280 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5281 return super(SetOf, self)._decode(
5287 ordering_check=True,
5291 def obj_by_path(pypath): # pragma: no cover
5292 """Import object specified as string Python path
5294 Modules must be separated from classes/functions with ``:``.
5296 >>> obj_by_path("foo.bar:Baz")
5297 <class 'foo.bar.Baz'>
5298 >>> obj_by_path("foo.bar:Baz.boo")
5299 <classmethod 'foo.bar.Baz.boo'>
5301 mod, objs = pypath.rsplit(":", 1)
5302 from importlib import import_module
5303 obj = import_module(mod)
5304 for obj_name in objs.split("."):
5305 obj = getattr(obj, obj_name)
5309 def generic_decoder(): # pragma: no cover
5310 # All of this below is a big hack with self references
5311 choice = PrimitiveTypes()
5312 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5313 choice.specs["SetOf"] = SetOf(schema=choice)
5315 choice.specs["SequenceOf%d" % i] = SequenceOf(
5319 choice.specs["Any"] = Any()
5321 # Class name equals to type name, to omit it from output
5322 class SEQUENCEOF(SequenceOf):
5330 with_decode_path=False,
5331 decode_path_only=(),
5333 def _pprint_pps(pps):
5335 if hasattr(pp, "_fields"):
5337 decode_path_only != () and
5338 pp.decode_path[:len(decode_path_only)] != decode_path_only
5341 if pp.asn1_type_name == Choice.asn1_type_name:
5343 pp_kwargs = pp._asdict()
5344 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5345 pp = _pp(**pp_kwargs)
5346 yield pp_console_row(
5351 with_colours=with_colours,
5352 with_decode_path=with_decode_path,
5353 decode_path_len_decrease=len(decode_path_only),
5355 for row in pp_console_blob(
5357 decode_path_len_decrease=len(decode_path_only),
5361 for row in _pprint_pps(pp):
5363 return "\n".join(_pprint_pps(obj.pps()))
5364 return SEQUENCEOF(), pprint_any
5367 def main(): # pragma: no cover
5369 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5370 parser.add_argument(
5374 help="Skip that number of bytes from the beginning",
5376 parser.add_argument(
5378 help="Python path to dictionary with OIDs",
5380 parser.add_argument(
5382 help="Python path to schema definition to use",
5384 parser.add_argument(
5385 "--defines-by-path",
5386 help="Python path to decoder's defines_by_path",
5388 parser.add_argument(
5390 action="store_true",
5391 help="Disallow BER encoding",
5393 parser.add_argument(
5394 "--print-decode-path",
5395 action="store_true",
5396 help="Print decode paths",
5398 parser.add_argument(
5399 "--decode-path-only",
5400 help="Print only specified decode path",
5402 parser.add_argument(
5404 action="store_true",
5405 help="Allow explicit tag out-of-bound",
5407 parser.add_argument(
5409 type=argparse.FileType("rb"),
5410 help="Path to DER file you want to decode",
5412 args = parser.parse_args()
5413 args.DERFile.seek(args.skip)
5414 der = memoryview(args.DERFile.read())
5415 args.DERFile.close()
5416 oids = obj_by_path(args.oids) if args.oids else {}
5418 schema = obj_by_path(args.schema)
5419 from functools import partial
5420 pprinter = partial(pprint, big_blobs=True)
5422 schema, pprinter = generic_decoder()
5424 "bered": not args.nobered,
5425 "allow_expl_oob": args.allow_expl_oob,
5427 if args.defines_by_path is not None:
5428 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5429 obj, tail = schema().decode(der, ctx=ctx)
5433 with_colours=True if environ.get("NO_COLOR") is None else False,
5434 with_decode_path=args.print_decode_path,
5436 () if args.decode_path_only is None else
5437 tuple(args.decode_path_only.split(":"))
5441 print("\nTrailing data: %s" % hexenc(tail))
5444 if __name__ == "__main__":