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:
918 "implicit and explicit tags can not be set simultaneously"
920 if default is not None:
922 self.optional = optional
923 self.offset, self.llen, self.vlen = _decoded
925 self.expl_lenindef = False
926 self.lenindef = False
930 def ready(self): # pragma: no cover
931 """Is object ready to be encoded?
933 raise NotImplementedError()
935 def _assert_ready(self):
937 raise ObjNotReady(self.__class__.__name__)
941 """Is object decoded?
943 return (self.llen + self.vlen) > 0
945 def copy(self): # pragma: no cover
946 """Make a copy of object, safe to be mutated
948 raise NotImplementedError()
956 return self.tlen + self.llen + self.vlen
958 def __str__(self): # pragma: no cover
959 return self.__bytes__() if PY2 else self.__unicode__()
961 def __ne__(self, their):
962 return not(self == their)
964 def __gt__(self, their): # pragma: no cover
965 return not(self < their)
967 def __le__(self, their): # pragma: no cover
968 return (self == their) or (self < their)
970 def __ge__(self, their): # pragma: no cover
971 return (self == their) or (self > their)
973 def _encode(self): # pragma: no cover
974 raise NotImplementedError()
976 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
977 raise NotImplementedError()
981 if self._expl is None:
983 return b"".join((self._expl, len_encode(len(raw)), raw))
996 :param data: either binary or memoryview
997 :param int offset: initial data's offset
998 :param bool leavemm: do we need to leave memoryview of remaining
999 data as is, or convert it to bytes otherwise
1000 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1001 :param tag_only: decode only the tag, without length and contents
1002 (used only in Choice and Set structures, trying to
1003 determine if tag satisfies the scheme)
1004 :returns: (Obj, remaining data)
1008 tlv = memoryview(data)
1009 if self._expl is None:
1010 result = self._decode(
1013 decode_path=decode_path,
1022 t, tlen, lv = tag_strip(tlv)
1023 except DecodeError as err:
1024 raise err.__class__(
1026 klass=self.__class__,
1027 decode_path=decode_path,
1032 klass=self.__class__,
1033 decode_path=decode_path,
1037 l, llen, v = len_decode(lv)
1038 except LenIndefForm as err:
1039 if not ctx.get("bered", False):
1040 raise err.__class__(
1042 klass=self.__class__,
1043 decode_path=decode_path,
1047 offset += tlen + llen
1048 result = self._decode(
1051 decode_path=decode_path,
1058 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1059 if eoc_expected.tobytes() != EOC:
1062 decode_path=decode_path,
1066 obj.expl_lenindef = True
1067 except DecodeError as err:
1068 raise err.__class__(
1070 klass=self.__class__,
1071 decode_path=decode_path,
1076 raise NotEnoughData(
1077 "encoded length is longer than data",
1078 klass=self.__class__,
1079 decode_path=decode_path,
1082 result = self._decode(
1084 offset=offset + tlen + llen,
1085 decode_path=decode_path,
1092 return obj, (tail if leavemm else tail.tobytes())
1096 return self._expl is not None
1103 def expl_tlen(self):
1104 return len(self._expl)
1107 def expl_llen(self):
1108 if self.expl_lenindef:
1110 return len(len_encode(self.tlvlen))
1113 def expl_offset(self):
1114 return self.offset - self.expl_tlen - self.expl_llen
1117 def expl_vlen(self):
1121 def expl_tlvlen(self):
1122 return self.expl_tlen + self.expl_llen + self.expl_vlen
1125 class DecodePathDefBy(object):
1126 """DEFINED BY representation inside decode path
1128 __slots__ = ("defined_by",)
1130 def __init__(self, defined_by):
1131 self.defined_by = defined_by
1133 def __ne__(self, their):
1134 return not(self == their)
1136 def __eq__(self, their):
1137 if not isinstance(their, self.__class__):
1139 return self.defined_by == their.defined_by
1142 return "DEFINED BY " + str(self.defined_by)
1145 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1148 ########################################################################
1150 ########################################################################
1152 PP = namedtuple("PP", (
1177 asn1_type_name="unknown",
1194 expl_lenindef=False,
1222 def _colorize(what, colour, with_colours, attrs=("bold",)):
1223 return colored(what, colour, attrs=attrs) if with_colours else what
1238 " " if pp.expl_offset is None else
1239 ("-%d" % (pp.offset - pp.expl_offset))
1242 cols.append(_colorize(col, "red", with_colours, ()))
1243 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1244 col = _colorize(col, "green", with_colours, ())
1246 if pp.expl_lenindef:
1251 " " if ber_deoffset == 0 else
1252 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1255 if len(pp.decode_path) > 0:
1256 cols.append(" ." * (len(pp.decode_path)))
1257 ent = pp.decode_path[-1]
1258 if isinstance(ent, DecodePathDefBy):
1259 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1260 value = str(ent.defined_by)
1262 oids is not None and
1263 ent.defined_by.asn1_type_name ==
1264 ObjectIdentifier.asn1_type_name and
1267 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1269 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1271 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1272 if pp.expl is not None:
1273 klass, _, num = pp.expl
1274 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1275 cols.append(_colorize(col, "blue", with_colours))
1276 if pp.impl is not None:
1277 klass, _, num = pp.impl
1278 col = "[%s%d]" % (TagClassReprs[klass], num)
1279 cols.append(_colorize(col, "blue", with_colours))
1280 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1281 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1283 cols.append(_colorize("BER", "red", with_colours))
1284 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1285 if pp.value is not None:
1287 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1289 oids is not None and
1290 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1293 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1295 if isinstance(pp.blob, binary_type):
1296 cols.append(hexenc(pp.blob))
1297 elif isinstance(pp.blob, tuple):
1298 cols.append(", ".join(pp.blob))
1300 cols.append(_colorize("OPTIONAL", "red", with_colours))
1302 cols.append(_colorize("DEFAULT", "red", with_colours))
1303 return " ".join(cols)
1306 def pp_console_blob(pp):
1307 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1308 if len(pp.decode_path) > 0:
1309 cols.append(" ." * (len(pp.decode_path) + 1))
1310 if isinstance(pp.blob, binary_type):
1311 blob = hexenc(pp.blob).upper()
1312 for i in range(0, len(blob), 32):
1313 chunk = blob[i:i + 32]
1314 yield " ".join(cols + [":".join(
1315 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1317 elif isinstance(pp.blob, tuple):
1318 yield " ".join(cols + [", ".join(pp.blob)])
1321 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1322 """Pretty print object
1324 :param Obj obj: object you want to pretty print
1325 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1326 from it is met, then its humand readable form is printed
1327 :param big_blobs: if large binary objects are met (like OctetString
1328 values), do we need to print them too, on separate
1330 :param with_colours: colourize output, if ``termcolor`` library
1333 def _pprint_pps(pps):
1335 if hasattr(pp, "_fields"):
1337 yield pp_console_row(
1342 with_colours=with_colours,
1344 for row in pp_console_blob(pp):
1347 yield pp_console_row(
1352 with_colours=with_colours,
1355 for row in _pprint_pps(pp):
1357 return "\n".join(_pprint_pps(obj.pps()))
1360 ########################################################################
1361 # ASN.1 primitive types
1362 ########################################################################
1365 """``BOOLEAN`` boolean type
1367 >>> b = Boolean(True)
1369 >>> b == Boolean(True)
1375 tag_default = tag_encode(1)
1376 asn1_type_name = "BOOLEAN"
1388 :param value: set the value. Either boolean type, or
1389 :py:class:`pyderasn.Boolean` object
1390 :param bytes impl: override default tag with ``IMPLICIT`` one
1391 :param bytes expl: override default tag with ``EXPLICIT`` one
1392 :param default: set default value. Type same as in ``value``
1393 :param bool optional: is object ``OPTIONAL`` in sequence
1395 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1396 self._value = None if value is None else self._value_sanitize(value)
1397 if default is not None:
1398 default = self._value_sanitize(default)
1399 self.default = self.__class__(
1405 self._value = default
1407 def _value_sanitize(self, value):
1408 if issubclass(value.__class__, Boolean):
1410 if isinstance(value, bool):
1412 raise InvalidValueType((self.__class__, bool))
1416 return self._value is not None
1419 obj = self.__class__()
1420 obj._value = self._value
1422 obj._expl = self._expl
1423 obj.default = self.default
1424 obj.optional = self.optional
1425 obj.offset = self.offset
1426 obj.llen = self.llen
1427 obj.vlen = self.vlen
1430 def __nonzero__(self):
1431 self._assert_ready()
1435 self._assert_ready()
1438 def __eq__(self, their):
1439 if isinstance(their, bool):
1440 return self._value == their
1441 if not issubclass(their.__class__, Boolean):
1444 self._value == their._value and
1445 self.tag == their.tag and
1446 self._expl == their._expl
1457 return self.__class__(
1459 impl=self.tag if impl is None else impl,
1460 expl=self._expl if expl is None else expl,
1461 default=self.default if default is None else default,
1462 optional=self.optional if optional is None else optional,
1466 self._assert_ready()
1470 (b"\xFF" if self._value else b"\x00"),
1473 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1475 t, _, lv = tag_strip(tlv)
1476 except DecodeError as err:
1477 raise err.__class__(
1479 klass=self.__class__,
1480 decode_path=decode_path,
1485 klass=self.__class__,
1486 decode_path=decode_path,
1492 l, _, v = len_decode(lv)
1493 except DecodeError as err:
1494 raise err.__class__(
1496 klass=self.__class__,
1497 decode_path=decode_path,
1501 raise InvalidLength(
1502 "Boolean's length must be equal to 1",
1503 klass=self.__class__,
1504 decode_path=decode_path,
1508 raise NotEnoughData(
1509 "encoded length is longer than data",
1510 klass=self.__class__,
1511 decode_path=decode_path,
1514 first_octet = byte2int(v)
1516 if first_octet == 0:
1518 elif first_octet == 0xFF:
1520 elif ctx.get("bered", False):
1525 "unacceptable Boolean value",
1526 klass=self.__class__,
1527 decode_path=decode_path,
1530 obj = self.__class__(
1534 default=self.default,
1535 optional=self.optional,
1536 _decoded=(offset, 1, 1),
1542 return pp_console_row(next(self.pps()))
1544 def pps(self, decode_path=()):
1546 asn1_type_name=self.asn1_type_name,
1547 obj_name=self.__class__.__name__,
1548 decode_path=decode_path,
1549 value=str(self._value) if self.ready else None,
1550 optional=self.optional,
1551 default=self == self.default,
1552 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1553 expl=None if self._expl is None else tag_decode(self._expl),
1558 expl_offset=self.expl_offset if self.expled else None,
1559 expl_tlen=self.expl_tlen if self.expled else None,
1560 expl_llen=self.expl_llen if self.expled else None,
1561 expl_vlen=self.expl_vlen if self.expled else None,
1562 expl_lenindef=self.expl_lenindef,
1568 """``INTEGER`` integer type
1570 >>> b = Integer(-123)
1572 >>> b == Integer(-123)
1577 >>> Integer(2, bounds=(1, 3))
1579 >>> Integer(5, bounds=(1, 3))
1580 Traceback (most recent call last):
1581 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1585 class Version(Integer):
1592 >>> v = Version("v1")
1599 {'v3': 2, 'v1': 0, 'v2': 1}
1601 __slots__ = ("specs", "_bound_min", "_bound_max")
1602 tag_default = tag_encode(2)
1603 asn1_type_name = "INTEGER"
1617 :param value: set the value. Either integer type, named value
1618 (if ``schema`` is specified in the class), or
1619 :py:class:`pyderasn.Integer` object
1620 :param bounds: set ``(MIN, MAX)`` value constraint.
1621 (-inf, +inf) by default
1622 :param bytes impl: override default tag with ``IMPLICIT`` one
1623 :param bytes expl: override default tag with ``EXPLICIT`` one
1624 :param default: set default value. Type same as in ``value``
1625 :param bool optional: is object ``OPTIONAL`` in sequence
1627 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1629 specs = getattr(self, "schema", {}) if _specs is None else _specs
1630 self.specs = specs if isinstance(specs, dict) else dict(specs)
1631 self._bound_min, self._bound_max = getattr(
1634 (float("-inf"), float("+inf")),
1635 ) if bounds is None else bounds
1636 if value is not None:
1637 self._value = self._value_sanitize(value)
1638 if default is not None:
1639 default = self._value_sanitize(default)
1640 self.default = self.__class__(
1646 if self._value is None:
1647 self._value = default
1649 def _value_sanitize(self, value):
1650 if issubclass(value.__class__, Integer):
1651 value = value._value
1652 elif isinstance(value, integer_types):
1654 elif isinstance(value, str):
1655 value = self.specs.get(value)
1657 raise ObjUnknown("integer value: %s" % value)
1659 raise InvalidValueType((self.__class__, int, str))
1660 if not self._bound_min <= value <= self._bound_max:
1661 raise BoundsError(self._bound_min, value, self._bound_max)
1666 return self._value is not None
1669 obj = self.__class__(_specs=self.specs)
1670 obj._value = self._value
1671 obj._bound_min = self._bound_min
1672 obj._bound_max = self._bound_max
1674 obj._expl = self._expl
1675 obj.default = self.default
1676 obj.optional = self.optional
1677 obj.offset = self.offset
1678 obj.llen = self.llen
1679 obj.vlen = self.vlen
1683 self._assert_ready()
1684 return int(self._value)
1687 self._assert_ready()
1690 bytes(self._expl or b"") +
1691 str(self._value).encode("ascii"),
1694 def __eq__(self, their):
1695 if isinstance(their, integer_types):
1696 return self._value == their
1697 if not issubclass(their.__class__, Integer):
1700 self._value == their._value and
1701 self.tag == their.tag and
1702 self._expl == their._expl
1705 def __lt__(self, their):
1706 return self._value < their._value
1710 for name, value in self.specs.items():
1711 if value == self._value:
1723 return self.__class__(
1726 (self._bound_min, self._bound_max)
1727 if bounds is None else bounds
1729 impl=self.tag if impl is None else impl,
1730 expl=self._expl if expl is None else expl,
1731 default=self.default if default is None else default,
1732 optional=self.optional if optional is None else optional,
1737 self._assert_ready()
1741 octets = bytearray([0])
1745 octets = bytearray()
1747 octets.append((value & 0xFF) ^ 0xFF)
1749 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1752 octets = bytearray()
1754 octets.append(value & 0xFF)
1756 if octets[-1] & 0x80 > 0:
1759 octets = bytes(octets)
1761 bytes_len = ceil(value.bit_length() / 8) or 1
1764 octets = value.to_bytes(
1769 except OverflowError:
1773 return b"".join((self.tag, len_encode(len(octets)), octets))
1775 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1777 t, _, lv = tag_strip(tlv)
1778 except DecodeError as err:
1779 raise err.__class__(
1781 klass=self.__class__,
1782 decode_path=decode_path,
1787 klass=self.__class__,
1788 decode_path=decode_path,
1794 l, llen, v = len_decode(lv)
1795 except DecodeError as err:
1796 raise err.__class__(
1798 klass=self.__class__,
1799 decode_path=decode_path,
1803 raise NotEnoughData(
1804 "encoded length is longer than data",
1805 klass=self.__class__,
1806 decode_path=decode_path,
1810 raise NotEnoughData(
1812 klass=self.__class__,
1813 decode_path=decode_path,
1816 v, tail = v[:l], v[l:]
1817 first_octet = byte2int(v)
1819 second_octet = byte2int(v[1:])
1821 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1822 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1825 "non normalized integer",
1826 klass=self.__class__,
1827 decode_path=decode_path,
1832 if first_octet & 0x80 > 0:
1833 octets = bytearray()
1834 for octet in bytearray(v):
1835 octets.append(octet ^ 0xFF)
1836 for octet in octets:
1837 value = (value << 8) | octet
1841 for octet in bytearray(v):
1842 value = (value << 8) | octet
1844 value = int.from_bytes(v, byteorder="big", signed=True)
1846 obj = self.__class__(
1848 bounds=(self._bound_min, self._bound_max),
1851 default=self.default,
1852 optional=self.optional,
1854 _decoded=(offset, llen, l),
1856 except BoundsError as err:
1859 klass=self.__class__,
1860 decode_path=decode_path,
1866 return pp_console_row(next(self.pps()))
1868 def pps(self, decode_path=()):
1870 asn1_type_name=self.asn1_type_name,
1871 obj_name=self.__class__.__name__,
1872 decode_path=decode_path,
1873 value=(self.named or str(self._value)) if self.ready else None,
1874 optional=self.optional,
1875 default=self == self.default,
1876 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1877 expl=None if self._expl is None else tag_decode(self._expl),
1882 expl_offset=self.expl_offset if self.expled else None,
1883 expl_tlen=self.expl_tlen if self.expled else None,
1884 expl_llen=self.expl_llen if self.expled else None,
1885 expl_vlen=self.expl_vlen if self.expled else None,
1886 expl_lenindef=self.expl_lenindef,
1890 class BitString(Obj):
1891 """``BIT STRING`` bit string type
1893 >>> BitString(b"hello world")
1894 BIT STRING 88 bits 68656c6c6f20776f726c64
1897 >>> b == b"hello world"
1902 >>> BitString("'0A3B5F291CD'H")
1903 BIT STRING 44 bits 0a3b5f291cd0
1904 >>> b = BitString("'010110000000'B")
1905 BIT STRING 12 bits 5800
1908 >>> b[0], b[1], b[2], b[3]
1909 (False, True, False, True)
1913 [False, True, False, True, True, False, False, False, False, False, False, False]
1917 class KeyUsage(BitString):
1919 ("digitalSignature", 0),
1920 ("nonRepudiation", 1),
1921 ("keyEncipherment", 2),
1924 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1925 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1927 ['nonRepudiation', 'keyEncipherment']
1929 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1933 Pay attention that BIT STRING can be encoded both in primitive
1934 and constructed forms. Decoder always checks constructed form tag
1935 additionally to specified primitive one. If BER decoding is
1936 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
1937 of DER restrictions.
1939 __slots__ = ("tag_constructed", "specs", "defined")
1940 tag_default = tag_encode(3)
1941 asn1_type_name = "BIT STRING"
1954 :param value: set the value. Either binary type, tuple of named
1955 values (if ``schema`` is specified in the class),
1956 string in ``'XXX...'B`` form, or
1957 :py:class:`pyderasn.BitString` object
1958 :param bytes impl: override default tag with ``IMPLICIT`` one
1959 :param bytes expl: override default tag with ``EXPLICIT`` one
1960 :param default: set default value. Type same as in ``value``
1961 :param bool optional: is object ``OPTIONAL`` in sequence
1963 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1964 specs = getattr(self, "schema", {}) if _specs is None else _specs
1965 self.specs = specs if isinstance(specs, dict) else dict(specs)
1966 self._value = None if value is None else self._value_sanitize(value)
1967 if default is not None:
1968 default = self._value_sanitize(default)
1969 self.default = self.__class__(
1975 self._value = default
1977 tag_klass, _, tag_num = tag_decode(self.tag)
1978 self.tag_constructed = tag_encode(
1980 form=TagFormConstructed,
1984 def _bits2octets(self, bits):
1985 if len(self.specs) > 0:
1986 bits = bits.rstrip("0")
1988 bits += "0" * ((8 - (bit_len % 8)) % 8)
1989 octets = bytearray(len(bits) // 8)
1990 for i in six_xrange(len(octets)):
1991 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1992 return bit_len, bytes(octets)
1994 def _value_sanitize(self, value):
1995 if issubclass(value.__class__, BitString):
1997 if isinstance(value, (string_types, binary_type)):
1999 isinstance(value, string_types) and
2000 value.startswith("'")
2002 if value.endswith("'B"):
2004 if not set(value) <= set(("0", "1")):
2005 raise ValueError("B's coding contains unacceptable chars")
2006 return self._bits2octets(value)
2007 elif value.endswith("'H"):
2011 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2014 raise InvalidValueType((self.__class__, string_types, binary_type))
2015 elif isinstance(value, binary_type):
2016 return (len(value) * 8, value)
2018 raise InvalidValueType((self.__class__, string_types, binary_type))
2019 if isinstance(value, tuple):
2022 isinstance(value[0], integer_types) and
2023 isinstance(value[1], binary_type)
2028 bit = self.specs.get(name)
2030 raise ObjUnknown("BitString value: %s" % name)
2033 return self._bits2octets("")
2035 return self._bits2octets("".join(
2036 ("1" if bit in bits else "0")
2037 for bit in six_xrange(max(bits) + 1)
2039 raise InvalidValueType((self.__class__, binary_type, string_types))
2043 return self._value is not None
2046 obj = self.__class__(_specs=self.specs)
2048 if value is not None:
2049 value = (value[0], value[1])
2052 obj._expl = self._expl
2053 obj.default = self.default
2054 obj.optional = self.optional
2055 obj.offset = self.offset
2056 obj.llen = self.llen
2057 obj.vlen = self.vlen
2061 self._assert_ready()
2062 for i in six_xrange(self._value[0]):
2067 self._assert_ready()
2068 return self._value[0]
2070 def __bytes__(self):
2071 self._assert_ready()
2072 return self._value[1]
2074 def __eq__(self, their):
2075 if isinstance(their, bytes):
2076 return self._value[1] == their
2077 if not issubclass(their.__class__, BitString):
2080 self._value == their._value and
2081 self.tag == their.tag and
2082 self._expl == their._expl
2087 return [name for name, bit in self.specs.items() if self[bit]]
2097 return self.__class__(
2099 impl=self.tag if impl is None else impl,
2100 expl=self._expl if expl is None else expl,
2101 default=self.default if default is None else default,
2102 optional=self.optional if optional is None else optional,
2106 def __getitem__(self, key):
2107 if isinstance(key, int):
2108 bit_len, octets = self._value
2112 byte2int(memoryview(octets)[key // 8:]) >>
2115 if isinstance(key, string_types):
2116 value = self.specs.get(key)
2118 raise ObjUnknown("BitString value: %s" % key)
2120 raise InvalidValueType((int, str))
2123 self._assert_ready()
2124 bit_len, octets = self._value
2127 len_encode(len(octets) + 1),
2128 int2byte((8 - bit_len % 8) % 8),
2132 def _decode_chunk(self, lv, offset, decode_path, ctx):
2134 l, llen, v = len_decode(lv)
2135 except DecodeError as err:
2136 raise err.__class__(
2138 klass=self.__class__,
2139 decode_path=decode_path,
2143 raise NotEnoughData(
2144 "encoded length is longer than data",
2145 klass=self.__class__,
2146 decode_path=decode_path,
2150 raise NotEnoughData(
2152 klass=self.__class__,
2153 decode_path=decode_path,
2156 pad_size = byte2int(v)
2157 if l == 1 and pad_size != 0:
2159 "invalid empty value",
2160 klass=self.__class__,
2161 decode_path=decode_path,
2167 klass=self.__class__,
2168 decode_path=decode_path,
2171 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2174 klass=self.__class__,
2175 decode_path=decode_path,
2178 v, tail = v[:l], v[l:]
2179 obj = self.__class__(
2180 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2183 default=self.default,
2184 optional=self.optional,
2186 _decoded=(offset, llen, l),
2190 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2192 t, tlen, lv = tag_strip(tlv)
2193 except DecodeError as err:
2194 raise err.__class__(
2196 klass=self.__class__,
2197 decode_path=decode_path,
2203 return self._decode_chunk(lv, offset, decode_path, ctx)
2204 if t == self.tag_constructed:
2205 if not ctx.get("bered", False):
2207 msg="unallowed BER constructed encoding",
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 msg="chunk out of bounds",
2253 decode_path=len(chunks) - 1,
2254 offset=chunks[-1].offset,
2256 sub_decode_path = decode_path + (str(len(chunks)),)
2258 chunk, v_tail = BitString().decode(
2261 decode_path=sub_decode_path,
2267 msg="expected BitString encoded chunk",
2268 decode_path=sub_decode_path,
2271 chunks.append(chunk)
2272 sub_offset += chunk.tlvlen
2273 vlen += chunk.tlvlen
2275 if len(chunks) == 0:
2278 decode_path=decode_path,
2283 for chunk_i, chunk in enumerate(chunks[:-1]):
2284 if chunk.bit_len % 8 != 0:
2286 msg="BitString chunk is not multiple of 8 bit",
2287 decode_path=decode_path + (str(chunk_i),),
2288 offset=chunk.offset,
2290 values.append(bytes(chunk))
2291 bit_len += chunk.bit_len
2292 chunk_last = chunks[-1]
2293 values.append(bytes(chunk_last))
2294 bit_len += chunk_last.bit_len
2295 obj = self.__class__(
2296 value=(bit_len, b"".join(values)),
2299 default=self.default,
2300 optional=self.optional,
2302 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2304 obj.lenindef = lenindef
2306 return obj, (v[EOC_LEN:] if lenindef else v)
2308 klass=self.__class__,
2309 decode_path=decode_path,
2314 return pp_console_row(next(self.pps()))
2316 def pps(self, decode_path=()):
2320 bit_len, blob = self._value
2321 value = "%d bits" % bit_len
2322 if len(self.specs) > 0:
2323 blob = tuple(self.named)
2325 asn1_type_name=self.asn1_type_name,
2326 obj_name=self.__class__.__name__,
2327 decode_path=decode_path,
2330 optional=self.optional,
2331 default=self == self.default,
2332 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2333 expl=None if self._expl is None else tag_decode(self._expl),
2338 expl_offset=self.expl_offset if self.expled else None,
2339 expl_tlen=self.expl_tlen if self.expled else None,
2340 expl_llen=self.expl_llen if self.expled else None,
2341 expl_vlen=self.expl_vlen if self.expled else None,
2342 expl_lenindef=self.expl_lenindef,
2343 lenindef=self.lenindef,
2346 defined_by, defined = self.defined or (None, None)
2347 if defined_by is not None:
2349 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2353 class OctetString(Obj):
2354 """``OCTET STRING`` binary string type
2356 >>> s = OctetString(b"hello world")
2357 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2358 >>> s == OctetString(b"hello world")
2363 >>> OctetString(b"hello", bounds=(4, 4))
2364 Traceback (most recent call last):
2365 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2366 >>> OctetString(b"hell", bounds=(4, 4))
2367 OCTET STRING 4 bytes 68656c6c
2371 Pay attention that OCTET STRING can be encoded both in primitive
2372 and constructed forms. Decoder always checks constructed form tag
2373 additionally to specified primitive one. If BER decoding is
2374 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2375 of DER restrictions.
2377 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2378 tag_default = tag_encode(4)
2379 asn1_type_name = "OCTET STRING"
2392 :param value: set the value. Either binary type, or
2393 :py:class:`pyderasn.OctetString` object
2394 :param bounds: set ``(MIN, MAX)`` value size constraint.
2395 (-inf, +inf) by default
2396 :param bytes impl: override default tag with ``IMPLICIT`` one
2397 :param bytes expl: override default tag with ``EXPLICIT`` one
2398 :param default: set default value. Type same as in ``value``
2399 :param bool optional: is object ``OPTIONAL`` in sequence
2401 super(OctetString, self).__init__(
2409 self._bound_min, self._bound_max = getattr(
2413 ) if bounds is None else bounds
2414 if value is not None:
2415 self._value = self._value_sanitize(value)
2416 if default is not None:
2417 default = self._value_sanitize(default)
2418 self.default = self.__class__(
2423 if self._value is None:
2424 self._value = default
2426 tag_klass, _, tag_num = tag_decode(self.tag)
2427 self.tag_constructed = tag_encode(
2429 form=TagFormConstructed,
2433 def _value_sanitize(self, value):
2434 if issubclass(value.__class__, OctetString):
2435 value = value._value
2436 elif isinstance(value, binary_type):
2439 raise InvalidValueType((self.__class__, bytes))
2440 if not self._bound_min <= len(value) <= self._bound_max:
2441 raise BoundsError(self._bound_min, len(value), self._bound_max)
2446 return self._value is not None
2449 obj = self.__class__()
2450 obj._value = self._value
2451 obj._bound_min = self._bound_min
2452 obj._bound_max = self._bound_max
2454 obj._expl = self._expl
2455 obj.default = self.default
2456 obj.optional = self.optional
2457 obj.offset = self.offset
2458 obj.llen = self.llen
2459 obj.vlen = self.vlen
2462 def __bytes__(self):
2463 self._assert_ready()
2466 def __eq__(self, their):
2467 if isinstance(their, binary_type):
2468 return self._value == their
2469 if not issubclass(their.__class__, OctetString):
2472 self._value == their._value and
2473 self.tag == their.tag and
2474 self._expl == their._expl
2477 def __lt__(self, their):
2478 return self._value < their._value
2489 return self.__class__(
2492 (self._bound_min, self._bound_max)
2493 if bounds is None else bounds
2495 impl=self.tag if impl is None else impl,
2496 expl=self._expl if expl is None else expl,
2497 default=self.default if default is None else default,
2498 optional=self.optional if optional is None else optional,
2502 self._assert_ready()
2505 len_encode(len(self._value)),
2509 def _decode_chunk(self, lv, offset, decode_path, ctx):
2511 l, llen, v = len_decode(lv)
2512 except DecodeError as err:
2513 raise err.__class__(
2515 klass=self.__class__,
2516 decode_path=decode_path,
2520 raise NotEnoughData(
2521 "encoded length is longer than data",
2522 klass=self.__class__,
2523 decode_path=decode_path,
2526 v, tail = v[:l], v[l:]
2528 obj = self.__class__(
2530 bounds=(self._bound_min, self._bound_max),
2533 default=self.default,
2534 optional=self.optional,
2535 _decoded=(offset, llen, l),
2537 except DecodeError as err:
2540 klass=self.__class__,
2541 decode_path=decode_path,
2544 except BoundsError as err:
2547 klass=self.__class__,
2548 decode_path=decode_path,
2553 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2555 t, tlen, lv = tag_strip(tlv)
2556 except DecodeError as err:
2557 raise err.__class__(
2559 klass=self.__class__,
2560 decode_path=decode_path,
2566 return self._decode_chunk(lv, offset, decode_path, ctx)
2567 if t == self.tag_constructed:
2568 if not ctx.get("bered", False):
2570 msg="unallowed BER constructed encoding",
2571 decode_path=decode_path,
2578 l, llen, v = len_decode(lv)
2579 except LenIndefForm:
2580 llen, l, v = 1, 0, lv[1:]
2582 except DecodeError as err:
2583 raise err.__class__(
2585 klass=self.__class__,
2586 decode_path=decode_path,
2589 if l > 0 and l > len(v):
2590 raise NotEnoughData(
2591 "encoded length is longer than data",
2592 klass=self.__class__,
2593 decode_path=decode_path,
2596 if not lenindef and l == 0:
2597 raise NotEnoughData(
2599 klass=self.__class__,
2600 decode_path=decode_path,
2604 sub_offset = offset + tlen + llen
2608 if v[:EOC_LEN].tobytes() == EOC:
2615 msg="chunk out of bounds",
2616 decode_path=len(chunks) - 1,
2617 offset=chunks[-1].offset,
2619 sub_decode_path = decode_path + (str(len(chunks)),)
2621 chunk, v_tail = OctetString().decode(
2624 decode_path=sub_decode_path,
2630 msg="expected OctetString encoded chunk",
2631 decode_path=sub_decode_path,
2634 chunks.append(chunk)
2635 sub_offset += chunk.tlvlen
2636 vlen += chunk.tlvlen
2638 if len(chunks) == 0:
2641 decode_path=decode_path,
2645 obj = self.__class__(
2646 value=b"".join(bytes(chunk) for chunk in chunks),
2647 bounds=(self._bound_min, self._bound_max),
2650 default=self.default,
2651 optional=self.optional,
2652 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2654 except DecodeError as err:
2657 klass=self.__class__,
2658 decode_path=decode_path,
2661 except BoundsError as err:
2664 klass=self.__class__,
2665 decode_path=decode_path,
2668 obj.lenindef = lenindef
2670 return obj, (v[EOC_LEN:] if lenindef else v)
2672 klass=self.__class__,
2673 decode_path=decode_path,
2678 return pp_console_row(next(self.pps()))
2680 def pps(self, decode_path=()):
2682 asn1_type_name=self.asn1_type_name,
2683 obj_name=self.__class__.__name__,
2684 decode_path=decode_path,
2685 value=("%d bytes" % len(self._value)) if self.ready else None,
2686 blob=self._value if self.ready else None,
2687 optional=self.optional,
2688 default=self == self.default,
2689 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2690 expl=None if self._expl is None else tag_decode(self._expl),
2695 expl_offset=self.expl_offset if self.expled else None,
2696 expl_tlen=self.expl_tlen if self.expled else None,
2697 expl_llen=self.expl_llen if self.expled else None,
2698 expl_vlen=self.expl_vlen if self.expled else None,
2699 expl_lenindef=self.expl_lenindef,
2700 lenindef=self.lenindef,
2703 defined_by, defined = self.defined or (None, None)
2704 if defined_by is not None:
2706 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2711 """``NULL`` null object
2719 tag_default = tag_encode(5)
2720 asn1_type_name = "NULL"
2724 value=None, # unused, but Sequence passes it
2731 :param bytes impl: override default tag with ``IMPLICIT`` one
2732 :param bytes expl: override default tag with ``EXPLICIT`` one
2733 :param bool optional: is object ``OPTIONAL`` in sequence
2735 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2743 obj = self.__class__()
2745 obj._expl = self._expl
2746 obj.default = self.default
2747 obj.optional = self.optional
2748 obj.offset = self.offset
2749 obj.llen = self.llen
2750 obj.vlen = self.vlen
2753 def __eq__(self, their):
2754 if not issubclass(their.__class__, Null):
2757 self.tag == their.tag and
2758 self._expl == their._expl
2768 return self.__class__(
2769 impl=self.tag if impl is None else impl,
2770 expl=self._expl if expl is None else expl,
2771 optional=self.optional if optional is None else optional,
2775 return self.tag + len_encode(0)
2777 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2779 t, _, lv = tag_strip(tlv)
2780 except DecodeError as err:
2781 raise err.__class__(
2783 klass=self.__class__,
2784 decode_path=decode_path,
2789 klass=self.__class__,
2790 decode_path=decode_path,
2796 l, _, v = len_decode(lv)
2797 except DecodeError as err:
2798 raise err.__class__(
2800 klass=self.__class__,
2801 decode_path=decode_path,
2805 raise InvalidLength(
2806 "Null must have zero length",
2807 klass=self.__class__,
2808 decode_path=decode_path,
2811 obj = self.__class__(
2814 optional=self.optional,
2815 _decoded=(offset, 1, 0),
2820 return pp_console_row(next(self.pps()))
2822 def pps(self, decode_path=()):
2824 asn1_type_name=self.asn1_type_name,
2825 obj_name=self.__class__.__name__,
2826 decode_path=decode_path,
2827 optional=self.optional,
2828 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2829 expl=None if self._expl is None else tag_decode(self._expl),
2834 expl_offset=self.expl_offset if self.expled else None,
2835 expl_tlen=self.expl_tlen if self.expled else None,
2836 expl_llen=self.expl_llen if self.expled else None,
2837 expl_vlen=self.expl_vlen if self.expled else None,
2838 expl_lenindef=self.expl_lenindef,
2842 class ObjectIdentifier(Obj):
2843 """``OBJECT IDENTIFIER`` OID type
2845 >>> oid = ObjectIdentifier((1, 2, 3))
2846 OBJECT IDENTIFIER 1.2.3
2847 >>> oid == ObjectIdentifier("1.2.3")
2853 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2854 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2856 >>> str(ObjectIdentifier((3, 1)))
2857 Traceback (most recent call last):
2858 pyderasn.InvalidOID: unacceptable first arc value
2860 __slots__ = ("defines",)
2861 tag_default = tag_encode(6)
2862 asn1_type_name = "OBJECT IDENTIFIER"
2875 :param value: set the value. Either tuples of integers,
2876 string of "."-concatenated integers, or
2877 :py:class:`pyderasn.ObjectIdentifier` object
2878 :param defines: sequence of tuples. Each tuple has two elements.
2879 First one is relative to current one decode
2880 path, aiming to the field defined by that OID.
2881 Read about relative path in
2882 :py:func:`pyderasn.abs_decode_path`. Second
2883 tuple element is ``{OID: pyderasn.Obj()}``
2884 dictionary, mapping between current OID value
2885 and structure applied to defined field.
2886 :ref:`Read about DEFINED BY <definedby>`
2887 :param bytes impl: override default tag with ``IMPLICIT`` one
2888 :param bytes expl: override default tag with ``EXPLICIT`` one
2889 :param default: set default value. Type same as in ``value``
2890 :param bool optional: is object ``OPTIONAL`` in sequence
2892 super(ObjectIdentifier, self).__init__(
2900 if value is not None:
2901 self._value = self._value_sanitize(value)
2902 if default is not None:
2903 default = self._value_sanitize(default)
2904 self.default = self.__class__(
2909 if self._value is None:
2910 self._value = default
2911 self.defines = defines
2913 def __add__(self, their):
2914 if isinstance(their, self.__class__):
2915 return self.__class__(self._value + their._value)
2916 if isinstance(their, tuple):
2917 return self.__class__(self._value + their)
2918 raise InvalidValueType((self.__class__, tuple))
2920 def _value_sanitize(self, value):
2921 if issubclass(value.__class__, ObjectIdentifier):
2923 if isinstance(value, string_types):
2925 value = tuple(int(arc) for arc in value.split("."))
2927 raise InvalidOID("unacceptable arcs values")
2928 if isinstance(value, tuple):
2930 raise InvalidOID("less than 2 arcs")
2931 first_arc = value[0]
2932 if first_arc in (0, 1):
2933 if not (0 <= value[1] <= 39):
2934 raise InvalidOID("second arc is too wide")
2935 elif first_arc == 2:
2938 raise InvalidOID("unacceptable first arc value")
2940 raise InvalidValueType((self.__class__, str, tuple))
2944 return self._value is not None
2947 obj = self.__class__()
2948 obj._value = self._value
2949 obj.defines = self.defines
2951 obj._expl = self._expl
2952 obj.default = self.default
2953 obj.optional = self.optional
2954 obj.offset = self.offset
2955 obj.llen = self.llen
2956 obj.vlen = self.vlen
2960 self._assert_ready()
2961 return iter(self._value)
2964 return ".".join(str(arc) for arc in self._value or ())
2967 self._assert_ready()
2970 bytes(self._expl or b"") +
2971 str(self._value).encode("ascii"),
2974 def __eq__(self, their):
2975 if isinstance(their, tuple):
2976 return self._value == their
2977 if not issubclass(their.__class__, ObjectIdentifier):
2980 self.tag == their.tag and
2981 self._expl == their._expl and
2982 self._value == their._value
2985 def __lt__(self, their):
2986 return self._value < their._value
2997 return self.__class__(
2999 defines=self.defines if defines is None else defines,
3000 impl=self.tag if impl is None else impl,
3001 expl=self._expl if expl is None else expl,
3002 default=self.default if default is None else default,
3003 optional=self.optional if optional is None else optional,
3007 self._assert_ready()
3009 first_value = value[1]
3010 first_arc = value[0]
3013 elif first_arc == 1:
3015 elif first_arc == 2:
3017 else: # pragma: no cover
3018 raise RuntimeError("invalid arc is stored")
3019 octets = [zero_ended_encode(first_value)]
3020 for arc in value[2:]:
3021 octets.append(zero_ended_encode(arc))
3022 v = b"".join(octets)
3023 return b"".join((self.tag, len_encode(len(v)), v))
3025 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3027 t, _, lv = tag_strip(tlv)
3028 except DecodeError as err:
3029 raise err.__class__(
3031 klass=self.__class__,
3032 decode_path=decode_path,
3037 klass=self.__class__,
3038 decode_path=decode_path,
3044 l, llen, v = len_decode(lv)
3045 except DecodeError as err:
3046 raise err.__class__(
3048 klass=self.__class__,
3049 decode_path=decode_path,
3053 raise NotEnoughData(
3054 "encoded length is longer than data",
3055 klass=self.__class__,
3056 decode_path=decode_path,
3060 raise NotEnoughData(
3062 klass=self.__class__,
3063 decode_path=decode_path,
3066 v, tail = v[:l], v[l:]
3072 octet = indexbytes(v, i)
3073 arc = (arc << 7) | (octet & 0x7F)
3074 if octet & 0x80 == 0:
3082 klass=self.__class__,
3083 decode_path=decode_path,
3087 second_arc = arcs[0]
3088 if 0 <= second_arc <= 39:
3090 elif 40 <= second_arc <= 79:
3096 obj = self.__class__(
3097 value=tuple([first_arc, second_arc] + arcs[1:]),
3100 default=self.default,
3101 optional=self.optional,
3102 _decoded=(offset, llen, l),
3107 return pp_console_row(next(self.pps()))
3109 def pps(self, decode_path=()):
3111 asn1_type_name=self.asn1_type_name,
3112 obj_name=self.__class__.__name__,
3113 decode_path=decode_path,
3114 value=str(self) if self.ready else None,
3115 optional=self.optional,
3116 default=self == self.default,
3117 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3118 expl=None if self._expl is None else tag_decode(self._expl),
3123 expl_offset=self.expl_offset if self.expled else None,
3124 expl_tlen=self.expl_tlen if self.expled else None,
3125 expl_llen=self.expl_llen if self.expled else None,
3126 expl_vlen=self.expl_vlen if self.expled else None,
3127 expl_lenindef=self.expl_lenindef,
3131 class Enumerated(Integer):
3132 """``ENUMERATED`` integer type
3134 This type is identical to :py:class:`pyderasn.Integer`, but requires
3135 schema to be specified and does not accept values missing from it.
3138 tag_default = tag_encode(10)
3139 asn1_type_name = "ENUMERATED"
3150 bounds=None, # dummy argument, workability for Integer.decode
3152 super(Enumerated, self).__init__(
3161 if len(self.specs) == 0:
3162 raise ValueError("schema must be specified")
3164 def _value_sanitize(self, value):
3165 if isinstance(value, self.__class__):
3166 value = value._value
3167 elif isinstance(value, integer_types):
3168 if value not in list(self.specs.values()):
3170 "unknown integer value: %s" % value,
3171 klass=self.__class__,
3173 elif isinstance(value, string_types):
3174 value = self.specs.get(value)
3176 raise ObjUnknown("integer value: %s" % value)
3178 raise InvalidValueType((self.__class__, int, str))
3182 obj = self.__class__(_specs=self.specs)
3183 obj._value = self._value
3184 obj._bound_min = self._bound_min
3185 obj._bound_max = self._bound_max
3187 obj._expl = self._expl
3188 obj.default = self.default
3189 obj.optional = self.optional
3190 obj.offset = self.offset
3191 obj.llen = self.llen
3192 obj.vlen = self.vlen
3204 return self.__class__(
3206 impl=self.tag if impl is None else impl,
3207 expl=self._expl if expl is None else expl,
3208 default=self.default if default is None else default,
3209 optional=self.optional if optional is None else optional,
3214 class CommonString(OctetString):
3215 """Common class for all strings
3217 Everything resembles :py:class:`pyderasn.OctetString`, except
3218 ability to deal with unicode text strings.
3220 >>> hexenc("привет мир".encode("utf-8"))
3221 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3222 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3224 >>> s = UTF8String("привет мир")
3225 UTF8String UTF8String привет мир
3227 'привет мир'
3228 >>> hexenc(bytes(s))
3229 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3231 >>> PrintableString("привет мир")
3232 Traceback (most recent call last):
3233 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3235 >>> BMPString("ада", bounds=(2, 2))
3236 Traceback (most recent call last):
3237 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3238 >>> s = BMPString("ад", bounds=(2, 2))
3241 >>> hexenc(bytes(s))
3249 * - :py:class:`pyderasn.UTF8String`
3251 * - :py:class:`pyderasn.NumericString`
3253 * - :py:class:`pyderasn.PrintableString`
3255 * - :py:class:`pyderasn.TeletexString`
3257 * - :py:class:`pyderasn.T61String`
3259 * - :py:class:`pyderasn.VideotexString`
3261 * - :py:class:`pyderasn.IA5String`
3263 * - :py:class:`pyderasn.GraphicString`
3265 * - :py:class:`pyderasn.VisibleString`
3267 * - :py:class:`pyderasn.ISO646String`
3269 * - :py:class:`pyderasn.GeneralString`
3271 * - :py:class:`pyderasn.UniversalString`
3273 * - :py:class:`pyderasn.BMPString`
3276 __slots__ = ("encoding",)
3278 def _value_sanitize(self, value):
3280 value_decoded = None
3281 if isinstance(value, self.__class__):
3282 value_raw = value._value
3283 elif isinstance(value, text_type):
3284 value_decoded = value
3285 elif isinstance(value, binary_type):
3288 raise InvalidValueType((self.__class__, text_type, binary_type))
3291 value_decoded.encode(self.encoding)
3292 if value_raw is None else value_raw
3295 value_raw.decode(self.encoding)
3296 if value_decoded is None else value_decoded
3298 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3299 raise DecodeError(str(err))
3300 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3308 def __eq__(self, their):
3309 if isinstance(their, binary_type):
3310 return self._value == their
3311 if isinstance(their, text_type):
3312 return self._value == their.encode(self.encoding)
3313 if not isinstance(their, self.__class__):
3316 self._value == their._value and
3317 self.tag == their.tag and
3318 self._expl == their._expl
3321 def __unicode__(self):
3323 return self._value.decode(self.encoding)
3324 return text_type(self._value)
3327 return pp_console_row(next(self.pps(no_unicode=PY2)))
3329 def pps(self, decode_path=(), no_unicode=False):
3332 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3334 asn1_type_name=self.asn1_type_name,
3335 obj_name=self.__class__.__name__,
3336 decode_path=decode_path,
3338 optional=self.optional,
3339 default=self == self.default,
3340 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3341 expl=None if self._expl is None else tag_decode(self._expl),
3346 expl_offset=self.expl_offset if self.expled else None,
3347 expl_tlen=self.expl_tlen if self.expled else None,
3348 expl_llen=self.expl_llen if self.expled else None,
3349 expl_vlen=self.expl_vlen if self.expled else None,
3350 expl_lenindef=self.expl_lenindef,
3354 class UTF8String(CommonString):
3356 tag_default = tag_encode(12)
3358 asn1_type_name = "UTF8String"
3361 class NumericString(CommonString):
3364 Its value is properly sanitized: only ASCII digits can be stored.
3367 tag_default = tag_encode(18)
3369 asn1_type_name = "NumericString"
3370 allowable_chars = set(digits.encode("ascii"))
3372 def _value_sanitize(self, value):
3373 value = super(NumericString, self)._value_sanitize(value)
3374 if not set(value) <= self.allowable_chars:
3375 raise DecodeError("non-numeric value")
3379 class PrintableString(CommonString):
3381 tag_default = tag_encode(19)
3383 asn1_type_name = "PrintableString"
3386 class TeletexString(CommonString):
3388 tag_default = tag_encode(20)
3390 asn1_type_name = "TeletexString"
3393 class T61String(TeletexString):
3395 asn1_type_name = "T61String"
3398 class VideotexString(CommonString):
3400 tag_default = tag_encode(21)
3401 encoding = "iso-8859-1"
3402 asn1_type_name = "VideotexString"
3405 class IA5String(CommonString):
3407 tag_default = tag_encode(22)
3409 asn1_type_name = "IA5"
3412 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3413 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3414 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3417 class UTCTime(CommonString):
3418 """``UTCTime`` datetime type
3420 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3421 UTCTime UTCTime 2017-09-30T22:07:50
3427 datetime.datetime(2017, 9, 30, 22, 7, 50)
3428 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3429 datetime.datetime(1957, 9, 30, 22, 7, 50)
3432 tag_default = tag_encode(23)
3434 asn1_type_name = "UTCTime"
3436 fmt = "%y%m%d%H%M%SZ"
3446 bounds=None, # dummy argument, workability for OctetString.decode
3449 :param value: set the value. Either datetime type, or
3450 :py:class:`pyderasn.UTCTime` object
3451 :param bytes impl: override default tag with ``IMPLICIT`` one
3452 :param bytes expl: override default tag with ``EXPLICIT`` one
3453 :param default: set default value. Type same as in ``value``
3454 :param bool optional: is object ``OPTIONAL`` in sequence
3456 super(UTCTime, self).__init__(
3464 if value is not None:
3465 self._value = self._value_sanitize(value)
3466 if default is not None:
3467 default = self._value_sanitize(default)
3468 self.default = self.__class__(
3473 if self._value is None:
3474 self._value = default
3476 def _value_sanitize(self, value):
3477 if isinstance(value, self.__class__):
3479 if isinstance(value, datetime):
3480 return value.strftime(self.fmt).encode("ascii")
3481 if isinstance(value, binary_type):
3482 value_decoded = value.decode("ascii")
3483 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3485 datetime.strptime(value_decoded, self.fmt)
3487 raise DecodeError("invalid UTCTime format")
3490 raise DecodeError("invalid UTCTime length")
3491 raise InvalidValueType((self.__class__, datetime))
3493 def __eq__(self, their):
3494 if isinstance(their, binary_type):
3495 return self._value == their
3496 if isinstance(their, datetime):
3497 return self.todatetime() == their
3498 if not isinstance(their, self.__class__):
3501 self._value == their._value and
3502 self.tag == their.tag and
3503 self._expl == their._expl
3506 def todatetime(self):
3507 """Convert to datetime
3511 Pay attention that UTCTime can not hold full year, so all years
3512 having < 50 years are treated as 20xx, 19xx otherwise, according
3513 to X.509 recomendation.
3515 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3516 year = value.year % 100
3518 year=(2000 + year) if year < 50 else (1900 + year),
3522 minute=value.minute,
3523 second=value.second,
3527 return pp_console_row(next(self.pps()))
3529 def pps(self, decode_path=()):
3531 asn1_type_name=self.asn1_type_name,
3532 obj_name=self.__class__.__name__,
3533 decode_path=decode_path,
3534 value=self.todatetime().isoformat() if self.ready else None,
3535 optional=self.optional,
3536 default=self == self.default,
3537 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3538 expl=None if self._expl is None else tag_decode(self._expl),
3543 expl_offset=self.expl_offset if self.expled else None,
3544 expl_tlen=self.expl_tlen if self.expled else None,
3545 expl_llen=self.expl_llen if self.expled else None,
3546 expl_vlen=self.expl_vlen if self.expled else None,
3547 expl_lenindef=self.expl_lenindef,
3551 class GeneralizedTime(UTCTime):
3552 """``GeneralizedTime`` datetime type
3554 This type is similar to :py:class:`pyderasn.UTCTime`.
3556 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3557 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3559 '20170930220750.000123Z'
3560 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3561 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3564 tag_default = tag_encode(24)
3565 asn1_type_name = "GeneralizedTime"
3567 fmt = "%Y%m%d%H%M%SZ"
3568 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3570 def _value_sanitize(self, value):
3571 if isinstance(value, self.__class__):
3573 if isinstance(value, datetime):
3574 return value.strftime(
3575 self.fmt_ms if value.microsecond > 0 else self.fmt
3577 if isinstance(value, binary_type):
3578 value_decoded = value.decode("ascii")
3579 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3581 datetime.strptime(value_decoded, self.fmt)
3584 "invalid GeneralizedTime (without ms) format",
3587 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3589 datetime.strptime(value_decoded, self.fmt_ms)
3592 "invalid GeneralizedTime (with ms) format",
3597 "invalid GeneralizedTime length",
3598 klass=self.__class__,
3600 raise InvalidValueType((self.__class__, datetime))
3602 def todatetime(self):
3603 value = self._value.decode("ascii")
3604 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3605 return datetime.strptime(value, self.fmt)
3606 return datetime.strptime(value, self.fmt_ms)
3609 class GraphicString(CommonString):
3611 tag_default = tag_encode(25)
3612 encoding = "iso-8859-1"
3613 asn1_type_name = "GraphicString"
3616 class VisibleString(CommonString):
3618 tag_default = tag_encode(26)
3620 asn1_type_name = "VisibleString"
3623 class ISO646String(VisibleString):
3625 asn1_type_name = "ISO646String"
3628 class GeneralString(CommonString):
3630 tag_default = tag_encode(27)
3631 encoding = "iso-8859-1"
3632 asn1_type_name = "GeneralString"
3635 class UniversalString(CommonString):
3637 tag_default = tag_encode(28)
3638 encoding = "utf-32-be"
3639 asn1_type_name = "UniversalString"
3642 class BMPString(CommonString):
3644 tag_default = tag_encode(30)
3645 encoding = "utf-16-be"
3646 asn1_type_name = "BMPString"
3650 """``CHOICE`` special type
3654 class GeneralName(Choice):
3656 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3657 ("dNSName", IA5String(impl=tag_ctxp(2))),
3660 >>> gn = GeneralName()
3662 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3663 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3664 >>> gn["dNSName"] = IA5String("bar.baz")
3665 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3666 >>> gn["rfc822Name"]
3669 [2] IA5String IA5 bar.baz
3672 >>> gn.value == gn["dNSName"]
3675 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3677 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3678 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3680 __slots__ = ("specs",)
3682 asn1_type_name = "CHOICE"
3695 :param value: set the value. Either ``(choice, value)`` tuple, or
3696 :py:class:`pyderasn.Choice` object
3697 :param bytes impl: can not be set, do **not** use it
3698 :param bytes expl: override default tag with ``EXPLICIT`` one
3699 :param default: set default value. Type same as in ``value``
3700 :param bool optional: is object ``OPTIONAL`` in sequence
3702 if impl is not None:
3703 raise ValueError("no implicit tag allowed for CHOICE")
3704 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3706 schema = getattr(self, "schema", ())
3707 if len(schema) == 0:
3708 raise ValueError("schema must be specified")
3710 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3713 if value is not None:
3714 self._value = self._value_sanitize(value)
3715 if default is not None:
3716 default_value = self._value_sanitize(default)
3717 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3718 default_obj.specs = self.specs
3719 default_obj._value = default_value
3720 self.default = default_obj
3722 self._value = default_obj.copy()._value
3724 def _value_sanitize(self, value):
3725 if isinstance(value, self.__class__):
3727 if isinstance(value, tuple) and len(value) == 2:
3729 spec = self.specs.get(choice)
3731 raise ObjUnknown(choice)
3732 if not isinstance(obj, spec.__class__):
3733 raise InvalidValueType((spec,))
3734 return (choice, spec(obj))
3735 raise InvalidValueType((self.__class__, tuple))
3739 return self._value is not None and self._value[1].ready
3742 obj = self.__class__(schema=self.specs)
3743 obj._expl = self._expl
3744 obj.default = self.default
3745 obj.optional = self.optional
3746 obj.offset = self.offset
3747 obj.llen = self.llen
3748 obj.vlen = self.vlen
3750 if value is not None:
3751 obj._value = (value[0], value[1].copy())
3754 def __eq__(self, their):
3755 if isinstance(their, tuple) and len(their) == 2:
3756 return self._value == their
3757 if not isinstance(their, self.__class__):
3760 self.specs == their.specs and
3761 self._value == their._value
3771 return self.__class__(
3774 expl=self._expl if expl is None else expl,
3775 default=self.default if default is None else default,
3776 optional=self.optional if optional is None else optional,
3781 self._assert_ready()
3782 return self._value[0]
3786 self._assert_ready()
3787 return self._value[1]
3789 def __getitem__(self, key):
3790 if key not in self.specs:
3791 raise ObjUnknown(key)
3792 if self._value is None:
3794 choice, value = self._value
3799 def __setitem__(self, key, value):
3800 spec = self.specs.get(key)
3802 raise ObjUnknown(key)
3803 if not isinstance(value, spec.__class__):
3804 raise InvalidValueType((spec.__class__,))
3805 self._value = (key, spec(value))
3813 return self._value[1].decoded if self.ready else False
3816 self._assert_ready()
3817 return self._value[1].encode()
3819 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3820 for choice, spec in self.specs.items():
3821 sub_decode_path = decode_path + (choice,)
3827 decode_path=sub_decode_path,
3836 klass=self.__class__,
3837 decode_path=decode_path,
3842 value, tail = spec.decode(
3846 decode_path=sub_decode_path,
3849 obj = self.__class__(
3852 default=self.default,
3853 optional=self.optional,
3854 _decoded=(offset, 0, value.tlvlen),
3856 obj._value = (choice, value)
3860 value = pp_console_row(next(self.pps()))
3862 value = "%s[%r]" % (value, self.value)
3865 def pps(self, decode_path=()):
3867 asn1_type_name=self.asn1_type_name,
3868 obj_name=self.__class__.__name__,
3869 decode_path=decode_path,
3870 value=self.choice if self.ready else None,
3871 optional=self.optional,
3872 default=self == self.default,
3873 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3874 expl=None if self._expl is None else tag_decode(self._expl),
3879 expl_lenindef=self.expl_lenindef,
3882 yield self.value.pps(decode_path=decode_path + (self.choice,))
3885 class PrimitiveTypes(Choice):
3886 """Predefined ``CHOICE`` for all generic primitive types
3888 It could be useful for general decoding of some unspecified values:
3890 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3891 OCTET STRING 3 bytes 666f6f
3892 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3896 schema = tuple((klass.__name__, klass()) for klass in (
3921 """``ANY`` special type
3923 >>> Any(Integer(-123))
3925 >>> a = Any(OctetString(b"hello world").encode())
3926 ANY 040b68656c6c6f20776f726c64
3927 >>> hexenc(bytes(a))
3928 b'0x040x0bhello world'
3930 __slots__ = ("defined",)
3931 tag_default = tag_encode(0)
3932 asn1_type_name = "ANY"
3942 :param value: set the value. Either any kind of pyderasn's
3943 **ready** object, or bytes. Pay attention that
3944 **no** validation is performed is raw binary value
3946 :param bytes expl: override default tag with ``EXPLICIT`` one
3947 :param bool optional: is object ``OPTIONAL`` in sequence
3949 super(Any, self).__init__(None, expl, None, optional, _decoded)
3950 self._value = None if value is None else self._value_sanitize(value)
3953 def _value_sanitize(self, value):
3954 if isinstance(value, self.__class__):
3956 if isinstance(value, Obj):
3957 return value.encode()
3958 if isinstance(value, binary_type):
3960 raise InvalidValueType((self.__class__, Obj, binary_type))
3964 return self._value is not None
3967 obj = self.__class__()
3968 obj._value = self._value
3970 obj._expl = self._expl
3971 obj.optional = self.optional
3972 obj.offset = self.offset
3973 obj.llen = self.llen
3974 obj.vlen = self.vlen
3977 def __eq__(self, their):
3978 if isinstance(their, binary_type):
3979 return self._value == their
3980 if issubclass(their.__class__, Any):
3981 return self._value == their._value
3990 return self.__class__(
3992 expl=self._expl if expl is None else expl,
3993 optional=self.optional if optional is None else optional,
3996 def __bytes__(self):
3997 self._assert_ready()
4005 self._assert_ready()
4008 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4010 t, tlen, lv = tag_strip(tlv)
4011 except DecodeError as err:
4012 raise err.__class__(
4014 klass=self.__class__,
4015 decode_path=decode_path,
4019 l, llen, v = len_decode(lv)
4020 except LenIndefForm as err:
4021 if not ctx.get("bered", False):
4022 raise err.__class__(
4024 klass=self.__class__,
4025 decode_path=decode_path,
4028 llen, vlen, v = 1, 0, lv[1:]
4029 sub_offset = offset + tlen + llen
4032 if v[:EOC_LEN].tobytes() == EOC:
4033 tlvlen = tlen + llen + vlen + EOC_LEN
4034 obj = self.__class__(
4035 value=tlv[:tlvlen].tobytes(),
4037 optional=self.optional,
4038 _decoded=(offset, 0, tlvlen),
4042 return obj, v[EOC_LEN:]
4044 chunk, v = Any().decode(
4047 decode_path=decode_path + (str(chunk_i),),
4051 vlen += chunk.tlvlen
4052 sub_offset += chunk.tlvlen
4054 except DecodeError as err:
4055 raise err.__class__(
4057 klass=self.__class__,
4058 decode_path=decode_path,
4062 raise NotEnoughData(
4063 "encoded length is longer than data",
4064 klass=self.__class__,
4065 decode_path=decode_path,
4068 tlvlen = tlen + llen + l
4069 v, tail = tlv[:tlvlen], v[l:]
4070 obj = self.__class__(
4073 optional=self.optional,
4074 _decoded=(offset, 0, tlvlen),
4080 return pp_console_row(next(self.pps()))
4082 def pps(self, decode_path=()):
4084 asn1_type_name=self.asn1_type_name,
4085 obj_name=self.__class__.__name__,
4086 decode_path=decode_path,
4087 blob=self._value if self.ready else None,
4088 optional=self.optional,
4089 default=self == self.default,
4090 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4091 expl=None if self._expl is None else tag_decode(self._expl),
4096 expl_offset=self.expl_offset if self.expled else None,
4097 expl_tlen=self.expl_tlen if self.expled else None,
4098 expl_llen=self.expl_llen if self.expled else None,
4099 expl_vlen=self.expl_vlen if self.expled else None,
4100 expl_lenindef=self.expl_lenindef,
4101 lenindef=self.lenindef,
4103 defined_by, defined = self.defined or (None, None)
4104 if defined_by is not None:
4106 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4110 ########################################################################
4111 # ASN.1 constructed types
4112 ########################################################################
4114 def get_def_by_path(defines_by_path, sub_decode_path):
4115 """Get define by decode path
4117 for path, define in defines_by_path:
4118 if len(path) != len(sub_decode_path):
4120 for p1, p2 in zip(path, sub_decode_path):
4121 if (p1 != any) and (p1 != p2):
4127 def abs_decode_path(decode_path, rel_path):
4128 """Create an absolute decode path from current and relative ones
4130 :param decode_path: current decode path, starting point.
4132 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4133 If first tuple's element is "/", then treat it as
4134 an absolute path, ignoring ``decode_path`` as
4135 starting point. Also this tuple can contain ".."
4136 elements, stripping the leading element from
4139 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4140 ("foo", "bar", "baz", "whatever")
4141 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4143 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4146 if rel_path[0] == "/":
4148 if rel_path[0] == "..":
4149 return abs_decode_path(decode_path[:-1], rel_path[1:])
4150 return decode_path + rel_path
4153 class Sequence(Obj):
4154 """``SEQUENCE`` structure type
4156 You have to make specification of sequence::
4158 class Extension(Sequence):
4160 ("extnID", ObjectIdentifier()),
4161 ("critical", Boolean(default=False)),
4162 ("extnValue", OctetString()),
4165 Then, you can work with it as with dictionary.
4167 >>> ext = Extension()
4168 >>> Extension().specs
4170 ('extnID', OBJECT IDENTIFIER),
4171 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4172 ('extnValue', OCTET STRING),
4174 >>> ext["extnID"] = "1.2.3"
4175 Traceback (most recent call last):
4176 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4177 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4179 You can determine if sequence is ready to be encoded:
4184 Traceback (most recent call last):
4185 pyderasn.ObjNotReady: object is not ready: extnValue
4186 >>> ext["extnValue"] = OctetString(b"foobar")
4190 Value you want to assign, must have the same **type** as in
4191 corresponding specification, but it can have different tags,
4192 optional/default attributes -- they will be taken from specification
4195 class TBSCertificate(Sequence):
4197 ("version", Version(expl=tag_ctxc(0), default="v1")),
4200 >>> tbs = TBSCertificate()
4201 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4203 Assign ``None`` to remove value from sequence.
4205 You can set values in Sequence during its initialization:
4207 >>> AlgorithmIdentifier((
4208 ("algorithm", ObjectIdentifier("1.2.3")),
4209 ("parameters", Any(Null()))
4211 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4213 You can determine if value exists/set in the sequence and take its value:
4215 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4218 OBJECT IDENTIFIER 1.2.3
4220 But pay attention that if value has default, then it won't be (not
4221 in) in the sequence (because ``DEFAULT`` must not be encoded in
4222 DER), but you can read its value:
4224 >>> "critical" in ext, ext["critical"]
4225 (False, BOOLEAN False)
4226 >>> ext["critical"] = Boolean(True)
4227 >>> "critical" in ext, ext["critical"]
4228 (True, BOOLEAN True)
4230 All defaulted values are always optional.
4232 .. _strict_default_existence_ctx:
4236 When decoded DER contains defaulted value inside, then
4237 technically this is not valid DER encoding. But we allow and pass
4238 it **by default**. Of course reencoding of that kind of DER will
4239 result in different binary representation (validly without
4240 defaulted value inside). You can enable strict defaulted values
4241 existence validation by setting ``"strict_default_existence":
4242 True`` :ref:`context <ctx>` option -- decoding process will raise
4243 an exception if defaulted value is met.
4245 Two sequences are equal if they have equal specification (schema),
4246 implicit/explicit tagging and the same values.
4248 __slots__ = ("specs",)
4249 tag_default = tag_encode(form=TagFormConstructed, num=16)
4250 asn1_type_name = "SEQUENCE"
4262 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4264 schema = getattr(self, "schema", ())
4266 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4269 if value is not None:
4270 if issubclass(value.__class__, Sequence):
4271 self._value = value._value
4272 elif hasattr(value, "__iter__"):
4273 for seq_key, seq_value in value:
4274 self[seq_key] = seq_value
4276 raise InvalidValueType((Sequence,))
4277 if default is not None:
4278 if not issubclass(default.__class__, Sequence):
4279 raise InvalidValueType((Sequence,))
4280 default_value = default._value
4281 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4282 default_obj.specs = self.specs
4283 default_obj._value = default_value
4284 self.default = default_obj
4286 self._value = default_obj.copy()._value
4290 for name, spec in self.specs.items():
4291 value = self._value.get(name)
4302 obj = self.__class__(schema=self.specs)
4304 obj._expl = self._expl
4305 obj.default = self.default
4306 obj.optional = self.optional
4307 obj.offset = self.offset
4308 obj.llen = self.llen
4309 obj.vlen = self.vlen
4310 obj._value = {k: v.copy() for k, v in self._value.items()}
4313 def __eq__(self, their):
4314 if not isinstance(their, self.__class__):
4317 self.specs == their.specs and
4318 self.tag == their.tag and
4319 self._expl == their._expl and
4320 self._value == their._value
4331 return self.__class__(
4334 impl=self.tag if impl is None else impl,
4335 expl=self._expl if expl is None else expl,
4336 default=self.default if default is None else default,
4337 optional=self.optional if optional is None else optional,
4340 def __contains__(self, key):
4341 return key in self._value
4343 def __setitem__(self, key, value):
4344 spec = self.specs.get(key)
4346 raise ObjUnknown(key)
4348 self._value.pop(key, None)
4350 if not isinstance(value, spec.__class__):
4351 raise InvalidValueType((spec.__class__,))
4352 value = spec(value=value)
4353 if spec.default is not None and value == spec.default:
4354 self._value.pop(key, None)
4356 self._value[key] = value
4358 def __getitem__(self, key):
4359 value = self._value.get(key)
4360 if value is not None:
4362 spec = self.specs.get(key)
4364 raise ObjUnknown(key)
4365 if spec.default is not None:
4369 def _encoded_values(self):
4371 for name, spec in self.specs.items():
4372 value = self._value.get(name)
4376 raise ObjNotReady(name)
4377 raws.append(value.encode())
4381 v = b"".join(self._encoded_values())
4382 return b"".join((self.tag, len_encode(len(v)), v))
4384 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4386 t, tlen, lv = tag_strip(tlv)
4387 except DecodeError as err:
4388 raise err.__class__(
4390 klass=self.__class__,
4391 decode_path=decode_path,
4396 klass=self.__class__,
4397 decode_path=decode_path,
4404 l, llen, v = len_decode(lv)
4405 except LenIndefForm as err:
4406 if not ctx.get("bered", False):
4407 raise err.__class__(
4409 klass=self.__class__,
4410 decode_path=decode_path,
4413 l, llen, v = 0, 1, lv[1:]
4415 except DecodeError as err:
4416 raise err.__class__(
4418 klass=self.__class__,
4419 decode_path=decode_path,
4423 raise NotEnoughData(
4424 "encoded length is longer than data",
4425 klass=self.__class__,
4426 decode_path=decode_path,
4430 v, tail = v[:l], v[l:]
4432 sub_offset = offset + tlen + llen
4434 for name, spec in self.specs.items():
4435 if spec.optional and (
4436 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4440 sub_decode_path = decode_path + (name,)
4442 value, v_tail = spec.decode(
4446 decode_path=sub_decode_path,
4454 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4455 if defined is not None:
4456 defined_by, defined_spec = defined
4457 if issubclass(value.__class__, SequenceOf):
4458 for i, _value in enumerate(value):
4459 sub_sub_decode_path = sub_decode_path + (
4461 DecodePathDefBy(defined_by),
4463 defined_value, defined_tail = defined_spec.decode(
4464 memoryview(bytes(_value)),
4466 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4467 if value.expled else (value.tlen + value.llen)
4470 decode_path=sub_sub_decode_path,
4473 if len(defined_tail) > 0:
4476 klass=self.__class__,
4477 decode_path=sub_sub_decode_path,
4480 _value.defined = (defined_by, defined_value)
4482 defined_value, defined_tail = defined_spec.decode(
4483 memoryview(bytes(value)),
4485 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4486 if value.expled else (value.tlen + value.llen)
4489 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4492 if len(defined_tail) > 0:
4495 klass=self.__class__,
4496 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4499 value.defined = (defined_by, defined_value)
4501 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4503 sub_offset += value_len
4505 if spec.default is not None and value == spec.default:
4506 if ctx.get("strict_default_existence", False):
4508 "DEFAULT value met",
4509 klass=self.__class__,
4510 decode_path=sub_decode_path,
4515 values[name] = value
4517 spec_defines = getattr(spec, "defines", ())
4518 if len(spec_defines) == 0:
4519 defines_by_path = ctx.get("defines_by_path", ())
4520 if len(defines_by_path) > 0:
4521 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4522 if spec_defines is not None and len(spec_defines) > 0:
4523 for rel_path, schema in spec_defines:
4524 defined = schema.get(value, None)
4525 if defined is not None:
4526 ctx.setdefault("defines", []).append((
4527 abs_decode_path(sub_decode_path[:-1], rel_path),
4531 if v[:EOC_LEN].tobytes() != EOC:
4534 klass=self.__class__,
4535 decode_path=decode_path,
4543 klass=self.__class__,
4544 decode_path=decode_path,
4547 obj = self.__class__(
4551 default=self.default,
4552 optional=self.optional,
4553 _decoded=(offset, llen, vlen),
4556 obj.lenindef = lenindef
4560 value = pp_console_row(next(self.pps()))
4562 for name in self.specs:
4563 _value = self._value.get(name)
4566 cols.append(repr(_value))
4567 return "%s[%s]" % (value, ", ".join(cols))
4569 def pps(self, decode_path=()):
4571 asn1_type_name=self.asn1_type_name,
4572 obj_name=self.__class__.__name__,
4573 decode_path=decode_path,
4574 optional=self.optional,
4575 default=self == self.default,
4576 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4577 expl=None if self._expl is None else tag_decode(self._expl),
4582 expl_offset=self.expl_offset if self.expled else None,
4583 expl_tlen=self.expl_tlen if self.expled else None,
4584 expl_llen=self.expl_llen if self.expled else None,
4585 expl_vlen=self.expl_vlen if self.expled else None,
4586 expl_lenindef=self.expl_lenindef,
4587 lenindef=self.lenindef,
4589 for name in self.specs:
4590 value = self._value.get(name)
4593 yield value.pps(decode_path=decode_path + (name,))
4596 class Set(Sequence):
4597 """``SET`` structure type
4599 Its usage is identical to :py:class:`pyderasn.Sequence`.
4602 tag_default = tag_encode(form=TagFormConstructed, num=17)
4603 asn1_type_name = "SET"
4606 raws = self._encoded_values()
4609 return b"".join((self.tag, len_encode(len(v)), v))
4611 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4613 t, tlen, lv = tag_strip(tlv)
4614 except DecodeError as err:
4615 raise err.__class__(
4617 klass=self.__class__,
4618 decode_path=decode_path,
4623 klass=self.__class__,
4624 decode_path=decode_path,
4631 l, llen, v = len_decode(lv)
4632 except LenIndefForm as err:
4633 if not ctx.get("bered", False):
4634 raise err.__class__(
4636 klass=self.__class__,
4637 decode_path=decode_path,
4640 l, llen, v = 0, 1, lv[1:]
4642 except DecodeError as err:
4643 raise err.__class__(
4645 klass=self.__class__,
4646 decode_path=decode_path,
4650 raise NotEnoughData(
4651 "encoded length is longer than data",
4652 klass=self.__class__,
4656 v, tail = v[:l], v[l:]
4658 sub_offset = offset + tlen + llen
4660 specs_items = self.specs.items
4662 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4664 for name, spec in specs_items():
4665 sub_decode_path = decode_path + (name,)
4671 decode_path=sub_decode_path,
4680 klass=self.__class__,
4681 decode_path=decode_path,
4684 value, v_tail = spec.decode(
4688 decode_path=sub_decode_path,
4691 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4692 sub_offset += value_len
4695 if spec.default is None or value != spec.default: # pragma: no cover
4696 # SeqMixing.test_encoded_default_accepted covers that place
4697 values[name] = value
4698 obj = self.__class__(
4702 default=self.default,
4703 optional=self.optional,
4704 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4709 msg="not all values are ready",
4710 klass=self.__class__,
4711 decode_path=decode_path,
4714 obj.lenindef = lenindef
4715 return obj, (v[EOC_LEN:] if lenindef else tail)
4718 class SequenceOf(Obj):
4719 """``SEQUENCE OF`` sequence type
4721 For that kind of type you must specify the object it will carry on
4722 (bounds are for example here, not required)::
4724 class Ints(SequenceOf):
4729 >>> ints.append(Integer(123))
4730 >>> ints.append(Integer(234))
4732 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4733 >>> [int(i) for i in ints]
4735 >>> ints.append(Integer(345))
4736 Traceback (most recent call last):
4737 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4740 >>> ints[1] = Integer(345)
4742 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4744 Also you can initialize sequence with preinitialized values:
4746 >>> ints = Ints([Integer(123), Integer(234)])
4748 __slots__ = ("spec", "_bound_min", "_bound_max")
4749 tag_default = tag_encode(form=TagFormConstructed, num=16)
4750 asn1_type_name = "SEQUENCE OF"
4763 super(SequenceOf, self).__init__(
4771 schema = getattr(self, "schema", None)
4773 raise ValueError("schema must be specified")
4775 self._bound_min, self._bound_max = getattr(
4779 ) if bounds is None else bounds
4781 if value is not None:
4782 self._value = self._value_sanitize(value)
4783 if default is not None:
4784 default_value = self._value_sanitize(default)
4785 default_obj = self.__class__(
4790 default_obj._value = default_value
4791 self.default = default_obj
4793 self._value = default_obj.copy()._value
4795 def _value_sanitize(self, value):
4796 if issubclass(value.__class__, SequenceOf):
4797 value = value._value
4798 elif hasattr(value, "__iter__"):
4801 raise InvalidValueType((self.__class__, iter))
4802 if not self._bound_min <= len(value) <= self._bound_max:
4803 raise BoundsError(self._bound_min, len(value), self._bound_max)
4805 if not isinstance(v, self.spec.__class__):
4806 raise InvalidValueType((self.spec.__class__,))
4811 return all(v.ready for v in self._value)
4814 obj = self.__class__(schema=self.spec)
4815 obj._bound_min = self._bound_min
4816 obj._bound_max = self._bound_max
4818 obj._expl = self._expl
4819 obj.default = self.default
4820 obj.optional = self.optional
4821 obj.offset = self.offset
4822 obj.llen = self.llen
4823 obj.vlen = self.vlen
4824 obj._value = [v.copy() for v in self._value]
4827 def __eq__(self, their):
4828 if isinstance(their, self.__class__):
4830 self.spec == their.spec and
4831 self.tag == their.tag and
4832 self._expl == their._expl and
4833 self._value == their._value
4835 if hasattr(their, "__iter__"):
4836 return self._value == list(their)
4848 return self.__class__(
4852 (self._bound_min, self._bound_max)
4853 if bounds is None else bounds
4855 impl=self.tag if impl is None else impl,
4856 expl=self._expl if expl is None else expl,
4857 default=self.default if default is None else default,
4858 optional=self.optional if optional is None else optional,
4861 def __contains__(self, key):
4862 return key in self._value
4864 def append(self, value):
4865 if not isinstance(value, self.spec.__class__):
4866 raise InvalidValueType((self.spec.__class__,))
4867 if len(self._value) + 1 > self._bound_max:
4870 len(self._value) + 1,
4873 self._value.append(value)
4876 self._assert_ready()
4877 return iter(self._value)
4880 self._assert_ready()
4881 return len(self._value)
4883 def __setitem__(self, key, value):
4884 if not isinstance(value, self.spec.__class__):
4885 raise InvalidValueType((self.spec.__class__,))
4886 self._value[key] = self.spec(value=value)
4888 def __getitem__(self, key):
4889 return self._value[key]
4891 def _encoded_values(self):
4892 return [v.encode() for v in self._value]
4895 v = b"".join(self._encoded_values())
4896 return b"".join((self.tag, len_encode(len(v)), v))
4898 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4900 t, tlen, lv = tag_strip(tlv)
4901 except DecodeError as err:
4902 raise err.__class__(
4904 klass=self.__class__,
4905 decode_path=decode_path,
4910 klass=self.__class__,
4911 decode_path=decode_path,
4918 l, llen, v = len_decode(lv)
4919 except LenIndefForm as err:
4920 if not ctx.get("bered", False):
4921 raise err.__class__(
4923 klass=self.__class__,
4924 decode_path=decode_path,
4927 l, llen, v = 0, 1, lv[1:]
4929 except DecodeError as err:
4930 raise err.__class__(
4932 klass=self.__class__,
4933 decode_path=decode_path,
4937 raise NotEnoughData(
4938 "encoded length is longer than data",
4939 klass=self.__class__,
4940 decode_path=decode_path,
4944 v, tail = v[:l], v[l:]
4946 sub_offset = offset + tlen + llen
4950 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4952 value, v_tail = spec.decode(
4956 decode_path=decode_path + (str(len(_value)),),
4959 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4960 sub_offset += value_len
4963 _value.append(value)
4964 obj = self.__class__(
4967 bounds=(self._bound_min, self._bound_max),
4970 default=self.default,
4971 optional=self.optional,
4972 _decoded=(offset, llen, vlen),
4974 obj.lenindef = lenindef
4975 return obj, (v[EOC_LEN:] if lenindef else tail)
4979 pp_console_row(next(self.pps())),
4980 ", ".join(repr(v) for v in self._value),
4983 def pps(self, decode_path=()):
4985 asn1_type_name=self.asn1_type_name,
4986 obj_name=self.__class__.__name__,
4987 decode_path=decode_path,
4988 optional=self.optional,
4989 default=self == self.default,
4990 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4991 expl=None if self._expl is None else tag_decode(self._expl),
4996 expl_offset=self.expl_offset if self.expled else None,
4997 expl_tlen=self.expl_tlen if self.expled else None,
4998 expl_llen=self.expl_llen if self.expled else None,
4999 expl_vlen=self.expl_vlen if self.expled else None,
5000 expl_lenindef=self.expl_lenindef,
5001 lenindef=self.lenindef,
5003 for i, value in enumerate(self._value):
5004 yield value.pps(decode_path=decode_path + (str(i),))
5007 class SetOf(SequenceOf):
5008 """``SET OF`` sequence type
5010 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5013 tag_default = tag_encode(form=TagFormConstructed, num=17)
5014 asn1_type_name = "SET OF"
5017 raws = self._encoded_values()
5020 return b"".join((self.tag, len_encode(len(v)), v))
5023 def obj_by_path(pypath): # pragma: no cover
5024 """Import object specified as string Python path
5026 Modules must be separated from classes/functions with ``:``.
5028 >>> obj_by_path("foo.bar:Baz")
5029 <class 'foo.bar.Baz'>
5030 >>> obj_by_path("foo.bar:Baz.boo")
5031 <classmethod 'foo.bar.Baz.boo'>
5033 mod, objs = pypath.rsplit(":", 1)
5034 from importlib import import_module
5035 obj = import_module(mod)
5036 for obj_name in objs.split("."):
5037 obj = getattr(obj, obj_name)
5041 def generic_decoder(): # pragma: no cover
5042 # All of this below is a big hack with self references
5043 choice = PrimitiveTypes()
5044 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5045 choice.specs["SetOf"] = SetOf(schema=choice)
5047 choice.specs["SequenceOf%d" % i] = SequenceOf(
5051 choice.specs["Any"] = Any()
5053 # Class name equals to type name, to omit it from output
5054 class SEQUENCEOF(SequenceOf):
5058 def pprint_any(obj, oids=None, with_colours=False):
5059 def _pprint_pps(pps):
5061 if hasattr(pp, "_fields"):
5062 if pp.asn1_type_name == Choice.asn1_type_name:
5064 pp_kwargs = pp._asdict()
5065 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5066 pp = _pp(**pp_kwargs)
5067 yield pp_console_row(
5072 with_colours=with_colours,
5074 for row in pp_console_blob(pp):
5077 for row in _pprint_pps(pp):
5079 return "\n".join(_pprint_pps(obj.pps()))
5080 return SEQUENCEOF(), pprint_any
5083 def main(): # pragma: no cover
5085 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5086 parser.add_argument(
5090 help="Skip that number of bytes from the beginning",
5092 parser.add_argument(
5094 help="Python path to dictionary with OIDs",
5096 parser.add_argument(
5098 help="Python path to schema definition to use",
5100 parser.add_argument(
5101 "--defines-by-path",
5102 help="Python path to decoder's defines_by_path",
5104 parser.add_argument(
5106 action='store_true',
5107 help="Disallow BER encoding",
5109 parser.add_argument(
5111 type=argparse.FileType("rb"),
5112 help="Path to DER file you want to decode",
5114 args = parser.parse_args()
5115 args.DERFile.seek(args.skip)
5116 der = memoryview(args.DERFile.read())
5117 args.DERFile.close()
5118 oids = obj_by_path(args.oids) if args.oids else {}
5120 schema = obj_by_path(args.schema)
5121 from functools import partial
5122 pprinter = partial(pprint, big_blobs=True)
5124 schema, pprinter = generic_decoder()
5125 ctx = {"bered": not args.nobered}
5126 if args.defines_by_path is not None:
5127 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5128 obj, tail = schema().decode(der, ctx=ctx)
5132 with_colours=True if environ.get("NO_COLOR") is None else False,
5135 print("\nTrailing data: %s" % hexenc(tail))
5138 if __name__ == "__main__":