3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
203 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`bered <bered_ctx>`
210 * :ref:`defines_by_path <defines_by_path_ctx>`
211 * :ref:`strict_default_existence <strict_default_existence_ctx>`
218 All objects have ``pps()`` method, that is a generator of
219 :py:class:`pyderasn.PP` namedtuple, holding various raw information
220 about the object. If ``pps`` is called on sequences, then all underlying
221 ``PP`` will be yielded.
223 You can use :py:func:`pyderasn.pp_console_row` function, converting
224 those ``PP`` to human readable string. Actually exactly it is used for
225 all object ``repr``. But it is easy to write custom formatters.
227 >>> from pyderasn import pprint
228 >>> encoded = Integer(-12345).encode()
229 >>> obj, tail = Integer().decode(encoded)
230 >>> print(pprint(obj))
231 0 [1,1, 2] INTEGER -12345
238 ASN.1 structures often have ANY and OCTET STRING fields, that are
239 DEFINED BY some previously met ObjectIdentifier. This library provides
240 ability to specify mapping between some OID and field that must be
241 decoded with specific specification.
246 :py:class:`pyderasn.ObjectIdentifier` field inside
247 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
248 necessary for decoding structures. For example, CMS (:rfc:`5652`)
251 class ContentInfo(Sequence):
253 ("contentType", ContentType(defines=((("content",), {
254 id_digestedData: DigestedData(),
255 id_signedData: SignedData(),
257 ("content", Any(expl=tag_ctxc(0))),
260 ``contentType`` field tells that it defines that ``content`` must be
261 decoded with ``SignedData`` specification, if ``contentType`` equals to
262 ``id-signedData``. The same applies to ``DigestedData``. If
263 ``contentType`` contains unknown OID, then no automatic decoding is
266 You can specify multiple fields, that will be autodecoded -- that is why
267 ``defines`` kwarg is a sequence. You can specify defined field
268 relatively or absolutely to current decode path. For example ``defines``
269 for AlgorithmIdentifier of X.509's
270 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
274 id_ecPublicKey: ECParameters(),
275 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
277 (("..", "subjectPublicKey"), {
278 id_rsaEncryption: RSAPublicKey(),
279 id_GostR3410_2001: OctetString(),
283 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
284 autodecode its parameters inside SPKI's algorithm and its public key
287 Following types can be automatically decoded (DEFINED BY):
289 * :py:class:`pyderasn.Any`
290 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
291 * :py:class:`pyderasn.OctetString`
292 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
293 ``Any``/``BitString``/``OctetString``-s
295 When any of those fields is automatically decoded, then ``.defined``
296 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
297 was defined, ``value`` contains corresponding decoded value. For example
298 above, ``content_info["content"].defined == (id_signedData,
301 .. _defines_by_path_ctx:
303 defines_by_path context option
304 ______________________________
306 Sometimes you either can not or do not want to explicitly set *defines*
307 in the scheme. You can dynamically apply those definitions when calling
308 ``.decode()`` method.
310 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
311 value must be sequence of following tuples::
313 (decode_path, defines)
315 where ``decode_path`` is a tuple holding so-called decode path to the
316 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
317 ``defines``, holding exactly the same value as accepted in its keyword
320 For example, again for CMS, you want to automatically decode
321 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
322 structures it may hold. Also, automatically decode ``controlSequence``
325 content_info, tail = ContentInfo().decode(data, defines_by_path=(
328 ((("content",), {id_signedData: SignedData()}),),
333 DecodePathDefBy(id_signedData),
338 id_cct_PKIData: PKIData(),
339 id_cct_PKIResponse: PKIResponse(),
345 DecodePathDefBy(id_signedData),
348 DecodePathDefBy(id_cct_PKIResponse),
354 id_cmc_recipientNonce: RecipientNonce(),
355 id_cmc_senderNonce: SenderNonce(),
356 id_cmc_statusInfoV2: CMCStatusInfoV2(),
357 id_cmc_transactionId: TransactionId(),
362 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
363 First function is useful for path construction when some automatic
364 decoding is already done. ``any`` means literally any value it meet --
365 useful for SEQUENCE/SET OF-s.
374 Currently BER support is not extensively tested.
376 By default PyDERASN accepts only DER encoded data. It always encodes to
377 DER. But you can optionally enable BER decoding with setting ``bered``
378 :ref:`context <ctx>` argument to True. Indefinite lengths and
379 constructed primitive types should be parsed successfully.
381 * If object is encoded in BER form (not the DER one), then ``bered``
382 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
383 STRING`` can contain it.
384 * If object has an indefinite length encoding, then its ``lenindef``
385 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
386 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
388 * If object has an indefinite length encoded explicit tag, then
389 ``expl_lenindef`` is set to True.
391 EOC (end-of-contents) token's length is taken in advance in object's
399 .. autoclass:: pyderasn.Boolean
404 .. autoclass:: pyderasn.Integer
409 .. autoclass:: pyderasn.BitString
414 .. autoclass:: pyderasn.OctetString
419 .. autoclass:: pyderasn.Null
424 .. autoclass:: pyderasn.ObjectIdentifier
429 .. autoclass:: pyderasn.Enumerated
433 .. autoclass:: pyderasn.CommonString
437 .. autoclass:: pyderasn.NumericString
441 .. autoclass:: pyderasn.UTCTime
442 :members: __init__, todatetime
446 .. autoclass:: pyderasn.GeneralizedTime
453 .. autoclass:: pyderasn.Choice
458 .. autoclass:: PrimitiveTypes
462 .. autoclass:: pyderasn.Any
470 .. autoclass:: pyderasn.Sequence
475 .. autoclass:: pyderasn.Set
480 .. autoclass:: pyderasn.SequenceOf
485 .. autoclass:: pyderasn.SetOf
491 .. autofunction:: pyderasn.abs_decode_path
492 .. autofunction:: pyderasn.hexenc
493 .. autofunction:: pyderasn.hexdec
494 .. autofunction:: pyderasn.tag_encode
495 .. autofunction:: pyderasn.tag_decode
496 .. autofunction:: pyderasn.tag_ctxp
497 .. autofunction:: pyderasn.tag_ctxc
498 .. autoclass:: pyderasn.Obj
499 .. autoclass:: pyderasn.DecodeError
501 .. autoclass:: pyderasn.NotEnoughData
502 .. autoclass:: pyderasn.LenIndefForm
503 .. autoclass:: pyderasn.TagMismatch
504 .. autoclass:: pyderasn.InvalidLength
505 .. autoclass:: pyderasn.InvalidOID
506 .. autoclass:: pyderasn.ObjUnknown
507 .. autoclass:: pyderasn.ObjNotReady
508 .. autoclass:: pyderasn.InvalidValueType
509 .. autoclass:: pyderasn.BoundsError
512 from codecs import getdecoder
513 from codecs import getencoder
514 from collections import namedtuple
515 from collections import OrderedDict
516 from datetime import datetime
517 from math import ceil
518 from os import environ
519 from string import digits
521 from six import add_metaclass
522 from six import binary_type
523 from six import byte2int
524 from six import indexbytes
525 from six import int2byte
526 from six import integer_types
527 from six import iterbytes
529 from six import string_types
530 from six import text_type
531 from six.moves import xrange as six_xrange
535 from termcolor import colored
537 def colored(what, *args):
581 "TagClassApplication",
585 "TagFormConstructed",
596 TagClassUniversal = 0
597 TagClassApplication = 1 << 6
598 TagClassContext = 1 << 7
599 TagClassPrivate = 1 << 6 | 1 << 7
601 TagFormConstructed = 1 << 5
604 TagClassApplication: "APPLICATION ",
605 TagClassPrivate: "PRIVATE ",
606 TagClassUniversal: "UNIV ",
612 ########################################################################
614 ########################################################################
616 class DecodeError(Exception):
617 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
619 :param str msg: reason of decode failing
620 :param klass: optional exact DecodeError inherited class (like
621 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
622 :py:exc:`InvalidLength`)
623 :param decode_path: tuple of strings. It contains human
624 readable names of the fields through which
625 decoding process has passed
626 :param int offset: binary offset where failure happened
628 super(DecodeError, self).__init__()
631 self.decode_path = decode_path
637 "" if self.klass is None else self.klass.__name__,
639 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
640 if len(self.decode_path) > 0 else ""
642 ("(at %d)" % self.offset) if self.offset > 0 else "",
648 return "%s(%s)" % (self.__class__.__name__, self)
651 class NotEnoughData(DecodeError):
655 class LenIndefForm(DecodeError):
659 class TagMismatch(DecodeError):
663 class InvalidLength(DecodeError):
667 class InvalidOID(DecodeError):
671 class ObjUnknown(ValueError):
672 def __init__(self, name):
673 super(ObjUnknown, self).__init__()
677 return "object is unknown: %s" % self.name
680 return "%s(%s)" % (self.__class__.__name__, self)
683 class ObjNotReady(ValueError):
684 def __init__(self, name):
685 super(ObjNotReady, self).__init__()
689 return "object is not ready: %s" % self.name
692 return "%s(%s)" % (self.__class__.__name__, self)
695 class InvalidValueType(ValueError):
696 def __init__(self, expected_types):
697 super(InvalidValueType, self).__init__()
698 self.expected_types = expected_types
701 return "invalid value type, expected: %s" % ", ".join(
702 [repr(t) for t in self.expected_types]
706 return "%s(%s)" % (self.__class__.__name__, self)
709 class BoundsError(ValueError):
710 def __init__(self, bound_min, value, bound_max):
711 super(BoundsError, self).__init__()
712 self.bound_min = bound_min
714 self.bound_max = bound_max
717 return "unsatisfied bounds: %s <= %s <= %s" % (
724 return "%s(%s)" % (self.__class__.__name__, self)
727 ########################################################################
729 ########################################################################
731 _hexdecoder = getdecoder("hex")
732 _hexencoder = getencoder("hex")
736 """Binary data to hexadecimal string convert
738 return _hexdecoder(data)[0]
742 """Hexadecimal string to binary data convert
744 return _hexencoder(data)[0].decode("ascii")
747 def int_bytes_len(num, byte_len=8):
750 return int(ceil(float(num.bit_length()) / byte_len))
753 def zero_ended_encode(num):
754 octets = bytearray(int_bytes_len(num, 7))
756 octets[i] = num & 0x7F
760 octets[i] = 0x80 | (num & 0x7F)
766 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
767 """Encode tag to binary form
769 :param int num: tag's number
770 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
771 :py:data:`pyderasn.TagClassContext`,
772 :py:data:`pyderasn.TagClassApplication`,
773 :py:data:`pyderasn.TagClassPrivate`)
774 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
775 :py:data:`pyderasn.TagFormConstructed`)
779 return int2byte(klass | form | num)
780 # [XX|X|11111][1.......][1.......] ... [0.......]
781 return int2byte(klass | form | 31) + zero_ended_encode(num)
785 """Decode tag from binary form
789 No validation is performed, assuming that it has already passed.
791 It returns tuple with three integers, as
792 :py:func:`pyderasn.tag_encode` accepts.
794 first_octet = byte2int(tag)
795 klass = first_octet & 0xC0
796 form = first_octet & 0x20
797 if first_octet & 0x1F < 0x1F:
798 return (klass, form, first_octet & 0x1F)
800 for octet in iterbytes(tag[1:]):
803 return (klass, form, num)
807 """Create CONTEXT PRIMITIVE tag
809 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
813 """Create CONTEXT CONSTRUCTED tag
815 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
819 """Take off tag from the data
821 :returns: (encoded tag, tag length, remaining data)
824 raise NotEnoughData("no data at all")
825 if byte2int(data) & 0x1F < 31:
826 return data[:1], 1, data[1:]
831 raise DecodeError("unfinished tag")
832 if indexbytes(data, i) & 0x80 == 0:
835 return data[:i], i, data[i:]
841 octets = bytearray(int_bytes_len(l) + 1)
842 octets[0] = 0x80 | (len(octets) - 1)
843 for i in six_xrange(len(octets) - 1, 0, -1):
849 def len_decode(data):
852 :returns: (decoded length, length's length, remaining data)
853 :raises LenIndefForm: if indefinite form encoding is met
856 raise NotEnoughData("no data at all")
857 first_octet = byte2int(data)
858 if first_octet & 0x80 == 0:
859 return first_octet, 1, data[1:]
860 octets_num = first_octet & 0x7F
861 if octets_num + 1 > len(data):
862 raise NotEnoughData("encoded length is longer than data")
865 if byte2int(data[1:]) == 0:
866 raise DecodeError("leading zeros")
868 for v in iterbytes(data[1:1 + octets_num]):
871 raise DecodeError("long form instead of short one")
872 return l, 1 + octets_num, data[1 + octets_num:]
875 ########################################################################
877 ########################################################################
879 class AutoAddSlots(type):
880 def __new__(mcs, name, bases, _dict):
881 _dict["__slots__"] = _dict.get("__slots__", ())
882 return type.__new__(mcs, name, bases, _dict)
885 @add_metaclass(AutoAddSlots)
887 """Common ASN.1 object class
889 All ASN.1 types are inherited from it. It has metaclass that
890 automatically adds ``__slots__`` to all inherited classes.
914 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
915 self._expl = getattr(self, "expl", None) if expl is None else expl
916 if self.tag != self.tag_default and self._expl is not None:
917 raise ValueError("implicit and explicit tags can not be set simultaneously")
918 if default is not None:
920 self.optional = optional
921 self.offset, self.llen, self.vlen = _decoded
923 self.expl_lenindef = False
924 self.lenindef = False
928 def ready(self): # pragma: no cover
929 """Is object ready to be encoded?
931 raise NotImplementedError()
933 def _assert_ready(self):
935 raise ObjNotReady(self.__class__.__name__)
939 """Is object decoded?
941 return (self.llen + self.vlen) > 0
943 def copy(self): # pragma: no cover
944 """Make a copy of object, safe to be mutated
946 raise NotImplementedError()
954 return self.tlen + self.llen + self.vlen
956 def __str__(self): # pragma: no cover
957 return self.__bytes__() if PY2 else self.__unicode__()
959 def __ne__(self, their):
960 return not(self == their)
962 def __gt__(self, their): # pragma: no cover
963 return not(self < their)
965 def __le__(self, their): # pragma: no cover
966 return (self == their) or (self < their)
968 def __ge__(self, their): # pragma: no cover
969 return (self == their) or (self > their)
971 def _encode(self): # pragma: no cover
972 raise NotImplementedError()
974 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
975 raise NotImplementedError()
979 if self._expl is None:
981 return b"".join((self._expl, len_encode(len(raw)), raw))
994 :param data: either binary or memoryview
995 :param int offset: initial data's offset
996 :param bool leavemm: do we need to leave memoryview of remaining
997 data as is, or convert it to bytes otherwise
998 :param ctx: optional :ref:`context <ctx>` governing decoding process.
999 :param tag_only: decode only the tag, without length and contents
1000 (used only in Choice and Set structures, trying to
1001 determine if tag satisfies the scheme)
1002 :returns: (Obj, remaining data)
1006 tlv = memoryview(data)
1007 if self._expl is None:
1008 result = self._decode(
1011 decode_path=decode_path,
1020 t, tlen, lv = tag_strip(tlv)
1021 except DecodeError as err:
1022 raise err.__class__(
1024 klass=self.__class__,
1025 decode_path=decode_path,
1030 klass=self.__class__,
1031 decode_path=decode_path,
1035 l, llen, v = len_decode(lv)
1036 except LenIndefForm as err:
1037 if not ctx.get("bered", False):
1038 raise err.__class__(
1040 klass=self.__class__,
1041 decode_path=decode_path,
1045 offset += tlen + llen
1046 result = self._decode(
1049 decode_path=decode_path,
1056 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1057 if eoc_expected.tobytes() != EOC:
1060 decode_path=decode_path,
1064 obj.expl_lenindef = True
1065 except DecodeError as err:
1066 raise err.__class__(
1068 klass=self.__class__,
1069 decode_path=decode_path,
1074 raise NotEnoughData(
1075 "encoded length is longer than data",
1076 klass=self.__class__,
1077 decode_path=decode_path,
1080 result = self._decode(
1082 offset=offset + tlen + llen,
1083 decode_path=decode_path,
1090 return obj, (tail if leavemm else tail.tobytes())
1094 return self._expl is not None
1101 def expl_tlen(self):
1102 return len(self._expl)
1105 def expl_llen(self):
1106 if self.expl_lenindef:
1108 return len(len_encode(self.tlvlen))
1111 def expl_offset(self):
1112 return self.offset - self.expl_tlen - self.expl_llen
1115 def expl_vlen(self):
1119 def expl_tlvlen(self):
1120 return self.expl_tlen + self.expl_llen + self.expl_vlen
1123 class DecodePathDefBy(object):
1124 """DEFINED BY representation inside decode path
1126 __slots__ = ("defined_by",)
1128 def __init__(self, defined_by):
1129 self.defined_by = defined_by
1131 def __ne__(self, their):
1132 return not(self == their)
1134 def __eq__(self, their):
1135 if not isinstance(their, self.__class__):
1137 return self.defined_by == their.defined_by
1140 return "DEFINED BY " + str(self.defined_by)
1143 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1146 ########################################################################
1148 ########################################################################
1150 PP = namedtuple("PP", (
1175 asn1_type_name="unknown",
1192 expl_lenindef=False,
1220 def _colorize(what, colour, with_colours, attrs=("bold",)):
1221 return colored(what, colour, attrs=attrs) if with_colours else what
1236 " " if pp.expl_offset is None else
1237 ("-%d" % (pp.offset - pp.expl_offset))
1240 cols.append(_colorize(col, "red", with_colours, ()))
1241 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1242 col = _colorize(col, "green", with_colours, ())
1244 if pp.expl_lenindef:
1249 " " if ber_deoffset == 0 else
1250 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1253 if len(pp.decode_path) > 0:
1254 cols.append(" ." * (len(pp.decode_path)))
1255 ent = pp.decode_path[-1]
1256 if isinstance(ent, DecodePathDefBy):
1257 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1258 value = str(ent.defined_by)
1260 oids is not None and
1261 ent.defined_by.asn1_type_name ==
1262 ObjectIdentifier.asn1_type_name and
1265 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1267 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1269 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1270 if pp.expl is not None:
1271 klass, _, num = pp.expl
1272 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1273 cols.append(_colorize(col, "blue", with_colours))
1274 if pp.impl is not None:
1275 klass, _, num = pp.impl
1276 col = "[%s%d]" % (TagClassReprs[klass], num)
1277 cols.append(_colorize(col, "blue", with_colours))
1278 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1279 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1281 cols.append(_colorize("BER", "red", with_colours))
1282 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1283 if pp.value is not None:
1285 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1287 oids is not None and
1288 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1291 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1293 if isinstance(pp.blob, binary_type):
1294 cols.append(hexenc(pp.blob))
1295 elif isinstance(pp.blob, tuple):
1296 cols.append(", ".join(pp.blob))
1298 cols.append(_colorize("OPTIONAL", "red", with_colours))
1300 cols.append(_colorize("DEFAULT", "red", with_colours))
1301 return " ".join(cols)
1304 def pp_console_blob(pp):
1305 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1306 if len(pp.decode_path) > 0:
1307 cols.append(" ." * (len(pp.decode_path) + 1))
1308 if isinstance(pp.blob, binary_type):
1309 blob = hexenc(pp.blob).upper()
1310 for i in range(0, len(blob), 32):
1311 chunk = blob[i:i + 32]
1312 yield " ".join(cols + [":".join(
1313 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1315 elif isinstance(pp.blob, tuple):
1316 yield " ".join(cols + [", ".join(pp.blob)])
1319 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1320 """Pretty print object
1322 :param Obj obj: object you want to pretty print
1323 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1324 from it is met, then its humand readable form is printed
1325 :param big_blobs: if large binary objects are met (like OctetString
1326 values), do we need to print them too, on separate
1328 :param with_colours: colourize output, if ``termcolor`` library
1331 def _pprint_pps(pps):
1333 if hasattr(pp, "_fields"):
1335 yield pp_console_row(
1340 with_colours=with_colours,
1342 for row in pp_console_blob(pp):
1345 yield pp_console_row(
1350 with_colours=with_colours,
1353 for row in _pprint_pps(pp):
1355 return "\n".join(_pprint_pps(obj.pps()))
1358 ########################################################################
1359 # ASN.1 primitive types
1360 ########################################################################
1363 """``BOOLEAN`` boolean type
1365 >>> b = Boolean(True)
1367 >>> b == Boolean(True)
1373 tag_default = tag_encode(1)
1374 asn1_type_name = "BOOLEAN"
1386 :param value: set the value. Either boolean type, or
1387 :py:class:`pyderasn.Boolean` object
1388 :param bytes impl: override default tag with ``IMPLICIT`` one
1389 :param bytes expl: override default tag with ``EXPLICIT`` one
1390 :param default: set default value. Type same as in ``value``
1391 :param bool optional: is object ``OPTIONAL`` in sequence
1393 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1394 self._value = None if value is None else self._value_sanitize(value)
1395 if default is not None:
1396 default = self._value_sanitize(default)
1397 self.default = self.__class__(
1403 self._value = default
1405 def _value_sanitize(self, value):
1406 if issubclass(value.__class__, Boolean):
1408 if isinstance(value, bool):
1410 raise InvalidValueType((self.__class__, bool))
1414 return self._value is not None
1417 obj = self.__class__()
1418 obj._value = self._value
1420 obj._expl = self._expl
1421 obj.default = self.default
1422 obj.optional = self.optional
1423 obj.offset = self.offset
1424 obj.llen = self.llen
1425 obj.vlen = self.vlen
1428 def __nonzero__(self):
1429 self._assert_ready()
1433 self._assert_ready()
1436 def __eq__(self, their):
1437 if isinstance(their, bool):
1438 return self._value == their
1439 if not issubclass(their.__class__, Boolean):
1442 self._value == their._value and
1443 self.tag == their.tag and
1444 self._expl == their._expl
1455 return self.__class__(
1457 impl=self.tag if impl is None else impl,
1458 expl=self._expl if expl is None else expl,
1459 default=self.default if default is None else default,
1460 optional=self.optional if optional is None else optional,
1464 self._assert_ready()
1468 (b"\xFF" if self._value else b"\x00"),
1471 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1473 t, _, lv = tag_strip(tlv)
1474 except DecodeError as err:
1475 raise err.__class__(
1477 klass=self.__class__,
1478 decode_path=decode_path,
1483 klass=self.__class__,
1484 decode_path=decode_path,
1490 l, _, v = len_decode(lv)
1491 except DecodeError as err:
1492 raise err.__class__(
1494 klass=self.__class__,
1495 decode_path=decode_path,
1499 raise InvalidLength(
1500 "Boolean's length must be equal to 1",
1501 klass=self.__class__,
1502 decode_path=decode_path,
1506 raise NotEnoughData(
1507 "encoded length is longer than data",
1508 klass=self.__class__,
1509 decode_path=decode_path,
1512 first_octet = byte2int(v)
1514 if first_octet == 0:
1516 elif first_octet == 0xFF:
1518 elif ctx.get("bered", False):
1523 "unacceptable Boolean value",
1524 klass=self.__class__,
1525 decode_path=decode_path,
1528 obj = self.__class__(
1532 default=self.default,
1533 optional=self.optional,
1534 _decoded=(offset, 1, 1),
1540 return pp_console_row(next(self.pps()))
1542 def pps(self, decode_path=()):
1544 asn1_type_name=self.asn1_type_name,
1545 obj_name=self.__class__.__name__,
1546 decode_path=decode_path,
1547 value=str(self._value) if self.ready else None,
1548 optional=self.optional,
1549 default=self == self.default,
1550 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1551 expl=None if self._expl is None else tag_decode(self._expl),
1556 expl_offset=self.expl_offset if self.expled else None,
1557 expl_tlen=self.expl_tlen if self.expled else None,
1558 expl_llen=self.expl_llen if self.expled else None,
1559 expl_vlen=self.expl_vlen if self.expled else None,
1560 expl_lenindef=self.expl_lenindef,
1566 """``INTEGER`` integer type
1568 >>> b = Integer(-123)
1570 >>> b == Integer(-123)
1575 >>> Integer(2, bounds=(1, 3))
1577 >>> Integer(5, bounds=(1, 3))
1578 Traceback (most recent call last):
1579 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1583 class Version(Integer):
1590 >>> v = Version("v1")
1597 {'v3': 2, 'v1': 0, 'v2': 1}
1599 __slots__ = ("specs", "_bound_min", "_bound_max")
1600 tag_default = tag_encode(2)
1601 asn1_type_name = "INTEGER"
1615 :param value: set the value. Either integer type, named value
1616 (if ``schema`` is specified in the class), or
1617 :py:class:`pyderasn.Integer` object
1618 :param bounds: set ``(MIN, MAX)`` value constraint.
1619 (-inf, +inf) by default
1620 :param bytes impl: override default tag with ``IMPLICIT`` one
1621 :param bytes expl: override default tag with ``EXPLICIT`` one
1622 :param default: set default value. Type same as in ``value``
1623 :param bool optional: is object ``OPTIONAL`` in sequence
1625 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1627 specs = getattr(self, "schema", {}) if _specs is None else _specs
1628 self.specs = specs if isinstance(specs, dict) else dict(specs)
1629 self._bound_min, self._bound_max = getattr(
1632 (float("-inf"), float("+inf")),
1633 ) if bounds is None else bounds
1634 if value is not None:
1635 self._value = self._value_sanitize(value)
1636 if default is not None:
1637 default = self._value_sanitize(default)
1638 self.default = self.__class__(
1644 if self._value is None:
1645 self._value = default
1647 def _value_sanitize(self, value):
1648 if issubclass(value.__class__, Integer):
1649 value = value._value
1650 elif isinstance(value, integer_types):
1652 elif isinstance(value, str):
1653 value = self.specs.get(value)
1655 raise ObjUnknown("integer value: %s" % value)
1657 raise InvalidValueType((self.__class__, int, str))
1658 if not self._bound_min <= value <= self._bound_max:
1659 raise BoundsError(self._bound_min, value, self._bound_max)
1664 return self._value is not None
1667 obj = self.__class__(_specs=self.specs)
1668 obj._value = self._value
1669 obj._bound_min = self._bound_min
1670 obj._bound_max = self._bound_max
1672 obj._expl = self._expl
1673 obj.default = self.default
1674 obj.optional = self.optional
1675 obj.offset = self.offset
1676 obj.llen = self.llen
1677 obj.vlen = self.vlen
1681 self._assert_ready()
1682 return int(self._value)
1685 self._assert_ready()
1688 bytes(self._expl or b"") +
1689 str(self._value).encode("ascii"),
1692 def __eq__(self, their):
1693 if isinstance(their, integer_types):
1694 return self._value == their
1695 if not issubclass(their.__class__, Integer):
1698 self._value == their._value and
1699 self.tag == their.tag and
1700 self._expl == their._expl
1703 def __lt__(self, their):
1704 return self._value < their._value
1708 for name, value in self.specs.items():
1709 if value == self._value:
1721 return self.__class__(
1724 (self._bound_min, self._bound_max)
1725 if bounds is None else bounds
1727 impl=self.tag if impl is None else impl,
1728 expl=self._expl if expl is None else expl,
1729 default=self.default if default is None else default,
1730 optional=self.optional if optional is None else optional,
1735 self._assert_ready()
1739 octets = bytearray([0])
1743 octets = bytearray()
1745 octets.append((value & 0xFF) ^ 0xFF)
1747 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1750 octets = bytearray()
1752 octets.append(value & 0xFF)
1754 if octets[-1] & 0x80 > 0:
1757 octets = bytes(octets)
1759 bytes_len = ceil(value.bit_length() / 8) or 1
1762 octets = value.to_bytes(
1767 except OverflowError:
1771 return b"".join((self.tag, len_encode(len(octets)), octets))
1773 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1775 t, _, lv = tag_strip(tlv)
1776 except DecodeError as err:
1777 raise err.__class__(
1779 klass=self.__class__,
1780 decode_path=decode_path,
1785 klass=self.__class__,
1786 decode_path=decode_path,
1792 l, llen, v = len_decode(lv)
1793 except DecodeError as err:
1794 raise err.__class__(
1796 klass=self.__class__,
1797 decode_path=decode_path,
1801 raise NotEnoughData(
1802 "encoded length is longer than data",
1803 klass=self.__class__,
1804 decode_path=decode_path,
1808 raise NotEnoughData(
1810 klass=self.__class__,
1811 decode_path=decode_path,
1814 v, tail = v[:l], v[l:]
1815 first_octet = byte2int(v)
1817 second_octet = byte2int(v[1:])
1819 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1820 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1823 "non normalized integer",
1824 klass=self.__class__,
1825 decode_path=decode_path,
1830 if first_octet & 0x80 > 0:
1831 octets = bytearray()
1832 for octet in bytearray(v):
1833 octets.append(octet ^ 0xFF)
1834 for octet in octets:
1835 value = (value << 8) | octet
1839 for octet in bytearray(v):
1840 value = (value << 8) | octet
1842 value = int.from_bytes(v, byteorder="big", signed=True)
1844 obj = self.__class__(
1846 bounds=(self._bound_min, self._bound_max),
1849 default=self.default,
1850 optional=self.optional,
1852 _decoded=(offset, llen, l),
1854 except BoundsError as err:
1857 klass=self.__class__,
1858 decode_path=decode_path,
1864 return pp_console_row(next(self.pps()))
1866 def pps(self, decode_path=()):
1868 asn1_type_name=self.asn1_type_name,
1869 obj_name=self.__class__.__name__,
1870 decode_path=decode_path,
1871 value=(self.named or str(self._value)) if self.ready else None,
1872 optional=self.optional,
1873 default=self == self.default,
1874 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1875 expl=None if self._expl is None else tag_decode(self._expl),
1880 expl_offset=self.expl_offset if self.expled else None,
1881 expl_tlen=self.expl_tlen if self.expled else None,
1882 expl_llen=self.expl_llen if self.expled else None,
1883 expl_vlen=self.expl_vlen if self.expled else None,
1884 expl_lenindef=self.expl_lenindef,
1888 class BitString(Obj):
1889 """``BIT STRING`` bit string type
1891 >>> BitString(b"hello world")
1892 BIT STRING 88 bits 68656c6c6f20776f726c64
1895 >>> b == b"hello world"
1900 >>> BitString("'0A3B5F291CD'H")
1901 BIT STRING 44 bits 0a3b5f291cd0
1902 >>> b = BitString("'010110000000'B")
1903 BIT STRING 12 bits 5800
1906 >>> b[0], b[1], b[2], b[3]
1907 (False, True, False, True)
1911 [False, True, False, True, True, False, False, False, False, False, False, False]
1915 class KeyUsage(BitString):
1917 ("digitalSignature", 0),
1918 ("nonRepudiation", 1),
1919 ("keyEncipherment", 2),
1922 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1923 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1925 ['nonRepudiation', 'keyEncipherment']
1927 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1931 Pay attention that BIT STRING can be encoded both in primitive
1932 and constructed forms. Decoder always checks constructed form tag
1933 additionally to specified primitive one. If BER decoding is
1934 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
1935 of DER restrictions.
1937 __slots__ = ("tag_constructed", "specs", "defined")
1938 tag_default = tag_encode(3)
1939 asn1_type_name = "BIT STRING"
1952 :param value: set the value. Either binary type, tuple of named
1953 values (if ``schema`` is specified in the class),
1954 string in ``'XXX...'B`` form, or
1955 :py:class:`pyderasn.BitString` object
1956 :param bytes impl: override default tag with ``IMPLICIT`` one
1957 :param bytes expl: override default tag with ``EXPLICIT`` one
1958 :param default: set default value. Type same as in ``value``
1959 :param bool optional: is object ``OPTIONAL`` in sequence
1961 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1962 specs = getattr(self, "schema", {}) if _specs is None else _specs
1963 self.specs = specs if isinstance(specs, dict) else dict(specs)
1964 self._value = None if value is None else self._value_sanitize(value)
1965 if default is not None:
1966 default = self._value_sanitize(default)
1967 self.default = self.__class__(
1973 self._value = default
1975 tag_klass, _, tag_num = tag_decode(self.tag)
1976 self.tag_constructed = tag_encode(
1978 form=TagFormConstructed,
1982 def _bits2octets(self, bits):
1983 if len(self.specs) > 0:
1984 bits = bits.rstrip("0")
1986 bits += "0" * ((8 - (bit_len % 8)) % 8)
1987 octets = bytearray(len(bits) // 8)
1988 for i in six_xrange(len(octets)):
1989 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1990 return bit_len, bytes(octets)
1992 def _value_sanitize(self, value):
1993 if issubclass(value.__class__, BitString):
1995 if isinstance(value, (string_types, binary_type)):
1997 isinstance(value, string_types) and
1998 value.startswith("'")
2000 if value.endswith("'B"):
2002 if not set(value) <= set(("0", "1")):
2003 raise ValueError("B's coding contains unacceptable chars")
2004 return self._bits2octets(value)
2005 elif value.endswith("'H"):
2009 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2012 raise InvalidValueType((self.__class__, string_types, binary_type))
2013 elif isinstance(value, binary_type):
2014 return (len(value) * 8, value)
2016 raise InvalidValueType((self.__class__, string_types, binary_type))
2017 if isinstance(value, tuple):
2020 isinstance(value[0], integer_types) and
2021 isinstance(value[1], binary_type)
2026 bit = self.specs.get(name)
2028 raise ObjUnknown("BitString value: %s" % name)
2031 return self._bits2octets("")
2033 return self._bits2octets("".join(
2034 ("1" if bit in bits else "0")
2035 for bit in six_xrange(max(bits) + 1)
2037 raise InvalidValueType((self.__class__, binary_type, string_types))
2041 return self._value is not None
2044 obj = self.__class__(_specs=self.specs)
2046 if value is not None:
2047 value = (value[0], value[1])
2050 obj._expl = self._expl
2051 obj.default = self.default
2052 obj.optional = self.optional
2053 obj.offset = self.offset
2054 obj.llen = self.llen
2055 obj.vlen = self.vlen
2059 self._assert_ready()
2060 for i in six_xrange(self._value[0]):
2065 self._assert_ready()
2066 return self._value[0]
2068 def __bytes__(self):
2069 self._assert_ready()
2070 return self._value[1]
2072 def __eq__(self, their):
2073 if isinstance(their, bytes):
2074 return self._value[1] == their
2075 if not issubclass(their.__class__, BitString):
2078 self._value == their._value and
2079 self.tag == their.tag and
2080 self._expl == their._expl
2085 return [name for name, bit in self.specs.items() if self[bit]]
2095 return self.__class__(
2097 impl=self.tag if impl is None else impl,
2098 expl=self._expl if expl is None else expl,
2099 default=self.default if default is None else default,
2100 optional=self.optional if optional is None else optional,
2104 def __getitem__(self, key):
2105 if isinstance(key, int):
2106 bit_len, octets = self._value
2110 byte2int(memoryview(octets)[key // 8:]) >>
2113 if isinstance(key, string_types):
2114 value = self.specs.get(key)
2116 raise ObjUnknown("BitString value: %s" % key)
2118 raise InvalidValueType((int, str))
2121 self._assert_ready()
2122 bit_len, octets = self._value
2125 len_encode(len(octets) + 1),
2126 int2byte((8 - bit_len % 8) % 8),
2130 def _decode_chunk(self, lv, offset, decode_path, ctx):
2132 l, llen, v = len_decode(lv)
2133 except DecodeError as err:
2134 raise err.__class__(
2136 klass=self.__class__,
2137 decode_path=decode_path,
2141 raise NotEnoughData(
2142 "encoded length is longer than data",
2143 klass=self.__class__,
2144 decode_path=decode_path,
2148 raise NotEnoughData(
2150 klass=self.__class__,
2151 decode_path=decode_path,
2154 pad_size = byte2int(v)
2155 if l == 1 and pad_size != 0:
2157 "invalid empty value",
2158 klass=self.__class__,
2159 decode_path=decode_path,
2165 klass=self.__class__,
2166 decode_path=decode_path,
2169 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2172 klass=self.__class__,
2173 decode_path=decode_path,
2176 v, tail = v[:l], v[l:]
2177 obj = self.__class__(
2178 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2181 default=self.default,
2182 optional=self.optional,
2184 _decoded=(offset, llen, l),
2188 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2190 t, tlen, lv = tag_strip(tlv)
2191 except DecodeError as err:
2192 raise err.__class__(
2194 klass=self.__class__,
2195 decode_path=decode_path,
2201 return self._decode_chunk(lv, offset, decode_path, ctx)
2202 if t == self.tag_constructed:
2203 if not ctx.get("bered", False):
2205 "unallowed BER constructed encoding",
2206 decode_path=decode_path,
2213 l, llen, v = len_decode(lv)
2214 except LenIndefForm:
2215 llen, l, v = 1, 0, lv[1:]
2217 except DecodeError as err:
2218 raise err.__class__(
2220 klass=self.__class__,
2221 decode_path=decode_path,
2224 if l > 0 and l > len(v):
2225 raise NotEnoughData(
2226 "encoded length is longer than data",
2227 klass=self.__class__,
2228 decode_path=decode_path,
2231 if not lenindef and l == 0:
2232 raise NotEnoughData(
2234 klass=self.__class__,
2235 decode_path=decode_path,
2239 sub_offset = offset + tlen + llen
2243 if v[:EOC_LEN].tobytes() == EOC:
2250 "chunk out of bounds",
2251 decode_path=len(chunks) - 1,
2252 offset=chunks[-1].offset,
2254 sub_decode_path = decode_path + (str(len(chunks)),)
2256 chunk, v_tail = BitString().decode(
2259 decode_path=sub_decode_path,
2265 "expected BitString encoded chunk",
2266 decode_path=sub_decode_path,
2269 chunks.append(chunk)
2270 sub_offset += chunk.tlvlen
2271 vlen += chunk.tlvlen
2273 if len(chunks) == 0:
2276 decode_path=decode_path,
2281 for chunk_i, chunk in enumerate(chunks[:-1]):
2282 if chunk.bit_len % 8 != 0:
2284 "BitString chunk is not multiple of 8 bit",
2285 decode_path=decode_path + (str(chunk_i),),
2286 offset=chunk.offset,
2288 values.append(bytes(chunk))
2289 bit_len += chunk.bit_len
2290 chunk_last = chunks[-1]
2291 values.append(bytes(chunk_last))
2292 bit_len += chunk_last.bit_len
2293 obj = self.__class__(
2294 value=(bit_len, b"".join(values)),
2297 default=self.default,
2298 optional=self.optional,
2300 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2302 obj.lenindef = lenindef
2304 return obj, (v[EOC_LEN:] if lenindef else v)
2306 klass=self.__class__,
2307 decode_path=decode_path,
2312 return pp_console_row(next(self.pps()))
2314 def pps(self, decode_path=()):
2318 bit_len, blob = self._value
2319 value = "%d bits" % bit_len
2320 if len(self.specs) > 0:
2321 blob = tuple(self.named)
2323 asn1_type_name=self.asn1_type_name,
2324 obj_name=self.__class__.__name__,
2325 decode_path=decode_path,
2328 optional=self.optional,
2329 default=self == self.default,
2330 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2331 expl=None if self._expl is None else tag_decode(self._expl),
2336 expl_offset=self.expl_offset if self.expled else None,
2337 expl_tlen=self.expl_tlen if self.expled else None,
2338 expl_llen=self.expl_llen if self.expled else None,
2339 expl_vlen=self.expl_vlen if self.expled else None,
2340 expl_lenindef=self.expl_lenindef,
2341 lenindef=self.lenindef,
2344 defined_by, defined = self.defined or (None, None)
2345 if defined_by is not None:
2347 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2351 class OctetString(Obj):
2352 """``OCTET STRING`` binary string type
2354 >>> s = OctetString(b"hello world")
2355 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2356 >>> s == OctetString(b"hello world")
2361 >>> OctetString(b"hello", bounds=(4, 4))
2362 Traceback (most recent call last):
2363 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2364 >>> OctetString(b"hell", bounds=(4, 4))
2365 OCTET STRING 4 bytes 68656c6c
2369 Pay attention that OCTET STRING can be encoded both in primitive
2370 and constructed forms. Decoder always checks constructed form tag
2371 additionally to specified primitive one. If BER decoding is
2372 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2373 of DER restrictions.
2375 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2376 tag_default = tag_encode(4)
2377 asn1_type_name = "OCTET STRING"
2390 :param value: set the value. Either binary type, or
2391 :py:class:`pyderasn.OctetString` object
2392 :param bounds: set ``(MIN, MAX)`` value size constraint.
2393 (-inf, +inf) by default
2394 :param bytes impl: override default tag with ``IMPLICIT`` one
2395 :param bytes expl: override default tag with ``EXPLICIT`` one
2396 :param default: set default value. Type same as in ``value``
2397 :param bool optional: is object ``OPTIONAL`` in sequence
2399 super(OctetString, self).__init__(
2407 self._bound_min, self._bound_max = getattr(
2411 ) if bounds is None else bounds
2412 if value is not None:
2413 self._value = self._value_sanitize(value)
2414 if default is not None:
2415 default = self._value_sanitize(default)
2416 self.default = self.__class__(
2421 if self._value is None:
2422 self._value = default
2424 tag_klass, _, tag_num = tag_decode(self.tag)
2425 self.tag_constructed = tag_encode(
2427 form=TagFormConstructed,
2431 def _value_sanitize(self, value):
2432 if issubclass(value.__class__, OctetString):
2433 value = value._value
2434 elif isinstance(value, binary_type):
2437 raise InvalidValueType((self.__class__, bytes))
2438 if not self._bound_min <= len(value) <= self._bound_max:
2439 raise BoundsError(self._bound_min, len(value), self._bound_max)
2444 return self._value is not None
2447 obj = self.__class__()
2448 obj._value = self._value
2449 obj._bound_min = self._bound_min
2450 obj._bound_max = self._bound_max
2452 obj._expl = self._expl
2453 obj.default = self.default
2454 obj.optional = self.optional
2455 obj.offset = self.offset
2456 obj.llen = self.llen
2457 obj.vlen = self.vlen
2460 def __bytes__(self):
2461 self._assert_ready()
2464 def __eq__(self, their):
2465 if isinstance(their, binary_type):
2466 return self._value == their
2467 if not issubclass(their.__class__, OctetString):
2470 self._value == their._value and
2471 self.tag == their.tag and
2472 self._expl == their._expl
2475 def __lt__(self, their):
2476 return self._value < their._value
2487 return self.__class__(
2490 (self._bound_min, self._bound_max)
2491 if bounds is None else bounds
2493 impl=self.tag if impl is None else impl,
2494 expl=self._expl if expl is None else expl,
2495 default=self.default if default is None else default,
2496 optional=self.optional if optional is None else optional,
2500 self._assert_ready()
2503 len_encode(len(self._value)),
2507 def _decode_chunk(self, lv, offset, decode_path, ctx):
2509 l, llen, v = len_decode(lv)
2510 except DecodeError as err:
2511 raise err.__class__(
2513 klass=self.__class__,
2514 decode_path=decode_path,
2518 raise NotEnoughData(
2519 "encoded length is longer than data",
2520 klass=self.__class__,
2521 decode_path=decode_path,
2524 v, tail = v[:l], v[l:]
2526 obj = self.__class__(
2528 bounds=(self._bound_min, self._bound_max),
2531 default=self.default,
2532 optional=self.optional,
2533 _decoded=(offset, llen, l),
2535 except DecodeError as err:
2538 klass=self.__class__,
2539 decode_path=decode_path,
2542 except BoundsError as err:
2545 klass=self.__class__,
2546 decode_path=decode_path,
2551 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2553 t, tlen, lv = tag_strip(tlv)
2554 except DecodeError as err:
2555 raise err.__class__(
2557 klass=self.__class__,
2558 decode_path=decode_path,
2564 return self._decode_chunk(lv, offset, decode_path, ctx)
2565 if t == self.tag_constructed:
2566 if not ctx.get("bered", False):
2568 "unallowed BER constructed encoding",
2569 decode_path=decode_path,
2576 l, llen, v = len_decode(lv)
2577 except LenIndefForm:
2578 llen, l, v = 1, 0, lv[1:]
2580 except DecodeError as err:
2581 raise err.__class__(
2583 klass=self.__class__,
2584 decode_path=decode_path,
2587 if l > 0 and l > len(v):
2588 raise NotEnoughData(
2589 "encoded length is longer than data",
2590 klass=self.__class__,
2591 decode_path=decode_path,
2594 if not lenindef and l == 0:
2595 raise NotEnoughData(
2597 klass=self.__class__,
2598 decode_path=decode_path,
2602 sub_offset = offset + tlen + llen
2606 if v[:EOC_LEN].tobytes() == EOC:
2613 "chunk out of bounds",
2614 decode_path=len(chunks) - 1,
2615 offset=chunks[-1].offset,
2617 sub_decode_path = decode_path + (str(len(chunks)),)
2619 chunk, v_tail = OctetString().decode(
2622 decode_path=sub_decode_path,
2628 "expected OctetString encoded chunk",
2629 decode_path=sub_decode_path,
2632 chunks.append(chunk)
2633 sub_offset += chunk.tlvlen
2634 vlen += chunk.tlvlen
2636 if len(chunks) == 0:
2639 decode_path=decode_path,
2643 obj = self.__class__(
2644 value=b"".join(bytes(chunk) for chunk in chunks),
2645 bounds=(self._bound_min, self._bound_max),
2648 default=self.default,
2649 optional=self.optional,
2650 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2652 except DecodeError as err:
2655 klass=self.__class__,
2656 decode_path=decode_path,
2659 except BoundsError as err:
2662 klass=self.__class__,
2663 decode_path=decode_path,
2666 obj.lenindef = lenindef
2668 return obj, (v[EOC_LEN:] if lenindef else v)
2670 klass=self.__class__,
2671 decode_path=decode_path,
2676 return pp_console_row(next(self.pps()))
2678 def pps(self, decode_path=()):
2680 asn1_type_name=self.asn1_type_name,
2681 obj_name=self.__class__.__name__,
2682 decode_path=decode_path,
2683 value=("%d bytes" % len(self._value)) if self.ready else None,
2684 blob=self._value if self.ready else None,
2685 optional=self.optional,
2686 default=self == self.default,
2687 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2688 expl=None if self._expl is None else tag_decode(self._expl),
2693 expl_offset=self.expl_offset if self.expled else None,
2694 expl_tlen=self.expl_tlen if self.expled else None,
2695 expl_llen=self.expl_llen if self.expled else None,
2696 expl_vlen=self.expl_vlen if self.expled else None,
2697 expl_lenindef=self.expl_lenindef,
2698 lenindef=self.lenindef,
2701 defined_by, defined = self.defined or (None, None)
2702 if defined_by is not None:
2704 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2709 """``NULL`` null object
2717 tag_default = tag_encode(5)
2718 asn1_type_name = "NULL"
2722 value=None, # unused, but Sequence passes it
2729 :param bytes impl: override default tag with ``IMPLICIT`` one
2730 :param bytes expl: override default tag with ``EXPLICIT`` one
2731 :param bool optional: is object ``OPTIONAL`` in sequence
2733 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2741 obj = self.__class__()
2743 obj._expl = self._expl
2744 obj.default = self.default
2745 obj.optional = self.optional
2746 obj.offset = self.offset
2747 obj.llen = self.llen
2748 obj.vlen = self.vlen
2751 def __eq__(self, their):
2752 if not issubclass(their.__class__, Null):
2755 self.tag == their.tag and
2756 self._expl == their._expl
2766 return self.__class__(
2767 impl=self.tag if impl is None else impl,
2768 expl=self._expl if expl is None else expl,
2769 optional=self.optional if optional is None else optional,
2773 return self.tag + len_encode(0)
2775 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2777 t, _, lv = tag_strip(tlv)
2778 except DecodeError as err:
2779 raise err.__class__(
2781 klass=self.__class__,
2782 decode_path=decode_path,
2787 klass=self.__class__,
2788 decode_path=decode_path,
2794 l, _, v = len_decode(lv)
2795 except DecodeError as err:
2796 raise err.__class__(
2798 klass=self.__class__,
2799 decode_path=decode_path,
2803 raise InvalidLength(
2804 "Null must have zero length",
2805 klass=self.__class__,
2806 decode_path=decode_path,
2809 obj = self.__class__(
2812 optional=self.optional,
2813 _decoded=(offset, 1, 0),
2818 return pp_console_row(next(self.pps()))
2820 def pps(self, decode_path=()):
2822 asn1_type_name=self.asn1_type_name,
2823 obj_name=self.__class__.__name__,
2824 decode_path=decode_path,
2825 optional=self.optional,
2826 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2827 expl=None if self._expl is None else tag_decode(self._expl),
2832 expl_offset=self.expl_offset if self.expled else None,
2833 expl_tlen=self.expl_tlen if self.expled else None,
2834 expl_llen=self.expl_llen if self.expled else None,
2835 expl_vlen=self.expl_vlen if self.expled else None,
2836 expl_lenindef=self.expl_lenindef,
2840 class ObjectIdentifier(Obj):
2841 """``OBJECT IDENTIFIER`` OID type
2843 >>> oid = ObjectIdentifier((1, 2, 3))
2844 OBJECT IDENTIFIER 1.2.3
2845 >>> oid == ObjectIdentifier("1.2.3")
2851 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2852 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2854 >>> str(ObjectIdentifier((3, 1)))
2855 Traceback (most recent call last):
2856 pyderasn.InvalidOID: unacceptable first arc value
2858 __slots__ = ("defines",)
2859 tag_default = tag_encode(6)
2860 asn1_type_name = "OBJECT IDENTIFIER"
2873 :param value: set the value. Either tuples of integers,
2874 string of "."-concatenated integers, or
2875 :py:class:`pyderasn.ObjectIdentifier` object
2876 :param defines: sequence of tuples. Each tuple has two elements.
2877 First one is relative to current one decode
2878 path, aiming to the field defined by that OID.
2879 Read about relative path in
2880 :py:func:`pyderasn.abs_decode_path`. Second
2881 tuple element is ``{OID: pyderasn.Obj()}``
2882 dictionary, mapping between current OID value
2883 and structure applied to defined field.
2884 :ref:`Read about DEFINED BY <definedby>`
2885 :param bytes impl: override default tag with ``IMPLICIT`` one
2886 :param bytes expl: override default tag with ``EXPLICIT`` one
2887 :param default: set default value. Type same as in ``value``
2888 :param bool optional: is object ``OPTIONAL`` in sequence
2890 super(ObjectIdentifier, self).__init__(
2898 if value is not None:
2899 self._value = self._value_sanitize(value)
2900 if default is not None:
2901 default = self._value_sanitize(default)
2902 self.default = self.__class__(
2907 if self._value is None:
2908 self._value = default
2909 self.defines = defines
2911 def __add__(self, their):
2912 if isinstance(their, self.__class__):
2913 return self.__class__(self._value + their._value)
2914 if isinstance(their, tuple):
2915 return self.__class__(self._value + their)
2916 raise InvalidValueType((self.__class__, tuple))
2918 def _value_sanitize(self, value):
2919 if issubclass(value.__class__, ObjectIdentifier):
2921 if isinstance(value, string_types):
2923 value = tuple(int(arc) for arc in value.split("."))
2925 raise InvalidOID("unacceptable arcs values")
2926 if isinstance(value, tuple):
2928 raise InvalidOID("less than 2 arcs")
2929 first_arc = value[0]
2930 if first_arc in (0, 1):
2931 if not (0 <= value[1] <= 39):
2932 raise InvalidOID("second arc is too wide")
2933 elif first_arc == 2:
2936 raise InvalidOID("unacceptable first arc value")
2938 raise InvalidValueType((self.__class__, str, tuple))
2942 return self._value is not None
2945 obj = self.__class__()
2946 obj._value = self._value
2947 obj.defines = self.defines
2949 obj._expl = self._expl
2950 obj.default = self.default
2951 obj.optional = self.optional
2952 obj.offset = self.offset
2953 obj.llen = self.llen
2954 obj.vlen = self.vlen
2958 self._assert_ready()
2959 return iter(self._value)
2962 return ".".join(str(arc) for arc in self._value or ())
2965 self._assert_ready()
2968 bytes(self._expl or b"") +
2969 str(self._value).encode("ascii"),
2972 def __eq__(self, their):
2973 if isinstance(their, tuple):
2974 return self._value == their
2975 if not issubclass(their.__class__, ObjectIdentifier):
2978 self.tag == their.tag and
2979 self._expl == their._expl and
2980 self._value == their._value
2983 def __lt__(self, their):
2984 return self._value < their._value
2995 return self.__class__(
2997 defines=self.defines if defines is None else defines,
2998 impl=self.tag if impl is None else impl,
2999 expl=self._expl if expl is None else expl,
3000 default=self.default if default is None else default,
3001 optional=self.optional if optional is None else optional,
3005 self._assert_ready()
3007 first_value = value[1]
3008 first_arc = value[0]
3011 elif first_arc == 1:
3013 elif first_arc == 2:
3015 else: # pragma: no cover
3016 raise RuntimeError("invalid arc is stored")
3017 octets = [zero_ended_encode(first_value)]
3018 for arc in value[2:]:
3019 octets.append(zero_ended_encode(arc))
3020 v = b"".join(octets)
3021 return b"".join((self.tag, len_encode(len(v)), v))
3023 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3025 t, _, lv = tag_strip(tlv)
3026 except DecodeError as err:
3027 raise err.__class__(
3029 klass=self.__class__,
3030 decode_path=decode_path,
3035 klass=self.__class__,
3036 decode_path=decode_path,
3042 l, llen, v = len_decode(lv)
3043 except DecodeError as err:
3044 raise err.__class__(
3046 klass=self.__class__,
3047 decode_path=decode_path,
3051 raise NotEnoughData(
3052 "encoded length is longer than data",
3053 klass=self.__class__,
3054 decode_path=decode_path,
3058 raise NotEnoughData(
3060 klass=self.__class__,
3061 decode_path=decode_path,
3064 v, tail = v[:l], v[l:]
3070 octet = indexbytes(v, i)
3071 arc = (arc << 7) | (octet & 0x7F)
3072 if octet & 0x80 == 0:
3080 klass=self.__class__,
3081 decode_path=decode_path,
3085 second_arc = arcs[0]
3086 if 0 <= second_arc <= 39:
3088 elif 40 <= second_arc <= 79:
3094 obj = self.__class__(
3095 value=tuple([first_arc, second_arc] + arcs[1:]),
3098 default=self.default,
3099 optional=self.optional,
3100 _decoded=(offset, llen, l),
3105 return pp_console_row(next(self.pps()))
3107 def pps(self, decode_path=()):
3109 asn1_type_name=self.asn1_type_name,
3110 obj_name=self.__class__.__name__,
3111 decode_path=decode_path,
3112 value=str(self) if self.ready else None,
3113 optional=self.optional,
3114 default=self == self.default,
3115 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3116 expl=None if self._expl is None else tag_decode(self._expl),
3121 expl_offset=self.expl_offset if self.expled else None,
3122 expl_tlen=self.expl_tlen if self.expled else None,
3123 expl_llen=self.expl_llen if self.expled else None,
3124 expl_vlen=self.expl_vlen if self.expled else None,
3125 expl_lenindef=self.expl_lenindef,
3129 class Enumerated(Integer):
3130 """``ENUMERATED`` integer type
3132 This type is identical to :py:class:`pyderasn.Integer`, but requires
3133 schema to be specified and does not accept values missing from it.
3136 tag_default = tag_encode(10)
3137 asn1_type_name = "ENUMERATED"
3148 bounds=None, # dummy argument, workability for Integer.decode
3150 super(Enumerated, self).__init__(
3159 if len(self.specs) == 0:
3160 raise ValueError("schema must be specified")
3162 def _value_sanitize(self, value):
3163 if isinstance(value, self.__class__):
3164 value = value._value
3165 elif isinstance(value, integer_types):
3166 if value not in list(self.specs.values()):
3168 "unknown integer value: %s" % value,
3169 klass=self.__class__,
3171 elif isinstance(value, string_types):
3172 value = self.specs.get(value)
3174 raise ObjUnknown("integer value: %s" % value)
3176 raise InvalidValueType((self.__class__, int, str))
3180 obj = self.__class__(_specs=self.specs)
3181 obj._value = self._value
3182 obj._bound_min = self._bound_min
3183 obj._bound_max = self._bound_max
3185 obj._expl = self._expl
3186 obj.default = self.default
3187 obj.optional = self.optional
3188 obj.offset = self.offset
3189 obj.llen = self.llen
3190 obj.vlen = self.vlen
3202 return self.__class__(
3204 impl=self.tag if impl is None else impl,
3205 expl=self._expl if expl is None else expl,
3206 default=self.default if default is None else default,
3207 optional=self.optional if optional is None else optional,
3212 class CommonString(OctetString):
3213 """Common class for all strings
3215 Everything resembles :py:class:`pyderasn.OctetString`, except
3216 ability to deal with unicode text strings.
3218 >>> hexenc("привет мир".encode("utf-8"))
3219 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3220 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3222 >>> s = UTF8String("привет мир")
3223 UTF8String UTF8String привет мир
3225 'привет мир'
3226 >>> hexenc(bytes(s))
3227 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3229 >>> PrintableString("привет мир")
3230 Traceback (most recent call last):
3231 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3233 >>> BMPString("ада", bounds=(2, 2))
3234 Traceback (most recent call last):
3235 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3236 >>> s = BMPString("ад", bounds=(2, 2))
3239 >>> hexenc(bytes(s))
3247 * - :py:class:`pyderasn.UTF8String`
3249 * - :py:class:`pyderasn.NumericString`
3251 * - :py:class:`pyderasn.PrintableString`
3253 * - :py:class:`pyderasn.TeletexString`
3255 * - :py:class:`pyderasn.T61String`
3257 * - :py:class:`pyderasn.VideotexString`
3259 * - :py:class:`pyderasn.IA5String`
3261 * - :py:class:`pyderasn.GraphicString`
3263 * - :py:class:`pyderasn.VisibleString`
3265 * - :py:class:`pyderasn.ISO646String`
3267 * - :py:class:`pyderasn.GeneralString`
3269 * - :py:class:`pyderasn.UniversalString`
3271 * - :py:class:`pyderasn.BMPString`
3274 __slots__ = ("encoding",)
3276 def _value_sanitize(self, value):
3278 value_decoded = None
3279 if isinstance(value, self.__class__):
3280 value_raw = value._value
3281 elif isinstance(value, text_type):
3282 value_decoded = value
3283 elif isinstance(value, binary_type):
3286 raise InvalidValueType((self.__class__, text_type, binary_type))
3289 value_decoded.encode(self.encoding)
3290 if value_raw is None else value_raw
3293 value_raw.decode(self.encoding)
3294 if value_decoded is None else value_decoded
3296 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3297 raise DecodeError(str(err))
3298 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3306 def __eq__(self, their):
3307 if isinstance(their, binary_type):
3308 return self._value == their
3309 if isinstance(their, text_type):
3310 return self._value == their.encode(self.encoding)
3311 if not isinstance(their, self.__class__):
3314 self._value == their._value and
3315 self.tag == their.tag and
3316 self._expl == their._expl
3319 def __unicode__(self):
3321 return self._value.decode(self.encoding)
3322 return text_type(self._value)
3325 return pp_console_row(next(self.pps(no_unicode=PY2)))
3327 def pps(self, decode_path=(), no_unicode=False):
3330 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3332 asn1_type_name=self.asn1_type_name,
3333 obj_name=self.__class__.__name__,
3334 decode_path=decode_path,
3336 optional=self.optional,
3337 default=self == self.default,
3338 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3339 expl=None if self._expl is None else tag_decode(self._expl),
3344 expl_offset=self.expl_offset if self.expled else None,
3345 expl_tlen=self.expl_tlen if self.expled else None,
3346 expl_llen=self.expl_llen if self.expled else None,
3347 expl_vlen=self.expl_vlen if self.expled else None,
3348 expl_lenindef=self.expl_lenindef,
3352 class UTF8String(CommonString):
3354 tag_default = tag_encode(12)
3356 asn1_type_name = "UTF8String"
3359 class NumericString(CommonString):
3362 Its value is properly sanitized: only ASCII digits can be stored.
3365 tag_default = tag_encode(18)
3367 asn1_type_name = "NumericString"
3368 allowable_chars = set(digits.encode("ascii"))
3370 def _value_sanitize(self, value):
3371 value = super(NumericString, self)._value_sanitize(value)
3372 if not set(value) <= self.allowable_chars:
3373 raise DecodeError("non-numeric value")
3377 class PrintableString(CommonString):
3379 tag_default = tag_encode(19)
3381 asn1_type_name = "PrintableString"
3384 class TeletexString(CommonString):
3386 tag_default = tag_encode(20)
3388 asn1_type_name = "TeletexString"
3391 class T61String(TeletexString):
3393 asn1_type_name = "T61String"
3396 class VideotexString(CommonString):
3398 tag_default = tag_encode(21)
3399 encoding = "iso-8859-1"
3400 asn1_type_name = "VideotexString"
3403 class IA5String(CommonString):
3405 tag_default = tag_encode(22)
3407 asn1_type_name = "IA5"
3410 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3411 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3412 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3415 class UTCTime(CommonString):
3416 """``UTCTime`` datetime type
3418 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3419 UTCTime UTCTime 2017-09-30T22:07:50
3425 datetime.datetime(2017, 9, 30, 22, 7, 50)
3426 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3427 datetime.datetime(1957, 9, 30, 22, 7, 50)
3430 tag_default = tag_encode(23)
3432 asn1_type_name = "UTCTime"
3434 fmt = "%y%m%d%H%M%SZ"
3444 bounds=None, # dummy argument, workability for OctetString.decode
3447 :param value: set the value. Either datetime type, or
3448 :py:class:`pyderasn.UTCTime` object
3449 :param bytes impl: override default tag with ``IMPLICIT`` one
3450 :param bytes expl: override default tag with ``EXPLICIT`` one
3451 :param default: set default value. Type same as in ``value``
3452 :param bool optional: is object ``OPTIONAL`` in sequence
3454 super(UTCTime, self).__init__(
3462 if value is not None:
3463 self._value = self._value_sanitize(value)
3464 if default is not None:
3465 default = self._value_sanitize(default)
3466 self.default = self.__class__(
3471 if self._value is None:
3472 self._value = default
3474 def _value_sanitize(self, value):
3475 if isinstance(value, self.__class__):
3477 if isinstance(value, datetime):
3478 return value.strftime(self.fmt).encode("ascii")
3479 if isinstance(value, binary_type):
3480 value_decoded = value.decode("ascii")
3481 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3483 datetime.strptime(value_decoded, self.fmt)
3485 raise DecodeError("invalid UTCTime format")
3488 raise DecodeError("invalid UTCTime length")
3489 raise InvalidValueType((self.__class__, datetime))
3491 def __eq__(self, their):
3492 if isinstance(their, binary_type):
3493 return self._value == their
3494 if isinstance(their, datetime):
3495 return self.todatetime() == their
3496 if not isinstance(their, self.__class__):
3499 self._value == their._value and
3500 self.tag == their.tag and
3501 self._expl == their._expl
3504 def todatetime(self):
3505 """Convert to datetime
3509 Pay attention that UTCTime can not hold full year, so all years
3510 having < 50 years are treated as 20xx, 19xx otherwise, according
3511 to X.509 recomendation.
3513 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3514 year = value.year % 100
3516 year=(2000 + year) if year < 50 else (1900 + year),
3520 minute=value.minute,
3521 second=value.second,
3525 return pp_console_row(next(self.pps()))
3527 def pps(self, decode_path=()):
3529 asn1_type_name=self.asn1_type_name,
3530 obj_name=self.__class__.__name__,
3531 decode_path=decode_path,
3532 value=self.todatetime().isoformat() if self.ready else None,
3533 optional=self.optional,
3534 default=self == self.default,
3535 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3536 expl=None if self._expl is None else tag_decode(self._expl),
3541 expl_offset=self.expl_offset if self.expled else None,
3542 expl_tlen=self.expl_tlen if self.expled else None,
3543 expl_llen=self.expl_llen if self.expled else None,
3544 expl_vlen=self.expl_vlen if self.expled else None,
3545 expl_lenindef=self.expl_lenindef,
3549 class GeneralizedTime(UTCTime):
3550 """``GeneralizedTime`` datetime type
3552 This type is similar to :py:class:`pyderasn.UTCTime`.
3554 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3555 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3557 '20170930220750.000123Z'
3558 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3559 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3562 tag_default = tag_encode(24)
3563 asn1_type_name = "GeneralizedTime"
3565 fmt = "%Y%m%d%H%M%SZ"
3566 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3568 def _value_sanitize(self, value):
3569 if isinstance(value, self.__class__):
3571 if isinstance(value, datetime):
3572 return value.strftime(
3573 self.fmt_ms if value.microsecond > 0 else self.fmt
3575 if isinstance(value, binary_type):
3576 value_decoded = value.decode("ascii")
3577 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3579 datetime.strptime(value_decoded, self.fmt)
3582 "invalid GeneralizedTime (without ms) format",
3585 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3587 datetime.strptime(value_decoded, self.fmt_ms)
3590 "invalid GeneralizedTime (with ms) format",
3595 "invalid GeneralizedTime length",
3596 klass=self.__class__,
3598 raise InvalidValueType((self.__class__, datetime))
3600 def todatetime(self):
3601 value = self._value.decode("ascii")
3602 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3603 return datetime.strptime(value, self.fmt)
3604 return datetime.strptime(value, self.fmt_ms)
3607 class GraphicString(CommonString):
3609 tag_default = tag_encode(25)
3610 encoding = "iso-8859-1"
3611 asn1_type_name = "GraphicString"
3614 class VisibleString(CommonString):
3616 tag_default = tag_encode(26)
3618 asn1_type_name = "VisibleString"
3621 class ISO646String(VisibleString):
3623 asn1_type_name = "ISO646String"
3626 class GeneralString(CommonString):
3628 tag_default = tag_encode(27)
3629 encoding = "iso-8859-1"
3630 asn1_type_name = "GeneralString"
3633 class UniversalString(CommonString):
3635 tag_default = tag_encode(28)
3636 encoding = "utf-32-be"
3637 asn1_type_name = "UniversalString"
3640 class BMPString(CommonString):
3642 tag_default = tag_encode(30)
3643 encoding = "utf-16-be"
3644 asn1_type_name = "BMPString"
3648 """``CHOICE`` special type
3652 class GeneralName(Choice):
3654 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3655 ("dNSName", IA5String(impl=tag_ctxp(2))),
3658 >>> gn = GeneralName()
3660 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3661 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3662 >>> gn["dNSName"] = IA5String("bar.baz")
3663 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3664 >>> gn["rfc822Name"]
3667 [2] IA5String IA5 bar.baz
3670 >>> gn.value == gn["dNSName"]
3673 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3675 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3676 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3678 __slots__ = ("specs",)
3680 asn1_type_name = "CHOICE"
3693 :param value: set the value. Either ``(choice, value)`` tuple, or
3694 :py:class:`pyderasn.Choice` object
3695 :param bytes impl: can not be set, do **not** use it
3696 :param bytes expl: override default tag with ``EXPLICIT`` one
3697 :param default: set default value. Type same as in ``value``
3698 :param bool optional: is object ``OPTIONAL`` in sequence
3700 if impl is not None:
3701 raise ValueError("no implicit tag allowed for CHOICE")
3702 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3704 schema = getattr(self, "schema", ())
3705 if len(schema) == 0:
3706 raise ValueError("schema must be specified")
3708 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3711 if value is not None:
3712 self._value = self._value_sanitize(value)
3713 if default is not None:
3714 default_value = self._value_sanitize(default)
3715 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3716 default_obj.specs = self.specs
3717 default_obj._value = default_value
3718 self.default = default_obj
3720 self._value = default_obj.copy()._value
3722 def _value_sanitize(self, value):
3723 if isinstance(value, self.__class__):
3725 if isinstance(value, tuple) and len(value) == 2:
3727 spec = self.specs.get(choice)
3729 raise ObjUnknown(choice)
3730 if not isinstance(obj, spec.__class__):
3731 raise InvalidValueType((spec,))
3732 return (choice, spec(obj))
3733 raise InvalidValueType((self.__class__, tuple))
3737 return self._value is not None and self._value[1].ready
3740 obj = self.__class__(schema=self.specs)
3741 obj._expl = self._expl
3742 obj.default = self.default
3743 obj.optional = self.optional
3744 obj.offset = self.offset
3745 obj.llen = self.llen
3746 obj.vlen = self.vlen
3748 if value is not None:
3749 obj._value = (value[0], value[1].copy())
3752 def __eq__(self, their):
3753 if isinstance(their, tuple) and len(their) == 2:
3754 return self._value == their
3755 if not isinstance(their, self.__class__):
3758 self.specs == their.specs and
3759 self._value == their._value
3769 return self.__class__(
3772 expl=self._expl if expl is None else expl,
3773 default=self.default if default is None else default,
3774 optional=self.optional if optional is None else optional,
3779 self._assert_ready()
3780 return self._value[0]
3784 self._assert_ready()
3785 return self._value[1]
3787 def __getitem__(self, key):
3788 if key not in self.specs:
3789 raise ObjUnknown(key)
3790 if self._value is None:
3792 choice, value = self._value
3797 def __setitem__(self, key, value):
3798 spec = self.specs.get(key)
3800 raise ObjUnknown(key)
3801 if not isinstance(value, spec.__class__):
3802 raise InvalidValueType((spec.__class__,))
3803 self._value = (key, spec(value))
3811 return self._value[1].decoded if self.ready else False
3814 self._assert_ready()
3815 return self._value[1].encode()
3817 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3818 for choice, spec in self.specs.items():
3819 sub_decode_path = decode_path + (choice,)
3825 decode_path=sub_decode_path,
3834 klass=self.__class__,
3835 decode_path=decode_path,
3840 value, tail = spec.decode(
3844 decode_path=sub_decode_path,
3847 obj = self.__class__(
3850 default=self.default,
3851 optional=self.optional,
3852 _decoded=(offset, 0, value.tlvlen),
3854 obj._value = (choice, value)
3858 value = pp_console_row(next(self.pps()))
3860 value = "%s[%r]" % (value, self.value)
3863 def pps(self, decode_path=()):
3865 asn1_type_name=self.asn1_type_name,
3866 obj_name=self.__class__.__name__,
3867 decode_path=decode_path,
3868 value=self.choice if self.ready else None,
3869 optional=self.optional,
3870 default=self == self.default,
3871 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3872 expl=None if self._expl is None else tag_decode(self._expl),
3877 expl_lenindef=self.expl_lenindef,
3880 yield self.value.pps(decode_path=decode_path + (self.choice,))
3883 class PrimitiveTypes(Choice):
3884 """Predefined ``CHOICE`` for all generic primitive types
3886 It could be useful for general decoding of some unspecified values:
3888 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3889 OCTET STRING 3 bytes 666f6f
3890 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3894 schema = tuple((klass.__name__, klass()) for klass in (
3919 """``ANY`` special type
3921 >>> Any(Integer(-123))
3923 >>> a = Any(OctetString(b"hello world").encode())
3924 ANY 040b68656c6c6f20776f726c64
3925 >>> hexenc(bytes(a))
3926 b'0x040x0bhello world'
3928 __slots__ = ("defined",)
3929 tag_default = tag_encode(0)
3930 asn1_type_name = "ANY"
3940 :param value: set the value. Either any kind of pyderasn's
3941 **ready** object, or bytes. Pay attention that
3942 **no** validation is performed is raw binary value
3944 :param bytes expl: override default tag with ``EXPLICIT`` one
3945 :param bool optional: is object ``OPTIONAL`` in sequence
3947 super(Any, self).__init__(None, expl, None, optional, _decoded)
3948 self._value = None if value is None else self._value_sanitize(value)
3951 def _value_sanitize(self, value):
3952 if isinstance(value, self.__class__):
3954 if isinstance(value, Obj):
3955 return value.encode()
3956 if isinstance(value, binary_type):
3958 raise InvalidValueType((self.__class__, Obj, binary_type))
3962 return self._value is not None
3965 obj = self.__class__()
3966 obj._value = self._value
3968 obj._expl = self._expl
3969 obj.optional = self.optional
3970 obj.offset = self.offset
3971 obj.llen = self.llen
3972 obj.vlen = self.vlen
3975 def __eq__(self, their):
3976 if isinstance(their, binary_type):
3977 return self._value == their
3978 if issubclass(their.__class__, Any):
3979 return self._value == their._value
3988 return self.__class__(
3990 expl=self._expl if expl is None else expl,
3991 optional=self.optional if optional is None else optional,
3994 def __bytes__(self):
3995 self._assert_ready()
4003 self._assert_ready()
4006 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4008 t, tlen, lv = tag_strip(tlv)
4009 except DecodeError as err:
4010 raise err.__class__(
4012 klass=self.__class__,
4013 decode_path=decode_path,
4017 l, llen, v = len_decode(lv)
4018 except LenIndefForm as err:
4019 if not ctx.get("bered", False):
4020 raise err.__class__(
4022 klass=self.__class__,
4023 decode_path=decode_path,
4026 llen, vlen, v = 1, 0, lv[1:]
4027 sub_offset = offset + tlen + llen
4030 if v[:EOC_LEN].tobytes() == EOC:
4031 tlvlen = tlen + llen + vlen + EOC_LEN
4032 obj = self.__class__(
4033 value=tlv[:tlvlen].tobytes(),
4035 optional=self.optional,
4036 _decoded=(offset, 0, tlvlen),
4040 return obj, v[EOC_LEN:]
4042 chunk, v = Any().decode(
4045 decode_path=decode_path + (str(chunk_i),),
4049 vlen += chunk.tlvlen
4050 sub_offset += chunk.tlvlen
4052 except DecodeError as err:
4053 raise err.__class__(
4055 klass=self.__class__,
4056 decode_path=decode_path,
4060 raise NotEnoughData(
4061 "encoded length is longer than data",
4062 klass=self.__class__,
4063 decode_path=decode_path,
4066 tlvlen = tlen + llen + l
4067 v, tail = tlv[:tlvlen], v[l:]
4068 obj = self.__class__(
4071 optional=self.optional,
4072 _decoded=(offset, 0, tlvlen),
4078 return pp_console_row(next(self.pps()))
4080 def pps(self, decode_path=()):
4082 asn1_type_name=self.asn1_type_name,
4083 obj_name=self.__class__.__name__,
4084 decode_path=decode_path,
4085 blob=self._value if self.ready else None,
4086 optional=self.optional,
4087 default=self == self.default,
4088 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4089 expl=None if self._expl is None else tag_decode(self._expl),
4094 expl_offset=self.expl_offset if self.expled else None,
4095 expl_tlen=self.expl_tlen if self.expled else None,
4096 expl_llen=self.expl_llen if self.expled else None,
4097 expl_vlen=self.expl_vlen if self.expled else None,
4098 expl_lenindef=self.expl_lenindef,
4099 lenindef=self.lenindef,
4101 defined_by, defined = self.defined or (None, None)
4102 if defined_by is not None:
4104 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4108 ########################################################################
4109 # ASN.1 constructed types
4110 ########################################################################
4112 def get_def_by_path(defines_by_path, sub_decode_path):
4113 """Get define by decode path
4115 for path, define in defines_by_path:
4116 if len(path) != len(sub_decode_path):
4118 for p1, p2 in zip(path, sub_decode_path):
4119 if (p1 != any) and (p1 != p2):
4125 def abs_decode_path(decode_path, rel_path):
4126 """Create an absolute decode path from current and relative ones
4128 :param decode_path: current decode path, starting point.
4130 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4131 If first tuple's element is "/", then treat it as
4132 an absolute path, ignoring ``decode_path`` as
4133 starting point. Also this tuple can contain ".."
4134 elements, stripping the leading element from
4137 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4138 ("foo", "bar", "baz", "whatever")
4139 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4141 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4144 if rel_path[0] == "/":
4146 if rel_path[0] == "..":
4147 return abs_decode_path(decode_path[:-1], rel_path[1:])
4148 return decode_path + rel_path
4151 class Sequence(Obj):
4152 """``SEQUENCE`` structure type
4154 You have to make specification of sequence::
4156 class Extension(Sequence):
4158 ("extnID", ObjectIdentifier()),
4159 ("critical", Boolean(default=False)),
4160 ("extnValue", OctetString()),
4163 Then, you can work with it as with dictionary.
4165 >>> ext = Extension()
4166 >>> Extension().specs
4168 ('extnID', OBJECT IDENTIFIER),
4169 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4170 ('extnValue', OCTET STRING),
4172 >>> ext["extnID"] = "1.2.3"
4173 Traceback (most recent call last):
4174 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4175 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4177 You can determine if sequence is ready to be encoded:
4182 Traceback (most recent call last):
4183 pyderasn.ObjNotReady: object is not ready: extnValue
4184 >>> ext["extnValue"] = OctetString(b"foobar")
4188 Value you want to assign, must have the same **type** as in
4189 corresponding specification, but it can have different tags,
4190 optional/default attributes -- they will be taken from specification
4193 class TBSCertificate(Sequence):
4195 ("version", Version(expl=tag_ctxc(0), default="v1")),
4198 >>> tbs = TBSCertificate()
4199 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4201 Assign ``None`` to remove value from sequence.
4203 You can set values in Sequence during its initialization:
4205 >>> AlgorithmIdentifier((
4206 ("algorithm", ObjectIdentifier("1.2.3")),
4207 ("parameters", Any(Null()))
4209 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4211 You can determine if value exists/set in the sequence and take its value:
4213 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4216 OBJECT IDENTIFIER 1.2.3
4218 But pay attention that if value has default, then it won't be (not
4219 in) in the sequence (because ``DEFAULT`` must not be encoded in
4220 DER), but you can read its value:
4222 >>> "critical" in ext, ext["critical"]
4223 (False, BOOLEAN False)
4224 >>> ext["critical"] = Boolean(True)
4225 >>> "critical" in ext, ext["critical"]
4226 (True, BOOLEAN True)
4228 All defaulted values are always optional.
4230 .. _strict_default_existence_ctx:
4234 When decoded DER contains defaulted value inside, then
4235 technically this is not valid DER encoding. But we allow and pass
4236 it **by default**. Of course reencoding of that kind of DER will
4237 result in different binary representation (validly without
4238 defaulted value inside). You can enable strict defaulted values
4239 existence validation by setting ``"strict_default_existence":
4240 True`` :ref:`context <ctx>` option -- decoding process will raise
4241 an exception if defaulted value is met.
4243 Two sequences are equal if they have equal specification (schema),
4244 implicit/explicit tagging and the same values.
4246 __slots__ = ("specs",)
4247 tag_default = tag_encode(form=TagFormConstructed, num=16)
4248 asn1_type_name = "SEQUENCE"
4260 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4262 schema = getattr(self, "schema", ())
4264 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4267 if value is not None:
4268 if issubclass(value.__class__, Sequence):
4269 self._value = value._value
4270 elif hasattr(value, "__iter__"):
4271 for seq_key, seq_value in value:
4272 self[seq_key] = seq_value
4274 raise InvalidValueType((Sequence,))
4275 if default is not None:
4276 if not issubclass(default.__class__, Sequence):
4277 raise InvalidValueType((Sequence,))
4278 default_value = default._value
4279 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4280 default_obj.specs = self.specs
4281 default_obj._value = default_value
4282 self.default = default_obj
4284 self._value = default_obj.copy()._value
4288 for name, spec in self.specs.items():
4289 value = self._value.get(name)
4300 obj = self.__class__(schema=self.specs)
4302 obj._expl = self._expl
4303 obj.default = self.default
4304 obj.optional = self.optional
4305 obj.offset = self.offset
4306 obj.llen = self.llen
4307 obj.vlen = self.vlen
4308 obj._value = {k: v.copy() for k, v in self._value.items()}
4311 def __eq__(self, their):
4312 if not isinstance(their, self.__class__):
4315 self.specs == their.specs and
4316 self.tag == their.tag and
4317 self._expl == their._expl and
4318 self._value == their._value
4329 return self.__class__(
4332 impl=self.tag if impl is None else impl,
4333 expl=self._expl if expl is None else expl,
4334 default=self.default if default is None else default,
4335 optional=self.optional if optional is None else optional,
4338 def __contains__(self, key):
4339 return key in self._value
4341 def __setitem__(self, key, value):
4342 spec = self.specs.get(key)
4344 raise ObjUnknown(key)
4346 self._value.pop(key, None)
4348 if not isinstance(value, spec.__class__):
4349 raise InvalidValueType((spec.__class__,))
4350 value = spec(value=value)
4351 if spec.default is not None and value == spec.default:
4352 self._value.pop(key, None)
4354 self._value[key] = value
4356 def __getitem__(self, key):
4357 value = self._value.get(key)
4358 if value is not None:
4360 spec = self.specs.get(key)
4362 raise ObjUnknown(key)
4363 if spec.default is not None:
4367 def _encoded_values(self):
4369 for name, spec in self.specs.items():
4370 value = self._value.get(name)
4374 raise ObjNotReady(name)
4375 raws.append(value.encode())
4379 v = b"".join(self._encoded_values())
4380 return b"".join((self.tag, len_encode(len(v)), v))
4382 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4384 t, tlen, lv = tag_strip(tlv)
4385 except DecodeError as err:
4386 raise err.__class__(
4388 klass=self.__class__,
4389 decode_path=decode_path,
4394 klass=self.__class__,
4395 decode_path=decode_path,
4402 l, llen, v = len_decode(lv)
4403 except LenIndefForm as err:
4404 if not ctx.get("bered", False):
4405 raise err.__class__(
4407 klass=self.__class__,
4408 decode_path=decode_path,
4411 l, llen, v = 0, 1, lv[1:]
4413 except DecodeError as err:
4414 raise err.__class__(
4416 klass=self.__class__,
4417 decode_path=decode_path,
4421 raise NotEnoughData(
4422 "encoded length is longer than data",
4423 klass=self.__class__,
4424 decode_path=decode_path,
4428 v, tail = v[:l], v[l:]
4430 sub_offset = offset + tlen + llen
4432 for name, spec in self.specs.items():
4433 if spec.optional and (
4434 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4438 sub_decode_path = decode_path + (name,)
4440 value, v_tail = spec.decode(
4444 decode_path=sub_decode_path,
4452 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4453 if defined is not None:
4454 defined_by, defined_spec = defined
4455 if issubclass(value.__class__, SequenceOf):
4456 for i, _value in enumerate(value):
4457 sub_sub_decode_path = sub_decode_path + (
4459 DecodePathDefBy(defined_by),
4461 defined_value, defined_tail = defined_spec.decode(
4462 memoryview(bytes(_value)),
4464 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4465 if value.expled else (value.tlen + value.llen)
4468 decode_path=sub_sub_decode_path,
4471 if len(defined_tail) > 0:
4474 klass=self.__class__,
4475 decode_path=sub_sub_decode_path,
4478 _value.defined = (defined_by, defined_value)
4480 defined_value, defined_tail = defined_spec.decode(
4481 memoryview(bytes(value)),
4483 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4484 if value.expled else (value.tlen + value.llen)
4487 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4490 if len(defined_tail) > 0:
4493 klass=self.__class__,
4494 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4497 value.defined = (defined_by, defined_value)
4499 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4501 sub_offset += value_len
4503 if spec.default is not None and value == spec.default:
4504 if ctx.get("strict_default_existence", False):
4506 "DEFAULT value met",
4507 klass=self.__class__,
4508 decode_path=sub_decode_path,
4513 values[name] = value
4515 spec_defines = getattr(spec, "defines", ())
4516 if len(spec_defines) == 0:
4517 defines_by_path = ctx.get("defines_by_path", ())
4518 if len(defines_by_path) > 0:
4519 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4520 if spec_defines is not None and len(spec_defines) > 0:
4521 for rel_path, schema in spec_defines:
4522 defined = schema.get(value, None)
4523 if defined is not None:
4524 ctx.setdefault("defines", []).append((
4525 abs_decode_path(sub_decode_path[:-1], rel_path),
4529 if v[:EOC_LEN].tobytes() != EOC:
4532 klass=self.__class__,
4533 decode_path=decode_path,
4541 klass=self.__class__,
4542 decode_path=decode_path,
4545 obj = self.__class__(
4549 default=self.default,
4550 optional=self.optional,
4551 _decoded=(offset, llen, vlen),
4554 obj.lenindef = lenindef
4558 value = pp_console_row(next(self.pps()))
4560 for name in self.specs:
4561 _value = self._value.get(name)
4564 cols.append(repr(_value))
4565 return "%s[%s]" % (value, ", ".join(cols))
4567 def pps(self, decode_path=()):
4569 asn1_type_name=self.asn1_type_name,
4570 obj_name=self.__class__.__name__,
4571 decode_path=decode_path,
4572 optional=self.optional,
4573 default=self == self.default,
4574 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4575 expl=None if self._expl is None else tag_decode(self._expl),
4580 expl_offset=self.expl_offset if self.expled else None,
4581 expl_tlen=self.expl_tlen if self.expled else None,
4582 expl_llen=self.expl_llen if self.expled else None,
4583 expl_vlen=self.expl_vlen if self.expled else None,
4584 expl_lenindef=self.expl_lenindef,
4585 lenindef=self.lenindef,
4587 for name in self.specs:
4588 value = self._value.get(name)
4591 yield value.pps(decode_path=decode_path + (name,))
4594 class Set(Sequence):
4595 """``SET`` structure type
4597 Its usage is identical to :py:class:`pyderasn.Sequence`.
4600 tag_default = tag_encode(form=TagFormConstructed, num=17)
4601 asn1_type_name = "SET"
4604 raws = self._encoded_values()
4607 return b"".join((self.tag, len_encode(len(v)), v))
4609 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4611 t, tlen, lv = tag_strip(tlv)
4612 except DecodeError as err:
4613 raise err.__class__(
4615 klass=self.__class__,
4616 decode_path=decode_path,
4621 klass=self.__class__,
4622 decode_path=decode_path,
4629 l, llen, v = len_decode(lv)
4630 except LenIndefForm as err:
4631 if not ctx.get("bered", False):
4632 raise err.__class__(
4634 klass=self.__class__,
4635 decode_path=decode_path,
4638 l, llen, v = 0, 1, lv[1:]
4640 except DecodeError as err:
4641 raise err.__class__(
4643 klass=self.__class__,
4644 decode_path=decode_path,
4648 raise NotEnoughData(
4649 "encoded length is longer than data",
4650 klass=self.__class__,
4654 v, tail = v[:l], v[l:]
4656 sub_offset = offset + tlen + llen
4658 specs_items = self.specs.items
4660 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4662 for name, spec in specs_items():
4663 sub_decode_path = decode_path + (name,)
4669 decode_path=sub_decode_path,
4678 klass=self.__class__,
4679 decode_path=decode_path,
4682 value, v_tail = spec.decode(
4686 decode_path=sub_decode_path,
4689 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4690 sub_offset += value_len
4693 if spec.default is None or value != spec.default: # pragma: no cover
4694 # SeqMixing.test_encoded_default_accepted covers that place
4695 values[name] = value
4696 obj = self.__class__(
4700 default=self.default,
4701 optional=self.optional,
4702 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4707 "not all values are ready",
4708 klass=self.__class__,
4709 decode_path=decode_path,
4712 obj.lenindef = lenindef
4713 return obj, (v[EOC_LEN:] if lenindef else tail)
4716 class SequenceOf(Obj):
4717 """``SEQUENCE OF`` sequence type
4719 For that kind of type you must specify the object it will carry on
4720 (bounds are for example here, not required)::
4722 class Ints(SequenceOf):
4727 >>> ints.append(Integer(123))
4728 >>> ints.append(Integer(234))
4730 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4731 >>> [int(i) for i in ints]
4733 >>> ints.append(Integer(345))
4734 Traceback (most recent call last):
4735 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4738 >>> ints[1] = Integer(345)
4740 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4742 Also you can initialize sequence with preinitialized values:
4744 >>> ints = Ints([Integer(123), Integer(234)])
4746 __slots__ = ("spec", "_bound_min", "_bound_max")
4747 tag_default = tag_encode(form=TagFormConstructed, num=16)
4748 asn1_type_name = "SEQUENCE OF"
4761 super(SequenceOf, self).__init__(
4769 schema = getattr(self, "schema", None)
4771 raise ValueError("schema must be specified")
4773 self._bound_min, self._bound_max = getattr(
4777 ) if bounds is None else bounds
4779 if value is not None:
4780 self._value = self._value_sanitize(value)
4781 if default is not None:
4782 default_value = self._value_sanitize(default)
4783 default_obj = self.__class__(
4788 default_obj._value = default_value
4789 self.default = default_obj
4791 self._value = default_obj.copy()._value
4793 def _value_sanitize(self, value):
4794 if issubclass(value.__class__, SequenceOf):
4795 value = value._value
4796 elif hasattr(value, "__iter__"):
4799 raise InvalidValueType((self.__class__, iter))
4800 if not self._bound_min <= len(value) <= self._bound_max:
4801 raise BoundsError(self._bound_min, len(value), self._bound_max)
4803 if not isinstance(v, self.spec.__class__):
4804 raise InvalidValueType((self.spec.__class__,))
4809 return all(v.ready for v in self._value)
4812 obj = self.__class__(schema=self.spec)
4813 obj._bound_min = self._bound_min
4814 obj._bound_max = self._bound_max
4816 obj._expl = self._expl
4817 obj.default = self.default
4818 obj.optional = self.optional
4819 obj.offset = self.offset
4820 obj.llen = self.llen
4821 obj.vlen = self.vlen
4822 obj._value = [v.copy() for v in self._value]
4825 def __eq__(self, their):
4826 if isinstance(their, self.__class__):
4828 self.spec == their.spec and
4829 self.tag == their.tag and
4830 self._expl == their._expl and
4831 self._value == their._value
4833 if hasattr(their, "__iter__"):
4834 return self._value == list(their)
4846 return self.__class__(
4850 (self._bound_min, self._bound_max)
4851 if bounds is None else bounds
4853 impl=self.tag if impl is None else impl,
4854 expl=self._expl if expl is None else expl,
4855 default=self.default if default is None else default,
4856 optional=self.optional if optional is None else optional,
4859 def __contains__(self, key):
4860 return key in self._value
4862 def append(self, value):
4863 if not isinstance(value, self.spec.__class__):
4864 raise InvalidValueType((self.spec.__class__,))
4865 if len(self._value) + 1 > self._bound_max:
4868 len(self._value) + 1,
4871 self._value.append(value)
4874 self._assert_ready()
4875 return iter(self._value)
4878 self._assert_ready()
4879 return len(self._value)
4881 def __setitem__(self, key, value):
4882 if not isinstance(value, self.spec.__class__):
4883 raise InvalidValueType((self.spec.__class__,))
4884 self._value[key] = self.spec(value=value)
4886 def __getitem__(self, key):
4887 return self._value[key]
4889 def _encoded_values(self):
4890 return [v.encode() for v in self._value]
4893 v = b"".join(self._encoded_values())
4894 return b"".join((self.tag, len_encode(len(v)), v))
4896 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4898 t, tlen, lv = tag_strip(tlv)
4899 except DecodeError as err:
4900 raise err.__class__(
4902 klass=self.__class__,
4903 decode_path=decode_path,
4908 klass=self.__class__,
4909 decode_path=decode_path,
4916 l, llen, v = len_decode(lv)
4917 except LenIndefForm as err:
4918 if not ctx.get("bered", False):
4919 raise err.__class__(
4921 klass=self.__class__,
4922 decode_path=decode_path,
4925 l, llen, v = 0, 1, lv[1:]
4927 except DecodeError as err:
4928 raise err.__class__(
4930 klass=self.__class__,
4931 decode_path=decode_path,
4935 raise NotEnoughData(
4936 "encoded length is longer than data",
4937 klass=self.__class__,
4938 decode_path=decode_path,
4942 v, tail = v[:l], v[l:]
4944 sub_offset = offset + tlen + llen
4948 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4950 value, v_tail = spec.decode(
4954 decode_path=decode_path + (str(len(_value)),),
4957 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4958 sub_offset += value_len
4961 _value.append(value)
4962 obj = self.__class__(
4965 bounds=(self._bound_min, self._bound_max),
4968 default=self.default,
4969 optional=self.optional,
4970 _decoded=(offset, llen, vlen),
4972 obj.lenindef = lenindef
4973 return obj, (v[EOC_LEN:] if lenindef else tail)
4977 pp_console_row(next(self.pps())),
4978 ", ".join(repr(v) for v in self._value),
4981 def pps(self, decode_path=()):
4983 asn1_type_name=self.asn1_type_name,
4984 obj_name=self.__class__.__name__,
4985 decode_path=decode_path,
4986 optional=self.optional,
4987 default=self == self.default,
4988 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4989 expl=None if self._expl is None else tag_decode(self._expl),
4994 expl_offset=self.expl_offset if self.expled else None,
4995 expl_tlen=self.expl_tlen if self.expled else None,
4996 expl_llen=self.expl_llen if self.expled else None,
4997 expl_vlen=self.expl_vlen if self.expled else None,
4998 expl_lenindef=self.expl_lenindef,
4999 lenindef=self.lenindef,
5001 for i, value in enumerate(self._value):
5002 yield value.pps(decode_path=decode_path + (str(i),))
5005 class SetOf(SequenceOf):
5006 """``SET OF`` sequence type
5008 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5011 tag_default = tag_encode(form=TagFormConstructed, num=17)
5012 asn1_type_name = "SET OF"
5015 raws = self._encoded_values()
5018 return b"".join((self.tag, len_encode(len(v)), v))
5021 def obj_by_path(pypath): # pragma: no cover
5022 """Import object specified as string Python path
5024 Modules must be separated from classes/functions with ``:``.
5026 >>> obj_by_path("foo.bar:Baz")
5027 <class 'foo.bar.Baz'>
5028 >>> obj_by_path("foo.bar:Baz.boo")
5029 <classmethod 'foo.bar.Baz.boo'>
5031 mod, objs = pypath.rsplit(":", 1)
5032 from importlib import import_module
5033 obj = import_module(mod)
5034 for obj_name in objs.split("."):
5035 obj = getattr(obj, obj_name)
5039 def generic_decoder(): # pragma: no cover
5040 # All of this below is a big hack with self references
5041 choice = PrimitiveTypes()
5042 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5043 choice.specs["SetOf"] = SetOf(schema=choice)
5045 choice.specs["SequenceOf%d" % i] = SequenceOf(
5049 choice.specs["Any"] = Any()
5051 # Class name equals to type name, to omit it from output
5052 class SEQUENCEOF(SequenceOf):
5056 def pprint_any(obj, oids=None, with_colours=False):
5057 def _pprint_pps(pps):
5059 if hasattr(pp, "_fields"):
5060 if pp.asn1_type_name == Choice.asn1_type_name:
5062 pp_kwargs = pp._asdict()
5063 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5064 pp = _pp(**pp_kwargs)
5065 yield pp_console_row(
5070 with_colours=with_colours,
5072 for row in pp_console_blob(pp):
5075 for row in _pprint_pps(pp):
5077 return "\n".join(_pprint_pps(obj.pps()))
5078 return SEQUENCEOF(), pprint_any
5081 def main(): # pragma: no cover
5083 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5084 parser.add_argument(
5088 help="Skip that number of bytes from the beginning",
5090 parser.add_argument(
5092 help="Python path to dictionary with OIDs",
5094 parser.add_argument(
5096 help="Python path to schema definition to use",
5098 parser.add_argument(
5099 "--defines-by-path",
5100 help="Python path to decoder's defines_by_path",
5102 parser.add_argument(
5104 action='store_true',
5105 help="Disallow BER encoding",
5107 parser.add_argument(
5109 type=argparse.FileType("rb"),
5110 help="Path to DER file you want to decode",
5112 args = parser.parse_args()
5113 args.DERFile.seek(args.skip)
5114 der = memoryview(args.DERFile.read())
5115 args.DERFile.close()
5116 oids = obj_by_path(args.oids) if args.oids else {}
5118 schema = obj_by_path(args.schema)
5119 from functools import partial
5120 pprinter = partial(pprint, big_blobs=True)
5122 schema, pprinter = generic_decoder()
5123 ctx = {"bered": not args.nobered}
5124 if args.defines_by_path is not None:
5125 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5126 obj, tail = schema().decode(der, ctx=ctx)
5130 with_colours=True if environ.get("NO_COLOR") is None else False,
5133 print("\nTrailing data: %s" % hexenc(tail))
5136 if __name__ == "__main__":