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:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
203 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`bered <bered_ctx>`
210 * :ref:`defines_by_path <defines_by_path_ctx>`
211 * :ref:`strict_default_existence <strict_default_existence_ctx>`
218 All objects have ``pps()`` method, that is a generator of
219 :py:class:`pyderasn.PP` namedtuple, holding various raw information
220 about the object. If ``pps`` is called on sequences, then all underlying
221 ``PP`` will be yielded.
223 You can use :py:func:`pyderasn.pp_console_row` function, converting
224 those ``PP`` to human readable string. Actually exactly it is used for
225 all object ``repr``. But it is easy to write custom formatters.
227 >>> from pyderasn import pprint
228 >>> encoded = Integer(-12345).encode()
229 >>> obj, tail = Integer().decode(encoded)
230 >>> print(pprint(obj))
231 0 [1,1, 2] INTEGER -12345
238 ASN.1 structures often have ANY and OCTET STRING fields, that are
239 DEFINED BY some previously met ObjectIdentifier. This library provides
240 ability to specify mapping between some OID and field that must be
241 decoded with specific specification.
246 :py:class:`pyderasn.ObjectIdentifier` field inside
247 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
248 necessary for decoding structures. For example, CMS (:rfc:`5652`)
251 class ContentInfo(Sequence):
253 ("contentType", ContentType(defines=((("content",), {
254 id_digestedData: DigestedData(),
255 id_signedData: SignedData(),
257 ("content", Any(expl=tag_ctxc(0))),
260 ``contentType`` field tells that it defines that ``content`` must be
261 decoded with ``SignedData`` specification, if ``contentType`` equals to
262 ``id-signedData``. The same applies to ``DigestedData``. If
263 ``contentType`` contains unknown OID, then no automatic decoding is
266 You can specify multiple fields, that will be autodecoded -- that is why
267 ``defines`` kwarg is a sequence. You can specify defined field
268 relatively or absolutely to current decode path. For example ``defines``
269 for AlgorithmIdentifier of X.509's
270 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
274 id_ecPublicKey: ECParameters(),
275 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
277 (("..", "subjectPublicKey"), {
278 id_rsaEncryption: RSAPublicKey(),
279 id_GostR3410_2001: OctetString(),
283 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
284 autodecode its parameters inside SPKI's algorithm and its public key
287 Following types can be automatically decoded (DEFINED BY):
289 * :py:class:`pyderasn.Any`
290 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
291 * :py:class:`pyderasn.OctetString`
292 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
293 ``Any``/``BitString``/``OctetString``-s
295 When any of those fields is automatically decoded, then ``.defined``
296 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
297 was defined, ``value`` contains corresponding decoded value. For example
298 above, ``content_info["content"].defined == (id_signedData,
301 .. _defines_by_path_ctx:
303 defines_by_path context option
304 ______________________________
306 Sometimes you either can not or do not want to explicitly set *defines*
307 in the scheme. You can dynamically apply those definitions when calling
308 ``.decode()`` method.
310 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
311 value must be sequence of following tuples::
313 (decode_path, defines)
315 where ``decode_path`` is a tuple holding so-called decode path to the
316 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
317 ``defines``, holding exactly the same value as accepted in its keyword
320 For example, again for CMS, you want to automatically decode
321 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
322 structures it may hold. Also, automatically decode ``controlSequence``
325 content_info, tail = ContentInfo().decode(data, defines_by_path=(
328 ((("content",), {id_signedData: SignedData()}),),
333 DecodePathDefBy(id_signedData),
338 id_cct_PKIData: PKIData(),
339 id_cct_PKIResponse: PKIResponse(),
345 DecodePathDefBy(id_signedData),
348 DecodePathDefBy(id_cct_PKIResponse),
354 id_cmc_recipientNonce: RecipientNonce(),
355 id_cmc_senderNonce: SenderNonce(),
356 id_cmc_statusInfoV2: CMCStatusInfoV2(),
357 id_cmc_transactionId: TransactionId(),
362 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
363 First function is useful for path construction when some automatic
364 decoding is already done. ``any`` means literally any value it meet --
365 useful for SEQUENCE/SET OF-s.
374 Currently BER support is not extensively tested.
376 By default PyDERASN accepts only DER encoded data. It always encodes to
377 DER. But you can optionally enable BER decoding with setting ``bered``
378 :ref:`context <ctx>` argument to True. Indefinite lengths and
379 constructed primitive types should be parsed successfully.
381 * If object is encoded in BER form (not the DER one), then ``bered``
382 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
383 STRING`` can contain it.
384 * If object has an indefinite length encoding, then its ``lenindef``
385 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
386 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
388 * If object has an indefinite length encoded explicit tag, then
389 ``expl_lenindef`` is set to True.
391 EOC (end-of-contents) token's length is taken in advance in object's
399 .. autoclass:: pyderasn.Boolean
404 .. autoclass:: pyderasn.Integer
409 .. autoclass:: pyderasn.BitString
414 .. autoclass:: pyderasn.OctetString
419 .. autoclass:: pyderasn.Null
424 .. autoclass:: pyderasn.ObjectIdentifier
429 .. autoclass:: pyderasn.Enumerated
433 .. autoclass:: pyderasn.CommonString
437 .. autoclass:: pyderasn.NumericString
441 .. autoclass:: pyderasn.UTCTime
442 :members: __init__, todatetime
446 .. autoclass:: pyderasn.GeneralizedTime
453 .. autoclass:: pyderasn.Choice
458 .. autoclass:: PrimitiveTypes
462 .. autoclass:: pyderasn.Any
470 .. autoclass:: pyderasn.Sequence
475 .. autoclass:: pyderasn.Set
480 .. autoclass:: pyderasn.SequenceOf
485 .. autoclass:: pyderasn.SetOf
491 .. autofunction:: pyderasn.abs_decode_path
492 .. autofunction:: pyderasn.hexenc
493 .. autofunction:: pyderasn.hexdec
494 .. autofunction:: pyderasn.tag_encode
495 .. autofunction:: pyderasn.tag_decode
496 .. autofunction:: pyderasn.tag_ctxp
497 .. autofunction:: pyderasn.tag_ctxc
498 .. autoclass:: pyderasn.Obj
499 .. autoclass:: pyderasn.DecodeError
501 .. autoclass:: pyderasn.NotEnoughData
502 .. autoclass:: pyderasn.LenIndefForm
503 .. autoclass:: pyderasn.TagMismatch
504 .. autoclass:: pyderasn.InvalidLength
505 .. autoclass:: pyderasn.InvalidOID
506 .. autoclass:: pyderasn.ObjUnknown
507 .. autoclass:: pyderasn.ObjNotReady
508 .. autoclass:: pyderasn.InvalidValueType
509 .. autoclass:: pyderasn.BoundsError
512 from codecs import getdecoder
513 from codecs import getencoder
514 from collections import namedtuple
515 from collections import OrderedDict
516 from datetime import datetime
517 from math import ceil
518 from os import environ
519 from string import digits
521 from six import add_metaclass
522 from six import binary_type
523 from six import byte2int
524 from six import indexbytes
525 from six import int2byte
526 from six import integer_types
527 from six import iterbytes
529 from six import string_types
530 from six import text_type
531 from six.moves import xrange as six_xrange
535 from termcolor import colored
537 def colored(what, *args):
581 "TagClassApplication",
585 "TagFormConstructed",
596 TagClassUniversal = 0
597 TagClassApplication = 1 << 6
598 TagClassContext = 1 << 7
599 TagClassPrivate = 1 << 6 | 1 << 7
601 TagFormConstructed = 1 << 5
604 TagClassApplication: "APPLICATION ",
605 TagClassPrivate: "PRIVATE ",
606 TagClassUniversal: "UNIV ",
612 ########################################################################
614 ########################################################################
616 class DecodeError(Exception):
617 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
619 :param str msg: reason of decode failing
620 :param klass: optional exact DecodeError inherited class (like
621 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
622 :py:exc:`InvalidLength`)
623 :param decode_path: tuple of strings. It contains human
624 readable names of the fields through which
625 decoding process has passed
626 :param int offset: binary offset where failure happened
628 super(DecodeError, self).__init__()
631 self.decode_path = decode_path
637 "" if self.klass is None else self.klass.__name__,
639 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
640 if len(self.decode_path) > 0 else ""
642 ("(at %d)" % self.offset) if self.offset > 0 else "",
648 return "%s(%s)" % (self.__class__.__name__, self)
651 class NotEnoughData(DecodeError):
655 class LenIndefForm(DecodeError):
659 class TagMismatch(DecodeError):
663 class InvalidLength(DecodeError):
667 class InvalidOID(DecodeError):
671 class ObjUnknown(ValueError):
672 def __init__(self, name):
673 super(ObjUnknown, self).__init__()
677 return "object is unknown: %s" % self.name
680 return "%s(%s)" % (self.__class__.__name__, self)
683 class ObjNotReady(ValueError):
684 def __init__(self, name):
685 super(ObjNotReady, self).__init__()
689 return "object is not ready: %s" % self.name
692 return "%s(%s)" % (self.__class__.__name__, self)
695 class InvalidValueType(ValueError):
696 def __init__(self, expected_types):
697 super(InvalidValueType, self).__init__()
698 self.expected_types = expected_types
701 return "invalid value type, expected: %s" % ", ".join(
702 [repr(t) for t in self.expected_types]
706 return "%s(%s)" % (self.__class__.__name__, self)
709 class BoundsError(ValueError):
710 def __init__(self, bound_min, value, bound_max):
711 super(BoundsError, self).__init__()
712 self.bound_min = bound_min
714 self.bound_max = bound_max
717 return "unsatisfied bounds: %s <= %s <= %s" % (
724 return "%s(%s)" % (self.__class__.__name__, self)
727 ########################################################################
729 ########################################################################
731 _hexdecoder = getdecoder("hex")
732 _hexencoder = getencoder("hex")
736 """Binary data to hexadecimal string convert
738 return _hexdecoder(data)[0]
742 """Hexadecimal string to binary data convert
744 return _hexencoder(data)[0].decode("ascii")
747 def int_bytes_len(num, byte_len=8):
750 return int(ceil(float(num.bit_length()) / byte_len))
753 def zero_ended_encode(num):
754 octets = bytearray(int_bytes_len(num, 7))
756 octets[i] = num & 0x7F
760 octets[i] = 0x80 | (num & 0x7F)
766 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
767 """Encode tag to binary form
769 :param int num: tag's number
770 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
771 :py:data:`pyderasn.TagClassContext`,
772 :py:data:`pyderasn.TagClassApplication`,
773 :py:data:`pyderasn.TagClassPrivate`)
774 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
775 :py:data:`pyderasn.TagFormConstructed`)
779 return int2byte(klass | form | num)
780 # [XX|X|11111][1.......][1.......] ... [0.......]
781 return int2byte(klass | form | 31) + zero_ended_encode(num)
785 """Decode tag from binary form
789 No validation is performed, assuming that it has already passed.
791 It returns tuple with three integers, as
792 :py:func:`pyderasn.tag_encode` accepts.
794 first_octet = byte2int(tag)
795 klass = first_octet & 0xC0
796 form = first_octet & 0x20
797 if first_octet & 0x1F < 0x1F:
798 return (klass, form, first_octet & 0x1F)
800 for octet in iterbytes(tag[1:]):
803 return (klass, form, num)
807 """Create CONTEXT PRIMITIVE tag
809 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
813 """Create CONTEXT CONSTRUCTED tag
815 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
819 """Take off tag from the data
821 :returns: (encoded tag, tag length, remaining data)
824 raise NotEnoughData("no data at all")
825 if byte2int(data) & 0x1F < 31:
826 return data[:1], 1, data[1:]
831 raise DecodeError("unfinished tag")
832 if indexbytes(data, i) & 0x80 == 0:
835 return data[:i], i, data[i:]
841 octets = bytearray(int_bytes_len(l) + 1)
842 octets[0] = 0x80 | (len(octets) - 1)
843 for i in six_xrange(len(octets) - 1, 0, -1):
849 def len_decode(data):
852 :returns: (decoded length, length's length, remaining data)
853 :raises LenIndefForm: if indefinite form encoding is met
856 raise NotEnoughData("no data at all")
857 first_octet = byte2int(data)
858 if first_octet & 0x80 == 0:
859 return first_octet, 1, data[1:]
860 octets_num = first_octet & 0x7F
861 if octets_num + 1 > len(data):
862 raise NotEnoughData("encoded length is longer than data")
865 if byte2int(data[1:]) == 0:
866 raise DecodeError("leading zeros")
868 for v in iterbytes(data[1:1 + octets_num]):
871 raise DecodeError("long form instead of short one")
872 return l, 1 + octets_num, data[1 + octets_num:]
875 ########################################################################
877 ########################################################################
879 class AutoAddSlots(type):
880 def __new__(mcs, name, bases, _dict):
881 _dict["__slots__"] = _dict.get("__slots__", ())
882 return type.__new__(mcs, name, bases, _dict)
885 @add_metaclass(AutoAddSlots)
887 """Common ASN.1 object class
889 All ASN.1 types are inherited from it. It has metaclass that
890 automatically adds ``__slots__`` to all inherited classes.
914 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
915 self._expl = getattr(self, "expl", None) if expl is None else expl
916 if self.tag != self.tag_default and self._expl is not None:
917 raise ValueError("implicit and explicit tags can not be set simultaneously")
918 if default is not None:
920 self.optional = optional
921 self.offset, self.llen, self.vlen = _decoded
923 self.expl_lenindef = False
924 self.lenindef = False
928 def ready(self): # pragma: no cover
929 """Is object ready to be encoded?
931 raise NotImplementedError()
933 def _assert_ready(self):
935 raise ObjNotReady(self.__class__.__name__)
939 """Is object decoded?
941 return (self.llen + self.vlen) > 0
943 def copy(self): # pragma: no cover
944 """Make a copy of object, safe to be mutated
946 raise NotImplementedError()
954 return self.tlen + self.llen + self.vlen
956 def __str__(self): # pragma: no cover
957 return self.__bytes__() if PY2 else self.__unicode__()
959 def __ne__(self, their):
960 return not(self == their)
962 def __gt__(self, their): # pragma: no cover
963 return not(self < their)
965 def __le__(self, their): # pragma: no cover
966 return (self == their) or (self < their)
968 def __ge__(self, their): # pragma: no cover
969 return (self == their) or (self > their)
971 def _encode(self): # pragma: no cover
972 raise NotImplementedError()
974 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
975 raise NotImplementedError()
979 if self._expl is None:
981 return b"".join((self._expl, len_encode(len(raw)), raw))
994 :param data: either binary or memoryview
995 :param int offset: initial data's offset
996 :param bool leavemm: do we need to leave memoryview of remaining
997 data as is, or convert it to bytes otherwise
998 :param ctx: optional :ref:`context <ctx>` governing decoding process.
999 :param tag_only: decode only the tag, without length and contents
1000 (used only in Choice and Set structures, trying to
1001 determine if tag satisfies the scheme)
1002 :returns: (Obj, remaining data)
1006 tlv = memoryview(data)
1007 if self._expl is None:
1008 result = self._decode(
1011 decode_path=decode_path,
1020 t, tlen, lv = tag_strip(tlv)
1021 except DecodeError as err:
1022 raise err.__class__(
1024 klass=self.__class__,
1025 decode_path=decode_path,
1030 klass=self.__class__,
1031 decode_path=decode_path,
1035 l, llen, v = len_decode(lv)
1036 except LenIndefForm as err:
1037 if not ctx.get("bered", False):
1038 raise err.__class__(
1040 klass=self.__class__,
1041 decode_path=decode_path,
1045 offset += tlen + llen
1046 result = self._decode(
1049 decode_path=decode_path,
1056 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1057 if eoc_expected.tobytes() != EOC:
1060 klass=self.__class__,
1061 decode_path=decode_path,
1065 obj.expl_lenindef = True
1066 except DecodeError as err:
1067 raise err.__class__(
1069 klass=self.__class__,
1070 decode_path=decode_path,
1075 raise NotEnoughData(
1076 "encoded length is longer than data",
1077 klass=self.__class__,
1078 decode_path=decode_path,
1081 result = self._decode(
1083 offset=offset + tlen + llen,
1084 decode_path=decode_path,
1091 return obj, (tail if leavemm else tail.tobytes())
1095 return self._expl is not None
1102 def expl_tlen(self):
1103 return len(self._expl)
1106 def expl_llen(self):
1107 if self.expl_lenindef:
1109 return len(len_encode(self.tlvlen))
1112 def expl_offset(self):
1113 return self.offset - self.expl_tlen - self.expl_llen
1116 def expl_vlen(self):
1120 def expl_tlvlen(self):
1121 return self.expl_tlen + self.expl_llen + self.expl_vlen
1124 class DecodePathDefBy(object):
1125 """DEFINED BY representation inside decode path
1127 __slots__ = ("defined_by",)
1129 def __init__(self, defined_by):
1130 self.defined_by = defined_by
1132 def __ne__(self, their):
1133 return not(self == their)
1135 def __eq__(self, their):
1136 if not isinstance(their, self.__class__):
1138 return self.defined_by == their.defined_by
1141 return "DEFINED BY " + str(self.defined_by)
1144 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1147 ########################################################################
1149 ########################################################################
1151 PP = namedtuple("PP", (
1176 asn1_type_name="unknown",
1193 expl_lenindef=False,
1221 def _colorize(what, colour, with_colours, attrs=("bold",)):
1222 return colored(what, colour, attrs=attrs) if with_colours else what
1237 " " if pp.expl_offset is None else
1238 ("-%d" % (pp.offset - pp.expl_offset))
1241 cols.append(_colorize(col, "red", with_colours, ()))
1242 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1243 col = _colorize(col, "green", with_colours, ())
1245 if pp.expl_lenindef:
1250 " " if ber_deoffset == 0 else
1251 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1254 if len(pp.decode_path) > 0:
1255 cols.append(" ." * (len(pp.decode_path)))
1256 ent = pp.decode_path[-1]
1257 if isinstance(ent, DecodePathDefBy):
1258 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1259 value = str(ent.defined_by)
1261 oids is not None and
1262 ent.defined_by.asn1_type_name ==
1263 ObjectIdentifier.asn1_type_name and
1266 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1268 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1270 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1271 if pp.expl is not None:
1272 klass, _, num = pp.expl
1273 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1274 cols.append(_colorize(col, "blue", with_colours))
1275 if pp.impl is not None:
1276 klass, _, num = pp.impl
1277 col = "[%s%d]" % (TagClassReprs[klass], num)
1278 cols.append(_colorize(col, "blue", with_colours))
1279 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1280 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1282 cols.append(_colorize("BER", "red", with_colours))
1283 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1284 if pp.value is not None:
1286 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1288 oids is not None and
1289 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1292 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1294 if isinstance(pp.blob, binary_type):
1295 cols.append(hexenc(pp.blob))
1296 elif isinstance(pp.blob, tuple):
1297 cols.append(", ".join(pp.blob))
1299 cols.append(_colorize("OPTIONAL", "red", with_colours))
1301 cols.append(_colorize("DEFAULT", "red", with_colours))
1302 return " ".join(cols)
1305 def pp_console_blob(pp):
1306 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1307 if len(pp.decode_path) > 0:
1308 cols.append(" ." * (len(pp.decode_path) + 1))
1309 if isinstance(pp.blob, binary_type):
1310 blob = hexenc(pp.blob).upper()
1311 for i in range(0, len(blob), 32):
1312 chunk = blob[i:i + 32]
1313 yield " ".join(cols + [":".join(
1314 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1316 elif isinstance(pp.blob, tuple):
1317 yield " ".join(cols + [", ".join(pp.blob)])
1320 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1321 """Pretty print object
1323 :param Obj obj: object you want to pretty print
1324 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1325 from it is met, then its humand readable form is printed
1326 :param big_blobs: if large binary objects are met (like OctetString
1327 values), do we need to print them too, on separate
1329 :param with_colours: colourize output, if ``termcolor`` library
1332 def _pprint_pps(pps):
1334 if hasattr(pp, "_fields"):
1336 yield pp_console_row(
1341 with_colours=with_colours,
1343 for row in pp_console_blob(pp):
1346 yield pp_console_row(
1351 with_colours=with_colours,
1354 for row in _pprint_pps(pp):
1356 return "\n".join(_pprint_pps(obj.pps()))
1359 ########################################################################
1360 # ASN.1 primitive types
1361 ########################################################################
1364 """``BOOLEAN`` boolean type
1366 >>> b = Boolean(True)
1368 >>> b == Boolean(True)
1374 tag_default = tag_encode(1)
1375 asn1_type_name = "BOOLEAN"
1387 :param value: set the value. Either boolean type, or
1388 :py:class:`pyderasn.Boolean` object
1389 :param bytes impl: override default tag with ``IMPLICIT`` one
1390 :param bytes expl: override default tag with ``EXPLICIT`` one
1391 :param default: set default value. Type same as in ``value``
1392 :param bool optional: is object ``OPTIONAL`` in sequence
1394 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1395 self._value = None if value is None else self._value_sanitize(value)
1396 if default is not None:
1397 default = self._value_sanitize(default)
1398 self.default = self.__class__(
1404 self._value = default
1406 def _value_sanitize(self, value):
1407 if issubclass(value.__class__, Boolean):
1409 if isinstance(value, bool):
1411 raise InvalidValueType((self.__class__, bool))
1415 return self._value is not None
1418 obj = self.__class__()
1419 obj._value = self._value
1421 obj._expl = self._expl
1422 obj.default = self.default
1423 obj.optional = self.optional
1424 obj.offset = self.offset
1425 obj.llen = self.llen
1426 obj.vlen = self.vlen
1429 def __nonzero__(self):
1430 self._assert_ready()
1434 self._assert_ready()
1437 def __eq__(self, their):
1438 if isinstance(their, bool):
1439 return self._value == their
1440 if not issubclass(their.__class__, Boolean):
1443 self._value == their._value and
1444 self.tag == their.tag and
1445 self._expl == their._expl
1456 return self.__class__(
1458 impl=self.tag if impl is None else impl,
1459 expl=self._expl if expl is None else expl,
1460 default=self.default if default is None else default,
1461 optional=self.optional if optional is None else optional,
1465 self._assert_ready()
1469 (b"\xFF" if self._value else b"\x00"),
1472 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1474 t, _, lv = tag_strip(tlv)
1475 except DecodeError as err:
1476 raise err.__class__(
1478 klass=self.__class__,
1479 decode_path=decode_path,
1484 klass=self.__class__,
1485 decode_path=decode_path,
1491 l, _, v = len_decode(lv)
1492 except DecodeError as err:
1493 raise err.__class__(
1495 klass=self.__class__,
1496 decode_path=decode_path,
1500 raise InvalidLength(
1501 "Boolean's length must be equal to 1",
1502 klass=self.__class__,
1503 decode_path=decode_path,
1507 raise NotEnoughData(
1508 "encoded length is longer than data",
1509 klass=self.__class__,
1510 decode_path=decode_path,
1513 first_octet = byte2int(v)
1515 if first_octet == 0:
1517 elif first_octet == 0xFF:
1519 elif ctx.get("bered", False):
1524 "unacceptable Boolean value",
1525 klass=self.__class__,
1526 decode_path=decode_path,
1529 obj = self.__class__(
1533 default=self.default,
1534 optional=self.optional,
1535 _decoded=(offset, 1, 1),
1541 return pp_console_row(next(self.pps()))
1543 def pps(self, decode_path=()):
1545 asn1_type_name=self.asn1_type_name,
1546 obj_name=self.__class__.__name__,
1547 decode_path=decode_path,
1548 value=str(self._value) if self.ready else None,
1549 optional=self.optional,
1550 default=self == self.default,
1551 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1552 expl=None if self._expl is None else tag_decode(self._expl),
1557 expl_offset=self.expl_offset if self.expled else None,
1558 expl_tlen=self.expl_tlen if self.expled else None,
1559 expl_llen=self.expl_llen if self.expled else None,
1560 expl_vlen=self.expl_vlen if self.expled else None,
1561 expl_lenindef=self.expl_lenindef,
1567 """``INTEGER`` integer type
1569 >>> b = Integer(-123)
1571 >>> b == Integer(-123)
1576 >>> Integer(2, bounds=(1, 3))
1578 >>> Integer(5, bounds=(1, 3))
1579 Traceback (most recent call last):
1580 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1584 class Version(Integer):
1591 >>> v = Version("v1")
1598 {'v3': 2, 'v1': 0, 'v2': 1}
1600 __slots__ = ("specs", "_bound_min", "_bound_max")
1601 tag_default = tag_encode(2)
1602 asn1_type_name = "INTEGER"
1616 :param value: set the value. Either integer type, named value
1617 (if ``schema`` is specified in the class), or
1618 :py:class:`pyderasn.Integer` object
1619 :param bounds: set ``(MIN, MAX)`` value constraint.
1620 (-inf, +inf) by default
1621 :param bytes impl: override default tag with ``IMPLICIT`` one
1622 :param bytes expl: override default tag with ``EXPLICIT`` one
1623 :param default: set default value. Type same as in ``value``
1624 :param bool optional: is object ``OPTIONAL`` in sequence
1626 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1628 specs = getattr(self, "schema", {}) if _specs is None else _specs
1629 self.specs = specs if isinstance(specs, dict) else dict(specs)
1630 self._bound_min, self._bound_max = getattr(
1633 (float("-inf"), float("+inf")),
1634 ) if bounds is None else bounds
1635 if value is not None:
1636 self._value = self._value_sanitize(value)
1637 if default is not None:
1638 default = self._value_sanitize(default)
1639 self.default = self.__class__(
1645 if self._value is None:
1646 self._value = default
1648 def _value_sanitize(self, value):
1649 if issubclass(value.__class__, Integer):
1650 value = value._value
1651 elif isinstance(value, integer_types):
1653 elif isinstance(value, str):
1654 value = self.specs.get(value)
1656 raise ObjUnknown("integer value: %s" % value)
1658 raise InvalidValueType((self.__class__, int, str))
1659 if not self._bound_min <= value <= self._bound_max:
1660 raise BoundsError(self._bound_min, value, self._bound_max)
1665 return self._value is not None
1668 obj = self.__class__(_specs=self.specs)
1669 obj._value = self._value
1670 obj._bound_min = self._bound_min
1671 obj._bound_max = self._bound_max
1673 obj._expl = self._expl
1674 obj.default = self.default
1675 obj.optional = self.optional
1676 obj.offset = self.offset
1677 obj.llen = self.llen
1678 obj.vlen = self.vlen
1682 self._assert_ready()
1683 return int(self._value)
1686 self._assert_ready()
1689 bytes(self._expl or b"") +
1690 str(self._value).encode("ascii"),
1693 def __eq__(self, their):
1694 if isinstance(their, integer_types):
1695 return self._value == their
1696 if not issubclass(their.__class__, Integer):
1699 self._value == their._value and
1700 self.tag == their.tag and
1701 self._expl == their._expl
1704 def __lt__(self, their):
1705 return self._value < their._value
1709 for name, value in self.specs.items():
1710 if value == self._value:
1722 return self.__class__(
1725 (self._bound_min, self._bound_max)
1726 if bounds is None else bounds
1728 impl=self.tag if impl is None else impl,
1729 expl=self._expl if expl is None else expl,
1730 default=self.default if default is None else default,
1731 optional=self.optional if optional is None else optional,
1736 self._assert_ready()
1740 octets = bytearray([0])
1744 octets = bytearray()
1746 octets.append((value & 0xFF) ^ 0xFF)
1748 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1751 octets = bytearray()
1753 octets.append(value & 0xFF)
1755 if octets[-1] & 0x80 > 0:
1758 octets = bytes(octets)
1760 bytes_len = ceil(value.bit_length() / 8) or 1
1763 octets = value.to_bytes(
1768 except OverflowError:
1772 return b"".join((self.tag, len_encode(len(octets)), octets))
1774 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1776 t, _, lv = tag_strip(tlv)
1777 except DecodeError as err:
1778 raise err.__class__(
1780 klass=self.__class__,
1781 decode_path=decode_path,
1786 klass=self.__class__,
1787 decode_path=decode_path,
1793 l, llen, v = len_decode(lv)
1794 except DecodeError as err:
1795 raise err.__class__(
1797 klass=self.__class__,
1798 decode_path=decode_path,
1802 raise NotEnoughData(
1803 "encoded length is longer than data",
1804 klass=self.__class__,
1805 decode_path=decode_path,
1809 raise NotEnoughData(
1811 klass=self.__class__,
1812 decode_path=decode_path,
1815 v, tail = v[:l], v[l:]
1816 first_octet = byte2int(v)
1818 second_octet = byte2int(v[1:])
1820 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1821 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1824 "non normalized integer",
1825 klass=self.__class__,
1826 decode_path=decode_path,
1831 if first_octet & 0x80 > 0:
1832 octets = bytearray()
1833 for octet in bytearray(v):
1834 octets.append(octet ^ 0xFF)
1835 for octet in octets:
1836 value = (value << 8) | octet
1840 for octet in bytearray(v):
1841 value = (value << 8) | octet
1843 value = int.from_bytes(v, byteorder="big", signed=True)
1845 obj = self.__class__(
1847 bounds=(self._bound_min, self._bound_max),
1850 default=self.default,
1851 optional=self.optional,
1853 _decoded=(offset, llen, l),
1855 except BoundsError as err:
1858 klass=self.__class__,
1859 decode_path=decode_path,
1865 return pp_console_row(next(self.pps()))
1867 def pps(self, decode_path=()):
1869 asn1_type_name=self.asn1_type_name,
1870 obj_name=self.__class__.__name__,
1871 decode_path=decode_path,
1872 value=(self.named or str(self._value)) if self.ready else None,
1873 optional=self.optional,
1874 default=self == self.default,
1875 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1876 expl=None if self._expl is None else tag_decode(self._expl),
1881 expl_offset=self.expl_offset if self.expled else None,
1882 expl_tlen=self.expl_tlen if self.expled else None,
1883 expl_llen=self.expl_llen if self.expled else None,
1884 expl_vlen=self.expl_vlen if self.expled else None,
1885 expl_lenindef=self.expl_lenindef,
1889 class BitString(Obj):
1890 """``BIT STRING`` bit string type
1892 >>> BitString(b"hello world")
1893 BIT STRING 88 bits 68656c6c6f20776f726c64
1896 >>> b == b"hello world"
1901 >>> BitString("'0A3B5F291CD'H")
1902 BIT STRING 44 bits 0a3b5f291cd0
1903 >>> b = BitString("'010110000000'B")
1904 BIT STRING 12 bits 5800
1907 >>> b[0], b[1], b[2], b[3]
1908 (False, True, False, True)
1912 [False, True, False, True, True, False, False, False, False, False, False, False]
1916 class KeyUsage(BitString):
1918 ("digitalSignature", 0),
1919 ("nonRepudiation", 1),
1920 ("keyEncipherment", 2),
1923 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1924 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1926 ['nonRepudiation', 'keyEncipherment']
1928 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1932 Pay attention that BIT STRING can be encoded both in primitive
1933 and constructed forms. Decoder always checks constructed form tag
1934 additionally to specified primitive one. If BER decoding is
1935 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
1936 of DER restrictions.
1938 __slots__ = ("tag_constructed", "specs", "defined")
1939 tag_default = tag_encode(3)
1940 asn1_type_name = "BIT STRING"
1953 :param value: set the value. Either binary type, tuple of named
1954 values (if ``schema`` is specified in the class),
1955 string in ``'XXX...'B`` form, or
1956 :py:class:`pyderasn.BitString` object
1957 :param bytes impl: override default tag with ``IMPLICIT`` one
1958 :param bytes expl: override default tag with ``EXPLICIT`` one
1959 :param default: set default value. Type same as in ``value``
1960 :param bool optional: is object ``OPTIONAL`` in sequence
1962 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1963 specs = getattr(self, "schema", {}) if _specs is None else _specs
1964 self.specs = specs if isinstance(specs, dict) else dict(specs)
1965 self._value = None if value is None else self._value_sanitize(value)
1966 if default is not None:
1967 default = self._value_sanitize(default)
1968 self.default = self.__class__(
1974 self._value = default
1976 tag_klass, _, tag_num = tag_decode(self.tag)
1977 self.tag_constructed = tag_encode(
1979 form=TagFormConstructed,
1983 def _bits2octets(self, bits):
1984 if len(self.specs) > 0:
1985 bits = bits.rstrip("0")
1987 bits += "0" * ((8 - (bit_len % 8)) % 8)
1988 octets = bytearray(len(bits) // 8)
1989 for i in six_xrange(len(octets)):
1990 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1991 return bit_len, bytes(octets)
1993 def _value_sanitize(self, value):
1994 if issubclass(value.__class__, BitString):
1996 if isinstance(value, (string_types, binary_type)):
1998 isinstance(value, string_types) and
1999 value.startswith("'")
2001 if value.endswith("'B"):
2003 if not set(value) <= set(("0", "1")):
2004 raise ValueError("B's coding contains unacceptable chars")
2005 return self._bits2octets(value)
2006 elif value.endswith("'H"):
2010 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2013 raise InvalidValueType((self.__class__, string_types, binary_type))
2014 elif isinstance(value, binary_type):
2015 return (len(value) * 8, value)
2017 raise InvalidValueType((self.__class__, string_types, binary_type))
2018 if isinstance(value, tuple):
2021 isinstance(value[0], integer_types) and
2022 isinstance(value[1], binary_type)
2027 bit = self.specs.get(name)
2029 raise ObjUnknown("BitString value: %s" % name)
2032 return self._bits2octets("")
2034 return self._bits2octets("".join(
2035 ("1" if bit in bits else "0")
2036 for bit in six_xrange(max(bits) + 1)
2038 raise InvalidValueType((self.__class__, binary_type, string_types))
2042 return self._value is not None
2045 obj = self.__class__(_specs=self.specs)
2047 if value is not None:
2048 value = (value[0], value[1])
2051 obj._expl = self._expl
2052 obj.default = self.default
2053 obj.optional = self.optional
2054 obj.offset = self.offset
2055 obj.llen = self.llen
2056 obj.vlen = self.vlen
2060 self._assert_ready()
2061 for i in six_xrange(self._value[0]):
2066 self._assert_ready()
2067 return self._value[0]
2069 def __bytes__(self):
2070 self._assert_ready()
2071 return self._value[1]
2073 def __eq__(self, their):
2074 if isinstance(their, bytes):
2075 return self._value[1] == their
2076 if not issubclass(their.__class__, BitString):
2079 self._value == their._value and
2080 self.tag == their.tag and
2081 self._expl == their._expl
2086 return [name for name, bit in self.specs.items() if self[bit]]
2096 return self.__class__(
2098 impl=self.tag if impl is None else impl,
2099 expl=self._expl if expl is None else expl,
2100 default=self.default if default is None else default,
2101 optional=self.optional if optional is None else optional,
2105 def __getitem__(self, key):
2106 if isinstance(key, int):
2107 bit_len, octets = self._value
2111 byte2int(memoryview(octets)[key // 8:]) >>
2114 if isinstance(key, string_types):
2115 value = self.specs.get(key)
2117 raise ObjUnknown("BitString value: %s" % key)
2119 raise InvalidValueType((int, str))
2122 self._assert_ready()
2123 bit_len, octets = self._value
2126 len_encode(len(octets) + 1),
2127 int2byte((8 - bit_len % 8) % 8),
2131 def _decode_chunk(self, lv, offset, decode_path, ctx):
2133 l, llen, v = len_decode(lv)
2134 except DecodeError as err:
2135 raise err.__class__(
2137 klass=self.__class__,
2138 decode_path=decode_path,
2142 raise NotEnoughData(
2143 "encoded length is longer than data",
2144 klass=self.__class__,
2145 decode_path=decode_path,
2149 raise NotEnoughData(
2151 klass=self.__class__,
2152 decode_path=decode_path,
2155 pad_size = byte2int(v)
2156 if l == 1 and pad_size != 0:
2158 "invalid empty value",
2159 klass=self.__class__,
2160 decode_path=decode_path,
2166 klass=self.__class__,
2167 decode_path=decode_path,
2170 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2173 klass=self.__class__,
2174 decode_path=decode_path,
2177 v, tail = v[:l], v[l:]
2178 obj = self.__class__(
2179 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2182 default=self.default,
2183 optional=self.optional,
2185 _decoded=(offset, llen, l),
2189 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2191 t, tlen, lv = tag_strip(tlv)
2192 except DecodeError as err:
2193 raise err.__class__(
2195 klass=self.__class__,
2196 decode_path=decode_path,
2202 return self._decode_chunk(lv, offset, decode_path, ctx)
2203 if t == self.tag_constructed:
2204 if not ctx.get("bered", False):
2206 "unallowed BER constructed encoding",
2207 klass=self.__class__,
2208 decode_path=decode_path,
2215 l, llen, v = len_decode(lv)
2216 except LenIndefForm:
2217 llen, l, v = 1, 0, lv[1:]
2219 except DecodeError as err:
2220 raise err.__class__(
2222 klass=self.__class__,
2223 decode_path=decode_path,
2226 if l > 0 and l > len(v):
2227 raise NotEnoughData(
2228 "encoded length is longer than data",
2229 klass=self.__class__,
2230 decode_path=decode_path,
2233 if not lenindef and l == 0:
2234 raise NotEnoughData(
2236 klass=self.__class__,
2237 decode_path=decode_path,
2241 sub_offset = offset + tlen + llen
2245 if v[:EOC_LEN].tobytes() == EOC:
2252 "chunk out of bounds",
2253 klass=self.__class__,
2254 decode_path=len(chunks) - 1,
2255 offset=chunks[-1].offset,
2257 sub_decode_path = decode_path + (str(len(chunks)),)
2259 chunk, v_tail = BitString().decode(
2262 decode_path=sub_decode_path,
2268 "expected BitString encoded chunk",
2269 klass=self.__class__,
2270 decode_path=sub_decode_path,
2273 chunks.append(chunk)
2274 sub_offset += chunk.tlvlen
2275 vlen += chunk.tlvlen
2277 if len(chunks) == 0:
2280 klass=self.__class__,
2281 decode_path=decode_path,
2286 for chunk_i, chunk in enumerate(chunks[:-1]):
2287 if chunk.bit_len % 8 != 0:
2289 "BitString chunk is not multiple of 8 bit",
2290 klass=self.__class__,
2291 decode_path=decode_path + (str(chunk_i),),
2292 offset=chunk.offset,
2294 values.append(bytes(chunk))
2295 bit_len += chunk.bit_len
2296 chunk_last = chunks[-1]
2297 values.append(bytes(chunk_last))
2298 bit_len += chunk_last.bit_len
2299 obj = self.__class__(
2300 value=(bit_len, b"".join(values)),
2303 default=self.default,
2304 optional=self.optional,
2306 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2308 obj.lenindef = lenindef
2310 return obj, (v[EOC_LEN:] if lenindef else v)
2312 klass=self.__class__,
2313 decode_path=decode_path,
2318 return pp_console_row(next(self.pps()))
2320 def pps(self, decode_path=()):
2324 bit_len, blob = self._value
2325 value = "%d bits" % bit_len
2326 if len(self.specs) > 0:
2327 blob = tuple(self.named)
2329 asn1_type_name=self.asn1_type_name,
2330 obj_name=self.__class__.__name__,
2331 decode_path=decode_path,
2334 optional=self.optional,
2335 default=self == self.default,
2336 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2337 expl=None if self._expl is None else tag_decode(self._expl),
2342 expl_offset=self.expl_offset if self.expled else None,
2343 expl_tlen=self.expl_tlen if self.expled else None,
2344 expl_llen=self.expl_llen if self.expled else None,
2345 expl_vlen=self.expl_vlen if self.expled else None,
2346 expl_lenindef=self.expl_lenindef,
2347 lenindef=self.lenindef,
2350 defined_by, defined = self.defined or (None, None)
2351 if defined_by is not None:
2353 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2357 class OctetString(Obj):
2358 """``OCTET STRING`` binary string type
2360 >>> s = OctetString(b"hello world")
2361 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2362 >>> s == OctetString(b"hello world")
2367 >>> OctetString(b"hello", bounds=(4, 4))
2368 Traceback (most recent call last):
2369 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2370 >>> OctetString(b"hell", bounds=(4, 4))
2371 OCTET STRING 4 bytes 68656c6c
2375 Pay attention that OCTET STRING can be encoded both in primitive
2376 and constructed forms. Decoder always checks constructed form tag
2377 additionally to specified primitive one. If BER decoding is
2378 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2379 of DER restrictions.
2381 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2382 tag_default = tag_encode(4)
2383 asn1_type_name = "OCTET STRING"
2396 :param value: set the value. Either binary type, or
2397 :py:class:`pyderasn.OctetString` object
2398 :param bounds: set ``(MIN, MAX)`` value size constraint.
2399 (-inf, +inf) by default
2400 :param bytes impl: override default tag with ``IMPLICIT`` one
2401 :param bytes expl: override default tag with ``EXPLICIT`` one
2402 :param default: set default value. Type same as in ``value``
2403 :param bool optional: is object ``OPTIONAL`` in sequence
2405 super(OctetString, self).__init__(
2413 self._bound_min, self._bound_max = getattr(
2417 ) if bounds is None else bounds
2418 if value is not None:
2419 self._value = self._value_sanitize(value)
2420 if default is not None:
2421 default = self._value_sanitize(default)
2422 self.default = self.__class__(
2427 if self._value is None:
2428 self._value = default
2430 tag_klass, _, tag_num = tag_decode(self.tag)
2431 self.tag_constructed = tag_encode(
2433 form=TagFormConstructed,
2437 def _value_sanitize(self, value):
2438 if issubclass(value.__class__, OctetString):
2439 value = value._value
2440 elif isinstance(value, binary_type):
2443 raise InvalidValueType((self.__class__, bytes))
2444 if not self._bound_min <= len(value) <= self._bound_max:
2445 raise BoundsError(self._bound_min, len(value), self._bound_max)
2450 return self._value is not None
2453 obj = self.__class__()
2454 obj._value = self._value
2455 obj._bound_min = self._bound_min
2456 obj._bound_max = self._bound_max
2458 obj._expl = self._expl
2459 obj.default = self.default
2460 obj.optional = self.optional
2461 obj.offset = self.offset
2462 obj.llen = self.llen
2463 obj.vlen = self.vlen
2466 def __bytes__(self):
2467 self._assert_ready()
2470 def __eq__(self, their):
2471 if isinstance(their, binary_type):
2472 return self._value == their
2473 if not issubclass(their.__class__, OctetString):
2476 self._value == their._value and
2477 self.tag == their.tag and
2478 self._expl == their._expl
2481 def __lt__(self, their):
2482 return self._value < their._value
2493 return self.__class__(
2496 (self._bound_min, self._bound_max)
2497 if bounds is None else bounds
2499 impl=self.tag if impl is None else impl,
2500 expl=self._expl if expl is None else expl,
2501 default=self.default if default is None else default,
2502 optional=self.optional if optional is None else optional,
2506 self._assert_ready()
2509 len_encode(len(self._value)),
2513 def _decode_chunk(self, lv, offset, decode_path, ctx):
2515 l, llen, v = len_decode(lv)
2516 except DecodeError as err:
2517 raise err.__class__(
2519 klass=self.__class__,
2520 decode_path=decode_path,
2524 raise NotEnoughData(
2525 "encoded length is longer than data",
2526 klass=self.__class__,
2527 decode_path=decode_path,
2530 v, tail = v[:l], v[l:]
2532 obj = self.__class__(
2534 bounds=(self._bound_min, self._bound_max),
2537 default=self.default,
2538 optional=self.optional,
2539 _decoded=(offset, llen, l),
2541 except DecodeError as err:
2544 klass=self.__class__,
2545 decode_path=decode_path,
2548 except BoundsError as err:
2551 klass=self.__class__,
2552 decode_path=decode_path,
2557 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2559 t, tlen, lv = tag_strip(tlv)
2560 except DecodeError as err:
2561 raise err.__class__(
2563 klass=self.__class__,
2564 decode_path=decode_path,
2570 return self._decode_chunk(lv, offset, decode_path, ctx)
2571 if t == self.tag_constructed:
2572 if not ctx.get("bered", False):
2574 "unallowed BER constructed encoding",
2575 klass=self.__class__,
2576 decode_path=decode_path,
2583 l, llen, v = len_decode(lv)
2584 except LenIndefForm:
2585 llen, l, v = 1, 0, lv[1:]
2587 except DecodeError as err:
2588 raise err.__class__(
2590 klass=self.__class__,
2591 decode_path=decode_path,
2594 if l > 0 and l > len(v):
2595 raise NotEnoughData(
2596 "encoded length is longer than data",
2597 klass=self.__class__,
2598 decode_path=decode_path,
2601 if not lenindef and l == 0:
2602 raise NotEnoughData(
2604 klass=self.__class__,
2605 decode_path=decode_path,
2609 sub_offset = offset + tlen + llen
2613 if v[:EOC_LEN].tobytes() == EOC:
2620 "chunk out of bounds",
2621 klass=self.__class__,
2622 decode_path=len(chunks) - 1,
2623 offset=chunks[-1].offset,
2625 sub_decode_path = decode_path + (str(len(chunks)),)
2627 chunk, v_tail = OctetString().decode(
2630 decode_path=sub_decode_path,
2636 "expected OctetString encoded chunk",
2637 klass=self.__class__,
2638 decode_path=sub_decode_path,
2641 chunks.append(chunk)
2642 sub_offset += chunk.tlvlen
2643 vlen += chunk.tlvlen
2645 if len(chunks) == 0:
2648 klass=self.__class__,
2649 decode_path=decode_path,
2653 obj = self.__class__(
2654 value=b"".join(bytes(chunk) for chunk in chunks),
2655 bounds=(self._bound_min, self._bound_max),
2658 default=self.default,
2659 optional=self.optional,
2660 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2662 except DecodeError as err:
2665 klass=self.__class__,
2666 decode_path=decode_path,
2669 except BoundsError as err:
2672 klass=self.__class__,
2673 decode_path=decode_path,
2676 obj.lenindef = lenindef
2678 return obj, (v[EOC_LEN:] if lenindef else v)
2680 klass=self.__class__,
2681 decode_path=decode_path,
2686 return pp_console_row(next(self.pps()))
2688 def pps(self, decode_path=()):
2690 asn1_type_name=self.asn1_type_name,
2691 obj_name=self.__class__.__name__,
2692 decode_path=decode_path,
2693 value=("%d bytes" % len(self._value)) if self.ready else None,
2694 blob=self._value if self.ready else None,
2695 optional=self.optional,
2696 default=self == self.default,
2697 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2698 expl=None if self._expl is None else tag_decode(self._expl),
2703 expl_offset=self.expl_offset if self.expled else None,
2704 expl_tlen=self.expl_tlen if self.expled else None,
2705 expl_llen=self.expl_llen if self.expled else None,
2706 expl_vlen=self.expl_vlen if self.expled else None,
2707 expl_lenindef=self.expl_lenindef,
2708 lenindef=self.lenindef,
2711 defined_by, defined = self.defined or (None, None)
2712 if defined_by is not None:
2714 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2719 """``NULL`` null object
2727 tag_default = tag_encode(5)
2728 asn1_type_name = "NULL"
2732 value=None, # unused, but Sequence passes it
2739 :param bytes impl: override default tag with ``IMPLICIT`` one
2740 :param bytes expl: override default tag with ``EXPLICIT`` one
2741 :param bool optional: is object ``OPTIONAL`` in sequence
2743 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2751 obj = self.__class__()
2753 obj._expl = self._expl
2754 obj.default = self.default
2755 obj.optional = self.optional
2756 obj.offset = self.offset
2757 obj.llen = self.llen
2758 obj.vlen = self.vlen
2761 def __eq__(self, their):
2762 if not issubclass(their.__class__, Null):
2765 self.tag == their.tag and
2766 self._expl == their._expl
2776 return self.__class__(
2777 impl=self.tag if impl is None else impl,
2778 expl=self._expl if expl is None else expl,
2779 optional=self.optional if optional is None else optional,
2783 return self.tag + len_encode(0)
2785 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2787 t, _, lv = tag_strip(tlv)
2788 except DecodeError as err:
2789 raise err.__class__(
2791 klass=self.__class__,
2792 decode_path=decode_path,
2797 klass=self.__class__,
2798 decode_path=decode_path,
2804 l, _, v = len_decode(lv)
2805 except DecodeError as err:
2806 raise err.__class__(
2808 klass=self.__class__,
2809 decode_path=decode_path,
2813 raise InvalidLength(
2814 "Null must have zero length",
2815 klass=self.__class__,
2816 decode_path=decode_path,
2819 obj = self.__class__(
2822 optional=self.optional,
2823 _decoded=(offset, 1, 0),
2828 return pp_console_row(next(self.pps()))
2830 def pps(self, decode_path=()):
2832 asn1_type_name=self.asn1_type_name,
2833 obj_name=self.__class__.__name__,
2834 decode_path=decode_path,
2835 optional=self.optional,
2836 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2837 expl=None if self._expl is None else tag_decode(self._expl),
2842 expl_offset=self.expl_offset if self.expled else None,
2843 expl_tlen=self.expl_tlen if self.expled else None,
2844 expl_llen=self.expl_llen if self.expled else None,
2845 expl_vlen=self.expl_vlen if self.expled else None,
2846 expl_lenindef=self.expl_lenindef,
2850 class ObjectIdentifier(Obj):
2851 """``OBJECT IDENTIFIER`` OID type
2853 >>> oid = ObjectIdentifier((1, 2, 3))
2854 OBJECT IDENTIFIER 1.2.3
2855 >>> oid == ObjectIdentifier("1.2.3")
2861 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2862 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2864 >>> str(ObjectIdentifier((3, 1)))
2865 Traceback (most recent call last):
2866 pyderasn.InvalidOID: unacceptable first arc value
2868 __slots__ = ("defines",)
2869 tag_default = tag_encode(6)
2870 asn1_type_name = "OBJECT IDENTIFIER"
2883 :param value: set the value. Either tuples of integers,
2884 string of "."-concatenated integers, or
2885 :py:class:`pyderasn.ObjectIdentifier` object
2886 :param defines: sequence of tuples. Each tuple has two elements.
2887 First one is relative to current one decode
2888 path, aiming to the field defined by that OID.
2889 Read about relative path in
2890 :py:func:`pyderasn.abs_decode_path`. Second
2891 tuple element is ``{OID: pyderasn.Obj()}``
2892 dictionary, mapping between current OID value
2893 and structure applied to defined field.
2894 :ref:`Read about DEFINED BY <definedby>`
2895 :param bytes impl: override default tag with ``IMPLICIT`` one
2896 :param bytes expl: override default tag with ``EXPLICIT`` one
2897 :param default: set default value. Type same as in ``value``
2898 :param bool optional: is object ``OPTIONAL`` in sequence
2900 super(ObjectIdentifier, self).__init__(
2908 if value is not None:
2909 self._value = self._value_sanitize(value)
2910 if default is not None:
2911 default = self._value_sanitize(default)
2912 self.default = self.__class__(
2917 if self._value is None:
2918 self._value = default
2919 self.defines = defines
2921 def __add__(self, their):
2922 if isinstance(their, self.__class__):
2923 return self.__class__(self._value + their._value)
2924 if isinstance(their, tuple):
2925 return self.__class__(self._value + their)
2926 raise InvalidValueType((self.__class__, tuple))
2928 def _value_sanitize(self, value):
2929 if issubclass(value.__class__, ObjectIdentifier):
2931 if isinstance(value, string_types):
2933 value = tuple(int(arc) for arc in value.split("."))
2935 raise InvalidOID("unacceptable arcs values")
2936 if isinstance(value, tuple):
2938 raise InvalidOID("less than 2 arcs")
2939 first_arc = value[0]
2940 if first_arc in (0, 1):
2941 if not (0 <= value[1] <= 39):
2942 raise InvalidOID("second arc is too wide")
2943 elif first_arc == 2:
2946 raise InvalidOID("unacceptable first arc value")
2948 raise InvalidValueType((self.__class__, str, tuple))
2952 return self._value is not None
2955 obj = self.__class__()
2956 obj._value = self._value
2957 obj.defines = self.defines
2959 obj._expl = self._expl
2960 obj.default = self.default
2961 obj.optional = self.optional
2962 obj.offset = self.offset
2963 obj.llen = self.llen
2964 obj.vlen = self.vlen
2968 self._assert_ready()
2969 return iter(self._value)
2972 return ".".join(str(arc) for arc in self._value or ())
2975 self._assert_ready()
2978 bytes(self._expl or b"") +
2979 str(self._value).encode("ascii"),
2982 def __eq__(self, their):
2983 if isinstance(their, tuple):
2984 return self._value == their
2985 if not issubclass(their.__class__, ObjectIdentifier):
2988 self.tag == their.tag and
2989 self._expl == their._expl and
2990 self._value == their._value
2993 def __lt__(self, their):
2994 return self._value < their._value
3005 return self.__class__(
3007 defines=self.defines if defines is None else defines,
3008 impl=self.tag if impl is None else impl,
3009 expl=self._expl if expl is None else expl,
3010 default=self.default if default is None else default,
3011 optional=self.optional if optional is None else optional,
3015 self._assert_ready()
3017 first_value = value[1]
3018 first_arc = value[0]
3021 elif first_arc == 1:
3023 elif first_arc == 2:
3025 else: # pragma: no cover
3026 raise RuntimeError("invalid arc is stored")
3027 octets = [zero_ended_encode(first_value)]
3028 for arc in value[2:]:
3029 octets.append(zero_ended_encode(arc))
3030 v = b"".join(octets)
3031 return b"".join((self.tag, len_encode(len(v)), v))
3033 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3035 t, _, lv = tag_strip(tlv)
3036 except DecodeError as err:
3037 raise err.__class__(
3039 klass=self.__class__,
3040 decode_path=decode_path,
3045 klass=self.__class__,
3046 decode_path=decode_path,
3052 l, llen, v = len_decode(lv)
3053 except DecodeError as err:
3054 raise err.__class__(
3056 klass=self.__class__,
3057 decode_path=decode_path,
3061 raise NotEnoughData(
3062 "encoded length is longer than data",
3063 klass=self.__class__,
3064 decode_path=decode_path,
3068 raise NotEnoughData(
3070 klass=self.__class__,
3071 decode_path=decode_path,
3074 v, tail = v[:l], v[l:]
3080 octet = indexbytes(v, i)
3081 arc = (arc << 7) | (octet & 0x7F)
3082 if octet & 0x80 == 0:
3090 klass=self.__class__,
3091 decode_path=decode_path,
3095 second_arc = arcs[0]
3096 if 0 <= second_arc <= 39:
3098 elif 40 <= second_arc <= 79:
3104 obj = self.__class__(
3105 value=tuple([first_arc, second_arc] + arcs[1:]),
3108 default=self.default,
3109 optional=self.optional,
3110 _decoded=(offset, llen, l),
3115 return pp_console_row(next(self.pps()))
3117 def pps(self, decode_path=()):
3119 asn1_type_name=self.asn1_type_name,
3120 obj_name=self.__class__.__name__,
3121 decode_path=decode_path,
3122 value=str(self) if self.ready else None,
3123 optional=self.optional,
3124 default=self == self.default,
3125 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3126 expl=None if self._expl is None else tag_decode(self._expl),
3131 expl_offset=self.expl_offset if self.expled else None,
3132 expl_tlen=self.expl_tlen if self.expled else None,
3133 expl_llen=self.expl_llen if self.expled else None,
3134 expl_vlen=self.expl_vlen if self.expled else None,
3135 expl_lenindef=self.expl_lenindef,
3139 class Enumerated(Integer):
3140 """``ENUMERATED`` integer type
3142 This type is identical to :py:class:`pyderasn.Integer`, but requires
3143 schema to be specified and does not accept values missing from it.
3146 tag_default = tag_encode(10)
3147 asn1_type_name = "ENUMERATED"
3158 bounds=None, # dummy argument, workability for Integer.decode
3160 super(Enumerated, self).__init__(
3169 if len(self.specs) == 0:
3170 raise ValueError("schema must be specified")
3172 def _value_sanitize(self, value):
3173 if isinstance(value, self.__class__):
3174 value = value._value
3175 elif isinstance(value, integer_types):
3176 if value not in list(self.specs.values()):
3178 "unknown integer value: %s" % value,
3179 klass=self.__class__,
3181 elif isinstance(value, string_types):
3182 value = self.specs.get(value)
3184 raise ObjUnknown("integer value: %s" % value)
3186 raise InvalidValueType((self.__class__, int, str))
3190 obj = self.__class__(_specs=self.specs)
3191 obj._value = self._value
3192 obj._bound_min = self._bound_min
3193 obj._bound_max = self._bound_max
3195 obj._expl = self._expl
3196 obj.default = self.default
3197 obj.optional = self.optional
3198 obj.offset = self.offset
3199 obj.llen = self.llen
3200 obj.vlen = self.vlen
3212 return self.__class__(
3214 impl=self.tag if impl is None else impl,
3215 expl=self._expl if expl is None else expl,
3216 default=self.default if default is None else default,
3217 optional=self.optional if optional is None else optional,
3222 class CommonString(OctetString):
3223 """Common class for all strings
3225 Everything resembles :py:class:`pyderasn.OctetString`, except
3226 ability to deal with unicode text strings.
3228 >>> hexenc("привет мир".encode("utf-8"))
3229 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3230 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3232 >>> s = UTF8String("привет мир")
3233 UTF8String UTF8String привет мир
3235 'привет мир'
3236 >>> hexenc(bytes(s))
3237 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3239 >>> PrintableString("привет мир")
3240 Traceback (most recent call last):
3241 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3243 >>> BMPString("ада", bounds=(2, 2))
3244 Traceback (most recent call last):
3245 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3246 >>> s = BMPString("ад", bounds=(2, 2))
3249 >>> hexenc(bytes(s))
3257 * - :py:class:`pyderasn.UTF8String`
3259 * - :py:class:`pyderasn.NumericString`
3261 * - :py:class:`pyderasn.PrintableString`
3263 * - :py:class:`pyderasn.TeletexString`
3265 * - :py:class:`pyderasn.T61String`
3267 * - :py:class:`pyderasn.VideotexString`
3269 * - :py:class:`pyderasn.IA5String`
3271 * - :py:class:`pyderasn.GraphicString`
3273 * - :py:class:`pyderasn.VisibleString`
3275 * - :py:class:`pyderasn.ISO646String`
3277 * - :py:class:`pyderasn.GeneralString`
3279 * - :py:class:`pyderasn.UniversalString`
3281 * - :py:class:`pyderasn.BMPString`
3284 __slots__ = ("encoding",)
3286 def _value_sanitize(self, value):
3288 value_decoded = None
3289 if isinstance(value, self.__class__):
3290 value_raw = value._value
3291 elif isinstance(value, text_type):
3292 value_decoded = value
3293 elif isinstance(value, binary_type):
3296 raise InvalidValueType((self.__class__, text_type, binary_type))
3299 value_decoded.encode(self.encoding)
3300 if value_raw is None else value_raw
3303 value_raw.decode(self.encoding)
3304 if value_decoded is None else value_decoded
3306 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3307 raise DecodeError(str(err))
3308 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3316 def __eq__(self, their):
3317 if isinstance(their, binary_type):
3318 return self._value == their
3319 if isinstance(their, text_type):
3320 return self._value == their.encode(self.encoding)
3321 if not isinstance(their, self.__class__):
3324 self._value == their._value and
3325 self.tag == their.tag and
3326 self._expl == their._expl
3329 def __unicode__(self):
3331 return self._value.decode(self.encoding)
3332 return text_type(self._value)
3335 return pp_console_row(next(self.pps(no_unicode=PY2)))
3337 def pps(self, decode_path=(), no_unicode=False):
3340 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3342 asn1_type_name=self.asn1_type_name,
3343 obj_name=self.__class__.__name__,
3344 decode_path=decode_path,
3346 optional=self.optional,
3347 default=self == self.default,
3348 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3349 expl=None if self._expl is None else tag_decode(self._expl),
3354 expl_offset=self.expl_offset if self.expled else None,
3355 expl_tlen=self.expl_tlen if self.expled else None,
3356 expl_llen=self.expl_llen if self.expled else None,
3357 expl_vlen=self.expl_vlen if self.expled else None,
3358 expl_lenindef=self.expl_lenindef,
3362 class UTF8String(CommonString):
3364 tag_default = tag_encode(12)
3366 asn1_type_name = "UTF8String"
3369 class NumericString(CommonString):
3372 Its value is properly sanitized: only ASCII digits can be stored.
3375 tag_default = tag_encode(18)
3377 asn1_type_name = "NumericString"
3378 allowable_chars = set(digits.encode("ascii"))
3380 def _value_sanitize(self, value):
3381 value = super(NumericString, self)._value_sanitize(value)
3382 if not set(value) <= self.allowable_chars:
3383 raise DecodeError("non-numeric value")
3387 class PrintableString(CommonString):
3389 tag_default = tag_encode(19)
3391 asn1_type_name = "PrintableString"
3394 class TeletexString(CommonString):
3396 tag_default = tag_encode(20)
3398 asn1_type_name = "TeletexString"
3401 class T61String(TeletexString):
3403 asn1_type_name = "T61String"
3406 class VideotexString(CommonString):
3408 tag_default = tag_encode(21)
3409 encoding = "iso-8859-1"
3410 asn1_type_name = "VideotexString"
3413 class IA5String(CommonString):
3415 tag_default = tag_encode(22)
3417 asn1_type_name = "IA5"
3420 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3421 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3422 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3425 class UTCTime(CommonString):
3426 """``UTCTime`` datetime type
3428 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3429 UTCTime UTCTime 2017-09-30T22:07:50
3435 datetime.datetime(2017, 9, 30, 22, 7, 50)
3436 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3437 datetime.datetime(1957, 9, 30, 22, 7, 50)
3440 tag_default = tag_encode(23)
3442 asn1_type_name = "UTCTime"
3444 fmt = "%y%m%d%H%M%SZ"
3454 bounds=None, # dummy argument, workability for OctetString.decode
3457 :param value: set the value. Either datetime type, or
3458 :py:class:`pyderasn.UTCTime` object
3459 :param bytes impl: override default tag with ``IMPLICIT`` one
3460 :param bytes expl: override default tag with ``EXPLICIT`` one
3461 :param default: set default value. Type same as in ``value``
3462 :param bool optional: is object ``OPTIONAL`` in sequence
3464 super(UTCTime, self).__init__(
3472 if value is not None:
3473 self._value = self._value_sanitize(value)
3474 if default is not None:
3475 default = self._value_sanitize(default)
3476 self.default = self.__class__(
3481 if self._value is None:
3482 self._value = default
3484 def _value_sanitize(self, value):
3485 if isinstance(value, self.__class__):
3487 if isinstance(value, datetime):
3488 return value.strftime(self.fmt).encode("ascii")
3489 if isinstance(value, binary_type):
3490 value_decoded = value.decode("ascii")
3491 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3493 datetime.strptime(value_decoded, self.fmt)
3495 raise DecodeError("invalid UTCTime format")
3498 raise DecodeError("invalid UTCTime length")
3499 raise InvalidValueType((self.__class__, datetime))
3501 def __eq__(self, their):
3502 if isinstance(their, binary_type):
3503 return self._value == their
3504 if isinstance(their, datetime):
3505 return self.todatetime() == their
3506 if not isinstance(their, self.__class__):
3509 self._value == their._value and
3510 self.tag == their.tag and
3511 self._expl == their._expl
3514 def todatetime(self):
3515 """Convert to datetime
3519 Pay attention that UTCTime can not hold full year, so all years
3520 having < 50 years are treated as 20xx, 19xx otherwise, according
3521 to X.509 recomendation.
3523 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3524 year = value.year % 100
3526 year=(2000 + year) if year < 50 else (1900 + year),
3530 minute=value.minute,
3531 second=value.second,
3535 return pp_console_row(next(self.pps()))
3537 def pps(self, decode_path=()):
3539 asn1_type_name=self.asn1_type_name,
3540 obj_name=self.__class__.__name__,
3541 decode_path=decode_path,
3542 value=self.todatetime().isoformat() if self.ready else None,
3543 optional=self.optional,
3544 default=self == self.default,
3545 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3546 expl=None if self._expl is None else tag_decode(self._expl),
3551 expl_offset=self.expl_offset if self.expled else None,
3552 expl_tlen=self.expl_tlen if self.expled else None,
3553 expl_llen=self.expl_llen if self.expled else None,
3554 expl_vlen=self.expl_vlen if self.expled else None,
3555 expl_lenindef=self.expl_lenindef,
3559 class GeneralizedTime(UTCTime):
3560 """``GeneralizedTime`` datetime type
3562 This type is similar to :py:class:`pyderasn.UTCTime`.
3564 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3565 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3567 '20170930220750.000123Z'
3568 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3569 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3572 tag_default = tag_encode(24)
3573 asn1_type_name = "GeneralizedTime"
3575 fmt = "%Y%m%d%H%M%SZ"
3576 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3578 def _value_sanitize(self, value):
3579 if isinstance(value, self.__class__):
3581 if isinstance(value, datetime):
3582 return value.strftime(
3583 self.fmt_ms if value.microsecond > 0 else self.fmt
3585 if isinstance(value, binary_type):
3586 value_decoded = value.decode("ascii")
3587 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3589 datetime.strptime(value_decoded, self.fmt)
3592 "invalid GeneralizedTime (without ms) format",
3595 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3597 datetime.strptime(value_decoded, self.fmt_ms)
3600 "invalid GeneralizedTime (with ms) format",
3605 "invalid GeneralizedTime length",
3606 klass=self.__class__,
3608 raise InvalidValueType((self.__class__, datetime))
3610 def todatetime(self):
3611 value = self._value.decode("ascii")
3612 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3613 return datetime.strptime(value, self.fmt)
3614 return datetime.strptime(value, self.fmt_ms)
3617 class GraphicString(CommonString):
3619 tag_default = tag_encode(25)
3620 encoding = "iso-8859-1"
3621 asn1_type_name = "GraphicString"
3624 class VisibleString(CommonString):
3626 tag_default = tag_encode(26)
3628 asn1_type_name = "VisibleString"
3631 class ISO646String(VisibleString):
3633 asn1_type_name = "ISO646String"
3636 class GeneralString(CommonString):
3638 tag_default = tag_encode(27)
3639 encoding = "iso-8859-1"
3640 asn1_type_name = "GeneralString"
3643 class UniversalString(CommonString):
3645 tag_default = tag_encode(28)
3646 encoding = "utf-32-be"
3647 asn1_type_name = "UniversalString"
3650 class BMPString(CommonString):
3652 tag_default = tag_encode(30)
3653 encoding = "utf-16-be"
3654 asn1_type_name = "BMPString"
3658 """``CHOICE`` special type
3662 class GeneralName(Choice):
3664 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3665 ("dNSName", IA5String(impl=tag_ctxp(2))),
3668 >>> gn = GeneralName()
3670 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3671 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3672 >>> gn["dNSName"] = IA5String("bar.baz")
3673 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3674 >>> gn["rfc822Name"]
3677 [2] IA5String IA5 bar.baz
3680 >>> gn.value == gn["dNSName"]
3683 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3685 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3686 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3688 __slots__ = ("specs",)
3690 asn1_type_name = "CHOICE"
3703 :param value: set the value. Either ``(choice, value)`` tuple, or
3704 :py:class:`pyderasn.Choice` object
3705 :param bytes impl: can not be set, do **not** use it
3706 :param bytes expl: override default tag with ``EXPLICIT`` one
3707 :param default: set default value. Type same as in ``value``
3708 :param bool optional: is object ``OPTIONAL`` in sequence
3710 if impl is not None:
3711 raise ValueError("no implicit tag allowed for CHOICE")
3712 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3714 schema = getattr(self, "schema", ())
3715 if len(schema) == 0:
3716 raise ValueError("schema must be specified")
3718 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3721 if value is not None:
3722 self._value = self._value_sanitize(value)
3723 if default is not None:
3724 default_value = self._value_sanitize(default)
3725 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3726 default_obj.specs = self.specs
3727 default_obj._value = default_value
3728 self.default = default_obj
3730 self._value = default_obj.copy()._value
3732 def _value_sanitize(self, value):
3733 if isinstance(value, self.__class__):
3735 if isinstance(value, tuple) and len(value) == 2:
3737 spec = self.specs.get(choice)
3739 raise ObjUnknown(choice)
3740 if not isinstance(obj, spec.__class__):
3741 raise InvalidValueType((spec,))
3742 return (choice, spec(obj))
3743 raise InvalidValueType((self.__class__, tuple))
3747 return self._value is not None and self._value[1].ready
3750 obj = self.__class__(schema=self.specs)
3751 obj._expl = self._expl
3752 obj.default = self.default
3753 obj.optional = self.optional
3754 obj.offset = self.offset
3755 obj.llen = self.llen
3756 obj.vlen = self.vlen
3758 if value is not None:
3759 obj._value = (value[0], value[1].copy())
3762 def __eq__(self, their):
3763 if isinstance(their, tuple) and len(their) == 2:
3764 return self._value == their
3765 if not isinstance(their, self.__class__):
3768 self.specs == their.specs and
3769 self._value == their._value
3779 return self.__class__(
3782 expl=self._expl if expl is None else expl,
3783 default=self.default if default is None else default,
3784 optional=self.optional if optional is None else optional,
3789 self._assert_ready()
3790 return self._value[0]
3794 self._assert_ready()
3795 return self._value[1]
3797 def __getitem__(self, key):
3798 if key not in self.specs:
3799 raise ObjUnknown(key)
3800 if self._value is None:
3802 choice, value = self._value
3807 def __setitem__(self, key, value):
3808 spec = self.specs.get(key)
3810 raise ObjUnknown(key)
3811 if not isinstance(value, spec.__class__):
3812 raise InvalidValueType((spec.__class__,))
3813 self._value = (key, spec(value))
3821 return self._value[1].decoded if self.ready else False
3824 self._assert_ready()
3825 return self._value[1].encode()
3827 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3828 for choice, spec in self.specs.items():
3829 sub_decode_path = decode_path + (choice,)
3835 decode_path=sub_decode_path,
3844 klass=self.__class__,
3845 decode_path=decode_path,
3850 value, tail = spec.decode(
3854 decode_path=sub_decode_path,
3857 obj = self.__class__(
3860 default=self.default,
3861 optional=self.optional,
3862 _decoded=(offset, 0, value.tlvlen),
3864 obj._value = (choice, value)
3868 value = pp_console_row(next(self.pps()))
3870 value = "%s[%r]" % (value, self.value)
3873 def pps(self, decode_path=()):
3875 asn1_type_name=self.asn1_type_name,
3876 obj_name=self.__class__.__name__,
3877 decode_path=decode_path,
3878 value=self.choice if self.ready else None,
3879 optional=self.optional,
3880 default=self == self.default,
3881 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3882 expl=None if self._expl is None else tag_decode(self._expl),
3887 expl_lenindef=self.expl_lenindef,
3890 yield self.value.pps(decode_path=decode_path + (self.choice,))
3893 class PrimitiveTypes(Choice):
3894 """Predefined ``CHOICE`` for all generic primitive types
3896 It could be useful for general decoding of some unspecified values:
3898 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3899 OCTET STRING 3 bytes 666f6f
3900 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3904 schema = tuple((klass.__name__, klass()) for klass in (
3929 """``ANY`` special type
3931 >>> Any(Integer(-123))
3933 >>> a = Any(OctetString(b"hello world").encode())
3934 ANY 040b68656c6c6f20776f726c64
3935 >>> hexenc(bytes(a))
3936 b'0x040x0bhello world'
3938 __slots__ = ("defined",)
3939 tag_default = tag_encode(0)
3940 asn1_type_name = "ANY"
3950 :param value: set the value. Either any kind of pyderasn's
3951 **ready** object, or bytes. Pay attention that
3952 **no** validation is performed is raw binary value
3954 :param bytes expl: override default tag with ``EXPLICIT`` one
3955 :param bool optional: is object ``OPTIONAL`` in sequence
3957 super(Any, self).__init__(None, expl, None, optional, _decoded)
3958 self._value = None if value is None else self._value_sanitize(value)
3961 def _value_sanitize(self, value):
3962 if isinstance(value, self.__class__):
3964 if isinstance(value, Obj):
3965 return value.encode()
3966 if isinstance(value, binary_type):
3968 raise InvalidValueType((self.__class__, Obj, binary_type))
3972 return self._value is not None
3975 obj = self.__class__()
3976 obj._value = self._value
3978 obj._expl = self._expl
3979 obj.optional = self.optional
3980 obj.offset = self.offset
3981 obj.llen = self.llen
3982 obj.vlen = self.vlen
3985 def __eq__(self, their):
3986 if isinstance(their, binary_type):
3987 return self._value == their
3988 if issubclass(their.__class__, Any):
3989 return self._value == their._value
3998 return self.__class__(
4000 expl=self._expl if expl is None else expl,
4001 optional=self.optional if optional is None else optional,
4004 def __bytes__(self):
4005 self._assert_ready()
4013 self._assert_ready()
4016 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4018 t, tlen, lv = tag_strip(tlv)
4019 except DecodeError as err:
4020 raise err.__class__(
4022 klass=self.__class__,
4023 decode_path=decode_path,
4027 l, llen, v = len_decode(lv)
4028 except LenIndefForm as err:
4029 if not ctx.get("bered", False):
4030 raise err.__class__(
4032 klass=self.__class__,
4033 decode_path=decode_path,
4036 llen, vlen, v = 1, 0, lv[1:]
4037 sub_offset = offset + tlen + llen
4040 if v[:EOC_LEN].tobytes() == EOC:
4041 tlvlen = tlen + llen + vlen + EOC_LEN
4042 obj = self.__class__(
4043 value=tlv[:tlvlen].tobytes(),
4045 optional=self.optional,
4046 _decoded=(offset, 0, tlvlen),
4050 return obj, v[EOC_LEN:]
4052 chunk, v = Any().decode(
4055 decode_path=decode_path + (str(chunk_i),),
4059 vlen += chunk.tlvlen
4060 sub_offset += chunk.tlvlen
4062 except DecodeError as err:
4063 raise err.__class__(
4065 klass=self.__class__,
4066 decode_path=decode_path,
4070 raise NotEnoughData(
4071 "encoded length is longer than data",
4072 klass=self.__class__,
4073 decode_path=decode_path,
4076 tlvlen = tlen + llen + l
4077 v, tail = tlv[:tlvlen], v[l:]
4078 obj = self.__class__(
4081 optional=self.optional,
4082 _decoded=(offset, 0, tlvlen),
4088 return pp_console_row(next(self.pps()))
4090 def pps(self, decode_path=()):
4092 asn1_type_name=self.asn1_type_name,
4093 obj_name=self.__class__.__name__,
4094 decode_path=decode_path,
4095 blob=self._value if self.ready else None,
4096 optional=self.optional,
4097 default=self == self.default,
4098 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4099 expl=None if self._expl is None else tag_decode(self._expl),
4104 expl_offset=self.expl_offset if self.expled else None,
4105 expl_tlen=self.expl_tlen if self.expled else None,
4106 expl_llen=self.expl_llen if self.expled else None,
4107 expl_vlen=self.expl_vlen if self.expled else None,
4108 expl_lenindef=self.expl_lenindef,
4109 lenindef=self.lenindef,
4111 defined_by, defined = self.defined or (None, None)
4112 if defined_by is not None:
4114 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4118 ########################################################################
4119 # ASN.1 constructed types
4120 ########################################################################
4122 def get_def_by_path(defines_by_path, sub_decode_path):
4123 """Get define by decode path
4125 for path, define in defines_by_path:
4126 if len(path) != len(sub_decode_path):
4128 for p1, p2 in zip(path, sub_decode_path):
4129 if (p1 != any) and (p1 != p2):
4135 def abs_decode_path(decode_path, rel_path):
4136 """Create an absolute decode path from current and relative ones
4138 :param decode_path: current decode path, starting point.
4140 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4141 If first tuple's element is "/", then treat it as
4142 an absolute path, ignoring ``decode_path`` as
4143 starting point. Also this tuple can contain ".."
4144 elements, stripping the leading element from
4147 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4148 ("foo", "bar", "baz", "whatever")
4149 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4151 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4154 if rel_path[0] == "/":
4156 if rel_path[0] == "..":
4157 return abs_decode_path(decode_path[:-1], rel_path[1:])
4158 return decode_path + rel_path
4161 class Sequence(Obj):
4162 """``SEQUENCE`` structure type
4164 You have to make specification of sequence::
4166 class Extension(Sequence):
4168 ("extnID", ObjectIdentifier()),
4169 ("critical", Boolean(default=False)),
4170 ("extnValue", OctetString()),
4173 Then, you can work with it as with dictionary.
4175 >>> ext = Extension()
4176 >>> Extension().specs
4178 ('extnID', OBJECT IDENTIFIER),
4179 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4180 ('extnValue', OCTET STRING),
4182 >>> ext["extnID"] = "1.2.3"
4183 Traceback (most recent call last):
4184 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4185 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4187 You can determine if sequence is ready to be encoded:
4192 Traceback (most recent call last):
4193 pyderasn.ObjNotReady: object is not ready: extnValue
4194 >>> ext["extnValue"] = OctetString(b"foobar")
4198 Value you want to assign, must have the same **type** as in
4199 corresponding specification, but it can have different tags,
4200 optional/default attributes -- they will be taken from specification
4203 class TBSCertificate(Sequence):
4205 ("version", Version(expl=tag_ctxc(0), default="v1")),
4208 >>> tbs = TBSCertificate()
4209 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4211 Assign ``None`` to remove value from sequence.
4213 You can set values in Sequence during its initialization:
4215 >>> AlgorithmIdentifier((
4216 ("algorithm", ObjectIdentifier("1.2.3")),
4217 ("parameters", Any(Null()))
4219 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4221 You can determine if value exists/set in the sequence and take its value:
4223 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4226 OBJECT IDENTIFIER 1.2.3
4228 But pay attention that if value has default, then it won't be (not
4229 in) in the sequence (because ``DEFAULT`` must not be encoded in
4230 DER), but you can read its value:
4232 >>> "critical" in ext, ext["critical"]
4233 (False, BOOLEAN False)
4234 >>> ext["critical"] = Boolean(True)
4235 >>> "critical" in ext, ext["critical"]
4236 (True, BOOLEAN True)
4238 All defaulted values are always optional.
4240 .. _strict_default_existence_ctx:
4244 When decoded DER contains defaulted value inside, then
4245 technically this is not valid DER encoding. But we allow and pass
4246 it **by default**. Of course reencoding of that kind of DER will
4247 result in different binary representation (validly without
4248 defaulted value inside). You can enable strict defaulted values
4249 existence validation by setting ``"strict_default_existence":
4250 True`` :ref:`context <ctx>` option -- decoding process will raise
4251 an exception if defaulted value is met.
4253 Two sequences are equal if they have equal specification (schema),
4254 implicit/explicit tagging and the same values.
4256 __slots__ = ("specs",)
4257 tag_default = tag_encode(form=TagFormConstructed, num=16)
4258 asn1_type_name = "SEQUENCE"
4270 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4272 schema = getattr(self, "schema", ())
4274 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4277 if value is not None:
4278 if issubclass(value.__class__, Sequence):
4279 self._value = value._value
4280 elif hasattr(value, "__iter__"):
4281 for seq_key, seq_value in value:
4282 self[seq_key] = seq_value
4284 raise InvalidValueType((Sequence,))
4285 if default is not None:
4286 if not issubclass(default.__class__, Sequence):
4287 raise InvalidValueType((Sequence,))
4288 default_value = default._value
4289 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4290 default_obj.specs = self.specs
4291 default_obj._value = default_value
4292 self.default = default_obj
4294 self._value = default_obj.copy()._value
4298 for name, spec in self.specs.items():
4299 value = self._value.get(name)
4310 obj = self.__class__(schema=self.specs)
4312 obj._expl = self._expl
4313 obj.default = self.default
4314 obj.optional = self.optional
4315 obj.offset = self.offset
4316 obj.llen = self.llen
4317 obj.vlen = self.vlen
4318 obj._value = {k: v.copy() for k, v in self._value.items()}
4321 def __eq__(self, their):
4322 if not isinstance(their, self.__class__):
4325 self.specs == their.specs and
4326 self.tag == their.tag and
4327 self._expl == their._expl and
4328 self._value == their._value
4339 return self.__class__(
4342 impl=self.tag if impl is None else impl,
4343 expl=self._expl if expl is None else expl,
4344 default=self.default if default is None else default,
4345 optional=self.optional if optional is None else optional,
4348 def __contains__(self, key):
4349 return key in self._value
4351 def __setitem__(self, key, value):
4352 spec = self.specs.get(key)
4354 raise ObjUnknown(key)
4356 self._value.pop(key, None)
4358 if not isinstance(value, spec.__class__):
4359 raise InvalidValueType((spec.__class__,))
4360 value = spec(value=value)
4361 if spec.default is not None and value == spec.default:
4362 self._value.pop(key, None)
4364 self._value[key] = value
4366 def __getitem__(self, key):
4367 value = self._value.get(key)
4368 if value is not None:
4370 spec = self.specs.get(key)
4372 raise ObjUnknown(key)
4373 if spec.default is not None:
4377 def _encoded_values(self):
4379 for name, spec in self.specs.items():
4380 value = self._value.get(name)
4384 raise ObjNotReady(name)
4385 raws.append(value.encode())
4389 v = b"".join(self._encoded_values())
4390 return b"".join((self.tag, len_encode(len(v)), v))
4392 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4394 t, tlen, lv = tag_strip(tlv)
4395 except DecodeError as err:
4396 raise err.__class__(
4398 klass=self.__class__,
4399 decode_path=decode_path,
4404 klass=self.__class__,
4405 decode_path=decode_path,
4412 l, llen, v = len_decode(lv)
4413 except LenIndefForm as err:
4414 if not ctx.get("bered", False):
4415 raise err.__class__(
4417 klass=self.__class__,
4418 decode_path=decode_path,
4421 l, llen, v = 0, 1, lv[1:]
4423 except DecodeError as err:
4424 raise err.__class__(
4426 klass=self.__class__,
4427 decode_path=decode_path,
4431 raise NotEnoughData(
4432 "encoded length is longer than data",
4433 klass=self.__class__,
4434 decode_path=decode_path,
4438 v, tail = v[:l], v[l:]
4440 sub_offset = offset + tlen + llen
4442 for name, spec in self.specs.items():
4443 if spec.optional and (
4444 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4448 sub_decode_path = decode_path + (name,)
4450 value, v_tail = spec.decode(
4454 decode_path=sub_decode_path,
4462 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4463 if defined is not None:
4464 defined_by, defined_spec = defined
4465 if issubclass(value.__class__, SequenceOf):
4466 for i, _value in enumerate(value):
4467 sub_sub_decode_path = sub_decode_path + (
4469 DecodePathDefBy(defined_by),
4471 defined_value, defined_tail = defined_spec.decode(
4472 memoryview(bytes(_value)),
4474 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4475 if value.expled else (value.tlen + value.llen)
4478 decode_path=sub_sub_decode_path,
4481 if len(defined_tail) > 0:
4484 klass=self.__class__,
4485 decode_path=sub_sub_decode_path,
4488 _value.defined = (defined_by, defined_value)
4490 defined_value, defined_tail = defined_spec.decode(
4491 memoryview(bytes(value)),
4493 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4494 if value.expled else (value.tlen + value.llen)
4497 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4500 if len(defined_tail) > 0:
4503 klass=self.__class__,
4504 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4507 value.defined = (defined_by, defined_value)
4509 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4511 sub_offset += value_len
4513 if spec.default is not None and value == spec.default:
4514 if ctx.get("strict_default_existence", False):
4516 "DEFAULT value met",
4517 klass=self.__class__,
4518 decode_path=sub_decode_path,
4523 values[name] = value
4525 spec_defines = getattr(spec, "defines", ())
4526 if len(spec_defines) == 0:
4527 defines_by_path = ctx.get("defines_by_path", ())
4528 if len(defines_by_path) > 0:
4529 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4530 if spec_defines is not None and len(spec_defines) > 0:
4531 for rel_path, schema in spec_defines:
4532 defined = schema.get(value, None)
4533 if defined is not None:
4534 ctx.setdefault("defines", []).append((
4535 abs_decode_path(sub_decode_path[:-1], rel_path),
4539 if v[:EOC_LEN].tobytes() != EOC:
4542 klass=self.__class__,
4543 decode_path=decode_path,
4551 klass=self.__class__,
4552 decode_path=decode_path,
4555 obj = self.__class__(
4559 default=self.default,
4560 optional=self.optional,
4561 _decoded=(offset, llen, vlen),
4564 obj.lenindef = lenindef
4568 value = pp_console_row(next(self.pps()))
4570 for name in self.specs:
4571 _value = self._value.get(name)
4574 cols.append(repr(_value))
4575 return "%s[%s]" % (value, ", ".join(cols))
4577 def pps(self, decode_path=()):
4579 asn1_type_name=self.asn1_type_name,
4580 obj_name=self.__class__.__name__,
4581 decode_path=decode_path,
4582 optional=self.optional,
4583 default=self == self.default,
4584 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4585 expl=None if self._expl is None else tag_decode(self._expl),
4590 expl_offset=self.expl_offset if self.expled else None,
4591 expl_tlen=self.expl_tlen if self.expled else None,
4592 expl_llen=self.expl_llen if self.expled else None,
4593 expl_vlen=self.expl_vlen if self.expled else None,
4594 expl_lenindef=self.expl_lenindef,
4595 lenindef=self.lenindef,
4597 for name in self.specs:
4598 value = self._value.get(name)
4601 yield value.pps(decode_path=decode_path + (name,))
4604 class Set(Sequence):
4605 """``SET`` structure type
4607 Its usage is identical to :py:class:`pyderasn.Sequence`.
4610 tag_default = tag_encode(form=TagFormConstructed, num=17)
4611 asn1_type_name = "SET"
4614 raws = self._encoded_values()
4617 return b"".join((self.tag, len_encode(len(v)), v))
4619 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4621 t, tlen, lv = tag_strip(tlv)
4622 except DecodeError as err:
4623 raise err.__class__(
4625 klass=self.__class__,
4626 decode_path=decode_path,
4631 klass=self.__class__,
4632 decode_path=decode_path,
4639 l, llen, v = len_decode(lv)
4640 except LenIndefForm as err:
4641 if not ctx.get("bered", False):
4642 raise err.__class__(
4644 klass=self.__class__,
4645 decode_path=decode_path,
4648 l, llen, v = 0, 1, lv[1:]
4650 except DecodeError as err:
4651 raise err.__class__(
4653 klass=self.__class__,
4654 decode_path=decode_path,
4658 raise NotEnoughData(
4659 "encoded length is longer than data",
4660 klass=self.__class__,
4664 v, tail = v[:l], v[l:]
4666 sub_offset = offset + tlen + llen
4668 specs_items = self.specs.items
4670 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4672 for name, spec in specs_items():
4673 sub_decode_path = decode_path + (name,)
4679 decode_path=sub_decode_path,
4688 klass=self.__class__,
4689 decode_path=decode_path,
4692 value, v_tail = spec.decode(
4696 decode_path=sub_decode_path,
4699 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4700 sub_offset += value_len
4703 if spec.default is None or value != spec.default: # pragma: no cover
4704 # SeqMixing.test_encoded_default_accepted covers that place
4705 values[name] = value
4706 obj = self.__class__(
4710 default=self.default,
4711 optional=self.optional,
4712 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4717 "not all values are ready",
4718 klass=self.__class__,
4719 decode_path=decode_path,
4722 obj.lenindef = lenindef
4723 return obj, (v[EOC_LEN:] if lenindef else tail)
4726 class SequenceOf(Obj):
4727 """``SEQUENCE OF`` sequence type
4729 For that kind of type you must specify the object it will carry on
4730 (bounds are for example here, not required)::
4732 class Ints(SequenceOf):
4737 >>> ints.append(Integer(123))
4738 >>> ints.append(Integer(234))
4740 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4741 >>> [int(i) for i in ints]
4743 >>> ints.append(Integer(345))
4744 Traceback (most recent call last):
4745 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4748 >>> ints[1] = Integer(345)
4750 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4752 Also you can initialize sequence with preinitialized values:
4754 >>> ints = Ints([Integer(123), Integer(234)])
4756 __slots__ = ("spec", "_bound_min", "_bound_max")
4757 tag_default = tag_encode(form=TagFormConstructed, num=16)
4758 asn1_type_name = "SEQUENCE OF"
4771 super(SequenceOf, self).__init__(
4779 schema = getattr(self, "schema", None)
4781 raise ValueError("schema must be specified")
4783 self._bound_min, self._bound_max = getattr(
4787 ) if bounds is None else bounds
4789 if value is not None:
4790 self._value = self._value_sanitize(value)
4791 if default is not None:
4792 default_value = self._value_sanitize(default)
4793 default_obj = self.__class__(
4798 default_obj._value = default_value
4799 self.default = default_obj
4801 self._value = default_obj.copy()._value
4803 def _value_sanitize(self, value):
4804 if issubclass(value.__class__, SequenceOf):
4805 value = value._value
4806 elif hasattr(value, "__iter__"):
4809 raise InvalidValueType((self.__class__, iter))
4810 if not self._bound_min <= len(value) <= self._bound_max:
4811 raise BoundsError(self._bound_min, len(value), self._bound_max)
4813 if not isinstance(v, self.spec.__class__):
4814 raise InvalidValueType((self.spec.__class__,))
4819 return all(v.ready for v in self._value)
4822 obj = self.__class__(schema=self.spec)
4823 obj._bound_min = self._bound_min
4824 obj._bound_max = self._bound_max
4826 obj._expl = self._expl
4827 obj.default = self.default
4828 obj.optional = self.optional
4829 obj.offset = self.offset
4830 obj.llen = self.llen
4831 obj.vlen = self.vlen
4832 obj._value = [v.copy() for v in self._value]
4835 def __eq__(self, their):
4836 if isinstance(their, self.__class__):
4838 self.spec == their.spec and
4839 self.tag == their.tag and
4840 self._expl == their._expl and
4841 self._value == their._value
4843 if hasattr(their, "__iter__"):
4844 return self._value == list(their)
4856 return self.__class__(
4860 (self._bound_min, self._bound_max)
4861 if bounds is None else bounds
4863 impl=self.tag if impl is None else impl,
4864 expl=self._expl if expl is None else expl,
4865 default=self.default if default is None else default,
4866 optional=self.optional if optional is None else optional,
4869 def __contains__(self, key):
4870 return key in self._value
4872 def append(self, value):
4873 if not isinstance(value, self.spec.__class__):
4874 raise InvalidValueType((self.spec.__class__,))
4875 if len(self._value) + 1 > self._bound_max:
4878 len(self._value) + 1,
4881 self._value.append(value)
4884 self._assert_ready()
4885 return iter(self._value)
4888 self._assert_ready()
4889 return len(self._value)
4891 def __setitem__(self, key, value):
4892 if not isinstance(value, self.spec.__class__):
4893 raise InvalidValueType((self.spec.__class__,))
4894 self._value[key] = self.spec(value=value)
4896 def __getitem__(self, key):
4897 return self._value[key]
4899 def _encoded_values(self):
4900 return [v.encode() for v in self._value]
4903 v = b"".join(self._encoded_values())
4904 return b"".join((self.tag, len_encode(len(v)), v))
4906 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4908 t, tlen, lv = tag_strip(tlv)
4909 except DecodeError as err:
4910 raise err.__class__(
4912 klass=self.__class__,
4913 decode_path=decode_path,
4918 klass=self.__class__,
4919 decode_path=decode_path,
4926 l, llen, v = len_decode(lv)
4927 except LenIndefForm as err:
4928 if not ctx.get("bered", False):
4929 raise err.__class__(
4931 klass=self.__class__,
4932 decode_path=decode_path,
4935 l, llen, v = 0, 1, lv[1:]
4937 except DecodeError as err:
4938 raise err.__class__(
4940 klass=self.__class__,
4941 decode_path=decode_path,
4945 raise NotEnoughData(
4946 "encoded length is longer than data",
4947 klass=self.__class__,
4948 decode_path=decode_path,
4952 v, tail = v[:l], v[l:]
4954 sub_offset = offset + tlen + llen
4958 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4960 value, v_tail = spec.decode(
4964 decode_path=decode_path + (str(len(_value)),),
4967 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4968 sub_offset += value_len
4971 _value.append(value)
4972 obj = self.__class__(
4975 bounds=(self._bound_min, self._bound_max),
4978 default=self.default,
4979 optional=self.optional,
4980 _decoded=(offset, llen, vlen),
4982 obj.lenindef = lenindef
4983 return obj, (v[EOC_LEN:] if lenindef else tail)
4987 pp_console_row(next(self.pps())),
4988 ", ".join(repr(v) for v in self._value),
4991 def pps(self, decode_path=()):
4993 asn1_type_name=self.asn1_type_name,
4994 obj_name=self.__class__.__name__,
4995 decode_path=decode_path,
4996 optional=self.optional,
4997 default=self == self.default,
4998 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4999 expl=None if self._expl is None else tag_decode(self._expl),
5004 expl_offset=self.expl_offset if self.expled else None,
5005 expl_tlen=self.expl_tlen if self.expled else None,
5006 expl_llen=self.expl_llen if self.expled else None,
5007 expl_vlen=self.expl_vlen if self.expled else None,
5008 expl_lenindef=self.expl_lenindef,
5009 lenindef=self.lenindef,
5011 for i, value in enumerate(self._value):
5012 yield value.pps(decode_path=decode_path + (str(i),))
5015 class SetOf(SequenceOf):
5016 """``SET OF`` sequence type
5018 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5021 tag_default = tag_encode(form=TagFormConstructed, num=17)
5022 asn1_type_name = "SET OF"
5025 raws = self._encoded_values()
5028 return b"".join((self.tag, len_encode(len(v)), v))
5031 def obj_by_path(pypath): # pragma: no cover
5032 """Import object specified as string Python path
5034 Modules must be separated from classes/functions with ``:``.
5036 >>> obj_by_path("foo.bar:Baz")
5037 <class 'foo.bar.Baz'>
5038 >>> obj_by_path("foo.bar:Baz.boo")
5039 <classmethod 'foo.bar.Baz.boo'>
5041 mod, objs = pypath.rsplit(":", 1)
5042 from importlib import import_module
5043 obj = import_module(mod)
5044 for obj_name in objs.split("."):
5045 obj = getattr(obj, obj_name)
5049 def generic_decoder(): # pragma: no cover
5050 # All of this below is a big hack with self references
5051 choice = PrimitiveTypes()
5052 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5053 choice.specs["SetOf"] = SetOf(schema=choice)
5055 choice.specs["SequenceOf%d" % i] = SequenceOf(
5059 choice.specs["Any"] = Any()
5061 # Class name equals to type name, to omit it from output
5062 class SEQUENCEOF(SequenceOf):
5066 def pprint_any(obj, oids=None, with_colours=False):
5067 def _pprint_pps(pps):
5069 if hasattr(pp, "_fields"):
5070 if pp.asn1_type_name == Choice.asn1_type_name:
5072 pp_kwargs = pp._asdict()
5073 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5074 pp = _pp(**pp_kwargs)
5075 yield pp_console_row(
5080 with_colours=with_colours,
5082 for row in pp_console_blob(pp):
5085 for row in _pprint_pps(pp):
5087 return "\n".join(_pprint_pps(obj.pps()))
5088 return SEQUENCEOF(), pprint_any
5091 def main(): # pragma: no cover
5093 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5094 parser.add_argument(
5098 help="Skip that number of bytes from the beginning",
5100 parser.add_argument(
5102 help="Python path to dictionary with OIDs",
5104 parser.add_argument(
5106 help="Python path to schema definition to use",
5108 parser.add_argument(
5109 "--defines-by-path",
5110 help="Python path to decoder's defines_by_path",
5112 parser.add_argument(
5114 action='store_true',
5115 help="Disallow BER encoding",
5117 parser.add_argument(
5119 type=argparse.FileType("rb"),
5120 help="Path to DER file you want to decode",
5122 args = parser.parse_args()
5123 args.DERFile.seek(args.skip)
5124 der = memoryview(args.DERFile.read())
5125 args.DERFile.close()
5126 oids = obj_by_path(args.oids) if args.oids else {}
5128 schema = obj_by_path(args.schema)
5129 from functools import partial
5130 pprinter = partial(pprint, big_blobs=True)
5132 schema, pprinter = generic_decoder()
5133 ctx = {"bered": not args.nobered}
5134 if args.defines_by_path is not None:
5135 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5136 obj, tail = schema().decode(der, ctx=ctx)
5140 with_colours=True if environ.get("NO_COLOR") is None else False,
5143 print("\nTrailing data: %s" % hexenc(tail))
5146 if __name__ == "__main__":