3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
193 * ``expled`` -- to know if explicit tag is set
194 * ``expl_offset`` (it is lesser than ``offset``)
197 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
198 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
200 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
203 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
210 You can specify so called context keyword argument during ``decode()``
211 invocation. It is dictionary containing various options governing
214 Currently available context options:
216 * :ref:`allow_default_values <allow_default_values_ctx>`
217 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
218 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
219 * :ref:`bered <bered_ctx>`
220 * :ref:`defines_by_path <defines_by_path_ctx>`
227 All objects have ``pps()`` method, that is a generator of
228 :py:class:`pyderasn.PP` namedtuple, holding various raw information
229 about the object. If ``pps`` is called on sequences, then all underlying
230 ``PP`` will be yielded.
232 You can use :py:func:`pyderasn.pp_console_row` function, converting
233 those ``PP`` to human readable string. Actually exactly it is used for
234 all object ``repr``. But it is easy to write custom formatters.
236 >>> from pyderasn import pprint
237 >>> encoded = Integer(-12345).encode()
238 >>> obj, tail = Integer().decode(encoded)
239 >>> print(pprint(obj))
240 0 [1,1, 2] INTEGER -12345
247 ASN.1 structures often have ANY and OCTET STRING fields, that are
248 DEFINED BY some previously met ObjectIdentifier. This library provides
249 ability to specify mapping between some OID and field that must be
250 decoded with specific specification.
255 :py:class:`pyderasn.ObjectIdentifier` field inside
256 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
257 necessary for decoding structures. For example, CMS (:rfc:`5652`)
260 class ContentInfo(Sequence):
262 ("contentType", ContentType(defines=((("content",), {
263 id_digestedData: DigestedData(),
264 id_signedData: SignedData(),
266 ("content", Any(expl=tag_ctxc(0))),
269 ``contentType`` field tells that it defines that ``content`` must be
270 decoded with ``SignedData`` specification, if ``contentType`` equals to
271 ``id-signedData``. The same applies to ``DigestedData``. If
272 ``contentType`` contains unknown OID, then no automatic decoding is
275 You can specify multiple fields, that will be autodecoded -- that is why
276 ``defines`` kwarg is a sequence. You can specify defined field
277 relatively or absolutely to current decode path. For example ``defines``
278 for AlgorithmIdentifier of X.509's
279 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
283 id_ecPublicKey: ECParameters(),
284 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
286 (("..", "subjectPublicKey"), {
287 id_rsaEncryption: RSAPublicKey(),
288 id_GostR3410_2001: OctetString(),
292 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
293 autodecode its parameters inside SPKI's algorithm and its public key
296 Following types can be automatically decoded (DEFINED BY):
298 * :py:class:`pyderasn.Any`
299 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
300 * :py:class:`pyderasn.OctetString`
301 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
302 ``Any``/``BitString``/``OctetString``-s
304 When any of those fields is automatically decoded, then ``.defined``
305 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
306 was defined, ``value`` contains corresponding decoded value. For example
307 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
309 .. _defines_by_path_ctx:
311 defines_by_path context option
312 ______________________________
314 Sometimes you either can not or do not want to explicitly set *defines*
315 in the scheme. You can dynamically apply those definitions when calling
316 ``.decode()`` method.
318 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
319 value must be sequence of following tuples::
321 (decode_path, defines)
323 where ``decode_path`` is a tuple holding so-called decode path to the
324 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
325 ``defines``, holding exactly the same value as accepted in its keyword
328 For example, again for CMS, you want to automatically decode
329 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
330 structures it may hold. Also, automatically decode ``controlSequence``
333 content_info, tail = ContentInfo().decode(data, defines_by_path=(
336 ((("content",), {id_signedData: SignedData()}),),
341 DecodePathDefBy(id_signedData),
346 id_cct_PKIData: PKIData(),
347 id_cct_PKIResponse: PKIResponse(),
353 DecodePathDefBy(id_signedData),
356 DecodePathDefBy(id_cct_PKIResponse),
362 id_cmc_recipientNonce: RecipientNonce(),
363 id_cmc_senderNonce: SenderNonce(),
364 id_cmc_statusInfoV2: CMCStatusInfoV2(),
365 id_cmc_transactionId: TransactionId(),
370 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
371 First function is useful for path construction when some automatic
372 decoding is already done. ``any`` means literally any value it meet --
373 useful for SEQUENCE/SET OF-s.
380 By default PyDERASN accepts only DER encoded data. It always encodes to
381 DER. But you can optionally enable BER decoding with setting ``bered``
382 :ref:`context <ctx>` argument to True. Indefinite lengths and
383 constructed primitive types should be parsed successfully.
385 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
386 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
387 STRING``, ``SEQUENCE``, ``SET``, ``SET OF`` can contain it.
388 * If object has an indefinite length encoding, then its ``lenindef``
389 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
390 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
392 * If object has an indefinite length encoded explicit tag, then
393 ``expl_lenindef`` is set to True.
394 * If object has either any of BER-related encoding (explicit tag
395 indefinite length, object's indefinite length, BER-encoding) or any
396 underlying component has that kind of encoding, then ``bered``
397 attribute is set to True. For example SignedData CMS can have
398 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
399 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
401 EOC (end-of-contents) token's length is taken in advance in object's
404 .. _allow_expl_oob_ctx:
406 Allow explicit tag out-of-bound
407 -------------------------------
409 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
410 one value, more than one object. If you set ``allow_expl_oob`` context
411 option to True, then no error will be raised and that invalid encoding
412 will be silently further processed. But pay attention that offsets and
413 lengths will be invalid in that case.
417 This option should be used only for skipping some decode errors, just
418 to see the decoded structure somehow.
425 .. autoclass:: pyderasn.Boolean
430 .. autoclass:: pyderasn.Integer
435 .. autoclass:: pyderasn.BitString
440 .. autoclass:: pyderasn.OctetString
445 .. autoclass:: pyderasn.Null
450 .. autoclass:: pyderasn.ObjectIdentifier
455 .. autoclass:: pyderasn.Enumerated
459 .. autoclass:: pyderasn.CommonString
463 .. autoclass:: pyderasn.NumericString
467 .. autoclass:: pyderasn.UTCTime
468 :members: __init__, todatetime
472 .. autoclass:: pyderasn.GeneralizedTime
479 .. autoclass:: pyderasn.Choice
484 .. autoclass:: PrimitiveTypes
488 .. autoclass:: pyderasn.Any
496 .. autoclass:: pyderasn.Sequence
501 .. autoclass:: pyderasn.Set
506 .. autoclass:: pyderasn.SequenceOf
511 .. autoclass:: pyderasn.SetOf
517 .. autofunction:: pyderasn.abs_decode_path
518 .. autofunction:: pyderasn.hexenc
519 .. autofunction:: pyderasn.hexdec
520 .. autofunction:: pyderasn.tag_encode
521 .. autofunction:: pyderasn.tag_decode
522 .. autofunction:: pyderasn.tag_ctxp
523 .. autofunction:: pyderasn.tag_ctxc
524 .. autoclass:: pyderasn.Obj
525 .. autoclass:: pyderasn.DecodeError
527 .. autoclass:: pyderasn.NotEnoughData
528 .. autoclass:: pyderasn.LenIndefForm
529 .. autoclass:: pyderasn.TagMismatch
530 .. autoclass:: pyderasn.InvalidLength
531 .. autoclass:: pyderasn.InvalidOID
532 .. autoclass:: pyderasn.ObjUnknown
533 .. autoclass:: pyderasn.ObjNotReady
534 .. autoclass:: pyderasn.InvalidValueType
535 .. autoclass:: pyderasn.BoundsError
538 from codecs import getdecoder
539 from codecs import getencoder
540 from collections import namedtuple
541 from collections import OrderedDict
542 from datetime import datetime
543 from math import ceil
544 from os import environ
545 from string import ascii_letters
546 from string import digits
548 from six import add_metaclass
549 from six import binary_type
550 from six import byte2int
551 from six import indexbytes
552 from six import int2byte
553 from six import integer_types
554 from six import iterbytes
556 from six import string_types
557 from six import text_type
558 from six.moves import xrange as six_xrange
562 from termcolor import colored
563 except ImportError: # pragma: no cover
564 def colored(what, *args):
608 "TagClassApplication",
612 "TagFormConstructed",
623 TagClassUniversal = 0
624 TagClassApplication = 1 << 6
625 TagClassContext = 1 << 7
626 TagClassPrivate = 1 << 6 | 1 << 7
628 TagFormConstructed = 1 << 5
631 TagClassApplication: "APPLICATION ",
632 TagClassPrivate: "PRIVATE ",
633 TagClassUniversal: "UNIV ",
637 LENINDEF = b"\x80" # length indefinite mark
638 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
641 ########################################################################
643 ########################################################################
645 class DecodeError(Exception):
646 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
648 :param str msg: reason of decode failing
649 :param klass: optional exact DecodeError inherited class (like
650 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
651 :py:exc:`InvalidLength`)
652 :param decode_path: tuple of strings. It contains human
653 readable names of the fields through which
654 decoding process has passed
655 :param int offset: binary offset where failure happened
657 super(DecodeError, self).__init__()
660 self.decode_path = decode_path
666 "" if self.klass is None else self.klass.__name__,
668 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
669 if len(self.decode_path) > 0 else ""
671 ("(at %d)" % self.offset) if self.offset > 0 else "",
677 return "%s(%s)" % (self.__class__.__name__, self)
680 class NotEnoughData(DecodeError):
684 class LenIndefForm(DecodeError):
688 class TagMismatch(DecodeError):
692 class InvalidLength(DecodeError):
696 class InvalidOID(DecodeError):
700 class ObjUnknown(ValueError):
701 def __init__(self, name):
702 super(ObjUnknown, self).__init__()
706 return "object is unknown: %s" % self.name
709 return "%s(%s)" % (self.__class__.__name__, self)
712 class ObjNotReady(ValueError):
713 def __init__(self, name):
714 super(ObjNotReady, self).__init__()
718 return "object is not ready: %s" % self.name
721 return "%s(%s)" % (self.__class__.__name__, self)
724 class InvalidValueType(ValueError):
725 def __init__(self, expected_types):
726 super(InvalidValueType, self).__init__()
727 self.expected_types = expected_types
730 return "invalid value type, expected: %s" % ", ".join(
731 [repr(t) for t in self.expected_types]
735 return "%s(%s)" % (self.__class__.__name__, self)
738 class BoundsError(ValueError):
739 def __init__(self, bound_min, value, bound_max):
740 super(BoundsError, self).__init__()
741 self.bound_min = bound_min
743 self.bound_max = bound_max
746 return "unsatisfied bounds: %s <= %s <= %s" % (
753 return "%s(%s)" % (self.__class__.__name__, self)
756 ########################################################################
758 ########################################################################
760 _hexdecoder = getdecoder("hex")
761 _hexencoder = getencoder("hex")
765 """Binary data to hexadecimal string convert
767 return _hexdecoder(data)[0]
771 """Hexadecimal string to binary data convert
773 return _hexencoder(data)[0].decode("ascii")
776 def int_bytes_len(num, byte_len=8):
779 return int(ceil(float(num.bit_length()) / byte_len))
782 def zero_ended_encode(num):
783 octets = bytearray(int_bytes_len(num, 7))
785 octets[i] = num & 0x7F
789 octets[i] = 0x80 | (num & 0x7F)
795 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
796 """Encode tag to binary form
798 :param int num: tag's number
799 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
800 :py:data:`pyderasn.TagClassContext`,
801 :py:data:`pyderasn.TagClassApplication`,
802 :py:data:`pyderasn.TagClassPrivate`)
803 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
804 :py:data:`pyderasn.TagFormConstructed`)
808 return int2byte(klass | form | num)
809 # [XX|X|11111][1.......][1.......] ... [0.......]
810 return int2byte(klass | form | 31) + zero_ended_encode(num)
814 """Decode tag from binary form
818 No validation is performed, assuming that it has already passed.
820 It returns tuple with three integers, as
821 :py:func:`pyderasn.tag_encode` accepts.
823 first_octet = byte2int(tag)
824 klass = first_octet & 0xC0
825 form = first_octet & 0x20
826 if first_octet & 0x1F < 0x1F:
827 return (klass, form, first_octet & 0x1F)
829 for octet in iterbytes(tag[1:]):
832 return (klass, form, num)
836 """Create CONTEXT PRIMITIVE tag
838 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
842 """Create CONTEXT CONSTRUCTED tag
844 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
848 """Take off tag from the data
850 :returns: (encoded tag, tag length, remaining data)
853 raise NotEnoughData("no data at all")
854 if byte2int(data) & 0x1F < 31:
855 return data[:1], 1, data[1:]
860 raise DecodeError("unfinished tag")
861 if indexbytes(data, i) & 0x80 == 0:
864 return data[:i], i, data[i:]
870 octets = bytearray(int_bytes_len(l) + 1)
871 octets[0] = 0x80 | (len(octets) - 1)
872 for i in six_xrange(len(octets) - 1, 0, -1):
878 def len_decode(data):
881 :returns: (decoded length, length's length, remaining data)
882 :raises LenIndefForm: if indefinite form encoding is met
885 raise NotEnoughData("no data at all")
886 first_octet = byte2int(data)
887 if first_octet & 0x80 == 0:
888 return first_octet, 1, data[1:]
889 octets_num = first_octet & 0x7F
890 if octets_num + 1 > len(data):
891 raise NotEnoughData("encoded length is longer than data")
894 if byte2int(data[1:]) == 0:
895 raise DecodeError("leading zeros")
897 for v in iterbytes(data[1:1 + octets_num]):
900 raise DecodeError("long form instead of short one")
901 return l, 1 + octets_num, data[1 + octets_num:]
904 ########################################################################
906 ########################################################################
908 class AutoAddSlots(type):
909 def __new__(mcs, name, bases, _dict):
910 _dict["__slots__"] = _dict.get("__slots__", ())
911 return type.__new__(mcs, name, bases, _dict)
914 @add_metaclass(AutoAddSlots)
916 """Common ASN.1 object class
918 All ASN.1 types are inherited from it. It has metaclass that
919 automatically adds ``__slots__`` to all inherited classes.
943 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
944 self._expl = getattr(self, "expl", None) if expl is None else expl
945 if self.tag != self.tag_default and self._expl is not None:
946 raise ValueError("implicit and explicit tags can not be set simultaneously")
947 if default is not None:
949 self.optional = optional
950 self.offset, self.llen, self.vlen = _decoded
952 self.expl_lenindef = False
953 self.lenindef = False
954 self.ber_encoded = False
957 def ready(self): # pragma: no cover
958 """Is object ready to be encoded?
960 raise NotImplementedError()
962 def _assert_ready(self):
964 raise ObjNotReady(self.__class__.__name__)
968 """Is either object or any elements inside is BER encoded?
970 return self.expl_lenindef or self.lenindef or self.ber_encoded
974 """Is object decoded?
976 return (self.llen + self.vlen) > 0
978 def copy(self): # pragma: no cover
979 """Make a copy of object, safe to be mutated
981 raise NotImplementedError()
989 return self.tlen + self.llen + self.vlen
991 def __str__(self): # pragma: no cover
992 return self.__bytes__() if PY2 else self.__unicode__()
994 def __ne__(self, their):
995 return not(self == their)
997 def __gt__(self, their): # pragma: no cover
998 return not(self < their)
1000 def __le__(self, their): # pragma: no cover
1001 return (self == their) or (self < their)
1003 def __ge__(self, their): # pragma: no cover
1004 return (self == their) or (self > their)
1006 def _encode(self): # pragma: no cover
1007 raise NotImplementedError()
1009 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1010 raise NotImplementedError()
1013 raw = self._encode()
1014 if self._expl is None:
1016 return b"".join((self._expl, len_encode(len(raw)), raw))
1029 :param data: either binary or memoryview
1030 :param int offset: initial data's offset
1031 :param bool leavemm: do we need to leave memoryview of remaining
1032 data as is, or convert it to bytes otherwise
1033 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1034 :param tag_only: decode only the tag, without length and contents
1035 (used only in Choice and Set structures, trying to
1036 determine if tag satisfies the scheme)
1037 :returns: (Obj, remaining data)
1041 tlv = memoryview(data)
1042 if self._expl is None:
1043 result = self._decode(
1046 decode_path=decode_path,
1055 t, tlen, lv = tag_strip(tlv)
1056 except DecodeError as err:
1057 raise err.__class__(
1059 klass=self.__class__,
1060 decode_path=decode_path,
1065 klass=self.__class__,
1066 decode_path=decode_path,
1070 l, llen, v = len_decode(lv)
1071 except LenIndefForm as err:
1072 if not ctx.get("bered", False):
1073 raise err.__class__(
1075 klass=self.__class__,
1076 decode_path=decode_path,
1080 offset += tlen + llen
1081 result = self._decode(
1084 decode_path=decode_path,
1088 if tag_only: # pragma: no cover
1091 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1092 if eoc_expected.tobytes() != EOC:
1095 klass=self.__class__,
1096 decode_path=decode_path,
1100 obj.expl_lenindef = True
1101 except DecodeError as err:
1102 raise err.__class__(
1104 klass=self.__class__,
1105 decode_path=decode_path,
1110 raise NotEnoughData(
1111 "encoded length is longer than data",
1112 klass=self.__class__,
1113 decode_path=decode_path,
1116 result = self._decode(
1118 offset=offset + tlen + llen,
1119 decode_path=decode_path,
1123 if tag_only: # pragma: no cover
1126 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1128 "explicit tag out-of-bound, longer than data",
1129 klass=self.__class__,
1130 decode_path=decode_path,
1133 return obj, (tail if leavemm else tail.tobytes())
1137 return self._expl is not None
1144 def expl_tlen(self):
1145 return len(self._expl)
1148 def expl_llen(self):
1149 if self.expl_lenindef:
1151 return len(len_encode(self.tlvlen))
1154 def expl_offset(self):
1155 return self.offset - self.expl_tlen - self.expl_llen
1158 def expl_vlen(self):
1162 def expl_tlvlen(self):
1163 return self.expl_tlen + self.expl_llen + self.expl_vlen
1166 def fulloffset(self):
1167 return self.expl_offset if self.expled else self.offset
1171 return self.expl_tlvlen if self.expled else self.tlvlen
1173 def pps_lenindef(self, decode_path):
1174 if self.lenindef and not (
1175 getattr(self, "defined", None) is not None and
1176 self.defined[1].lenindef
1179 asn1_type_name="EOC",
1181 decode_path=decode_path,
1183 self.offset + self.tlvlen -
1184 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1192 if self.expl_lenindef:
1194 asn1_type_name="EOC",
1195 obj_name="EXPLICIT",
1196 decode_path=decode_path,
1197 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1206 class DecodePathDefBy(object):
1207 """DEFINED BY representation inside decode path
1209 __slots__ = ("defined_by",)
1211 def __init__(self, defined_by):
1212 self.defined_by = defined_by
1214 def __ne__(self, their):
1215 return not(self == their)
1217 def __eq__(self, their):
1218 if not isinstance(their, self.__class__):
1220 return self.defined_by == their.defined_by
1223 return "DEFINED BY " + str(self.defined_by)
1226 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1229 ########################################################################
1231 ########################################################################
1233 PP = namedtuple("PP", (
1259 asn1_type_name="unknown",
1276 expl_lenindef=False,
1306 def _colourize(what, colour, with_colours, attrs=("bold",)):
1307 return colored(what, colour, attrs=attrs) if with_colours else what
1316 with_decode_path=False,
1317 decode_path_len_decrease=0,
1324 " " if pp.expl_offset is None else
1325 ("-%d" % (pp.offset - pp.expl_offset))
1327 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1329 col = _colourize(col, "red", with_colours, ())
1330 col += _colourize("B", "red", with_colours) if pp.bered else " "
1332 col = "[%d,%d,%4d]%s" % (
1336 LENINDEF_PP_CHAR if pp.lenindef else " "
1338 col = _colourize(col, "green", with_colours, ())
1340 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1341 if decode_path_len > 0:
1342 cols.append(" ." * decode_path_len)
1343 ent = pp.decode_path[-1]
1344 if isinstance(ent, DecodePathDefBy):
1345 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1346 value = str(ent.defined_by)
1348 oids is not None and
1349 ent.defined_by.asn1_type_name ==
1350 ObjectIdentifier.asn1_type_name and
1353 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1355 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1357 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1358 if pp.expl is not None:
1359 klass, _, num = pp.expl
1360 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1361 cols.append(_colourize(col, "blue", with_colours))
1362 if pp.impl is not None:
1363 klass, _, num = pp.impl
1364 col = "[%s%d]" % (TagClassReprs[klass], num)
1365 cols.append(_colourize(col, "blue", with_colours))
1366 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1367 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1369 cols.append(_colourize("BER", "red", with_colours))
1370 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1371 if pp.value is not None:
1373 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1375 oids is not None and
1376 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1379 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1381 if isinstance(pp.blob, binary_type):
1382 cols.append(hexenc(pp.blob))
1383 elif isinstance(pp.blob, tuple):
1384 cols.append(", ".join(pp.blob))
1386 cols.append(_colourize("OPTIONAL", "red", with_colours))
1388 cols.append(_colourize("DEFAULT", "red", with_colours))
1389 if with_decode_path:
1390 cols.append(_colourize(
1391 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1395 return " ".join(cols)
1398 def pp_console_blob(pp, decode_path_len_decrease=0):
1399 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1400 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1401 if decode_path_len > 0:
1402 cols.append(" ." * (decode_path_len + 1))
1403 if isinstance(pp.blob, binary_type):
1404 blob = hexenc(pp.blob).upper()
1405 for i in range(0, len(blob), 32):
1406 chunk = blob[i:i + 32]
1407 yield " ".join(cols + [":".join(
1408 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1410 elif isinstance(pp.blob, tuple):
1411 yield " ".join(cols + [", ".join(pp.blob)])
1419 with_decode_path=False,
1420 decode_path_only=(),
1422 """Pretty print object
1424 :param Obj obj: object you want to pretty print
1425 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1426 from it is met, then its humand readable form is printed
1427 :param big_blobs: if large binary objects are met (like OctetString
1428 values), do we need to print them too, on separate
1430 :param with_colours: colourize output, if ``termcolor`` library
1432 :param with_decode_path: print decode path
1433 :param decode_path_only: print only that specified decode path
1435 def _pprint_pps(pps):
1437 if hasattr(pp, "_fields"):
1439 decode_path_only != () and
1441 str(p) for p in pp.decode_path[:len(decode_path_only)]
1442 ) != decode_path_only
1446 yield pp_console_row(
1451 with_colours=with_colours,
1452 with_decode_path=with_decode_path,
1453 decode_path_len_decrease=len(decode_path_only),
1455 for row in pp_console_blob(
1457 decode_path_len_decrease=len(decode_path_only),
1461 yield pp_console_row(
1466 with_colours=with_colours,
1467 with_decode_path=with_decode_path,
1468 decode_path_len_decrease=len(decode_path_only),
1471 for row in _pprint_pps(pp):
1473 return "\n".join(_pprint_pps(obj.pps()))
1476 ########################################################################
1477 # ASN.1 primitive types
1478 ########################################################################
1481 """``BOOLEAN`` boolean type
1483 >>> b = Boolean(True)
1485 >>> b == Boolean(True)
1491 tag_default = tag_encode(1)
1492 asn1_type_name = "BOOLEAN"
1504 :param value: set the value. Either boolean type, or
1505 :py:class:`pyderasn.Boolean` object
1506 :param bytes impl: override default tag with ``IMPLICIT`` one
1507 :param bytes expl: override default tag with ``EXPLICIT`` one
1508 :param default: set default value. Type same as in ``value``
1509 :param bool optional: is object ``OPTIONAL`` in sequence
1511 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1512 self._value = None if value is None else self._value_sanitize(value)
1513 if default is not None:
1514 default = self._value_sanitize(default)
1515 self.default = self.__class__(
1521 self._value = default
1523 def _value_sanitize(self, value):
1524 if issubclass(value.__class__, Boolean):
1526 if isinstance(value, bool):
1528 raise InvalidValueType((self.__class__, bool))
1532 return self._value is not None
1535 obj = self.__class__()
1536 obj._value = self._value
1538 obj._expl = self._expl
1539 obj.default = self.default
1540 obj.optional = self.optional
1541 obj.offset = self.offset
1542 obj.llen = self.llen
1543 obj.vlen = self.vlen
1546 def __nonzero__(self):
1547 self._assert_ready()
1551 self._assert_ready()
1554 def __eq__(self, their):
1555 if isinstance(their, bool):
1556 return self._value == their
1557 if not issubclass(their.__class__, Boolean):
1560 self._value == their._value and
1561 self.tag == their.tag and
1562 self._expl == their._expl
1573 return self.__class__(
1575 impl=self.tag if impl is None else impl,
1576 expl=self._expl if expl is None else expl,
1577 default=self.default if default is None else default,
1578 optional=self.optional if optional is None else optional,
1582 self._assert_ready()
1586 (b"\xFF" if self._value else b"\x00"),
1589 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1591 t, _, lv = tag_strip(tlv)
1592 except DecodeError as err:
1593 raise err.__class__(
1595 klass=self.__class__,
1596 decode_path=decode_path,
1601 klass=self.__class__,
1602 decode_path=decode_path,
1608 l, _, v = len_decode(lv)
1609 except DecodeError as err:
1610 raise err.__class__(
1612 klass=self.__class__,
1613 decode_path=decode_path,
1617 raise InvalidLength(
1618 "Boolean's length must be equal to 1",
1619 klass=self.__class__,
1620 decode_path=decode_path,
1624 raise NotEnoughData(
1625 "encoded length is longer than data",
1626 klass=self.__class__,
1627 decode_path=decode_path,
1630 first_octet = byte2int(v)
1632 if first_octet == 0:
1634 elif first_octet == 0xFF:
1636 elif ctx.get("bered", False):
1641 "unacceptable Boolean value",
1642 klass=self.__class__,
1643 decode_path=decode_path,
1646 obj = self.__class__(
1650 default=self.default,
1651 optional=self.optional,
1652 _decoded=(offset, 1, 1),
1654 obj.ber_encoded = ber_encoded
1658 return pp_console_row(next(self.pps()))
1660 def pps(self, decode_path=()):
1662 asn1_type_name=self.asn1_type_name,
1663 obj_name=self.__class__.__name__,
1664 decode_path=decode_path,
1665 value=str(self._value) if self.ready else None,
1666 optional=self.optional,
1667 default=self == self.default,
1668 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1669 expl=None if self._expl is None else tag_decode(self._expl),
1674 expl_offset=self.expl_offset if self.expled else None,
1675 expl_tlen=self.expl_tlen if self.expled else None,
1676 expl_llen=self.expl_llen if self.expled else None,
1677 expl_vlen=self.expl_vlen if self.expled else None,
1678 expl_lenindef=self.expl_lenindef,
1679 ber_encoded=self.ber_encoded,
1682 for pp in self.pps_lenindef(decode_path):
1687 """``INTEGER`` integer type
1689 >>> b = Integer(-123)
1691 >>> b == Integer(-123)
1696 >>> Integer(2, bounds=(1, 3))
1698 >>> Integer(5, bounds=(1, 3))
1699 Traceback (most recent call last):
1700 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1704 class Version(Integer):
1711 >>> v = Version("v1")
1718 {'v3': 2, 'v1': 0, 'v2': 1}
1720 __slots__ = ("specs", "_bound_min", "_bound_max")
1721 tag_default = tag_encode(2)
1722 asn1_type_name = "INTEGER"
1736 :param value: set the value. Either integer type, named value
1737 (if ``schema`` is specified in the class), or
1738 :py:class:`pyderasn.Integer` object
1739 :param bounds: set ``(MIN, MAX)`` value constraint.
1740 (-inf, +inf) by default
1741 :param bytes impl: override default tag with ``IMPLICIT`` one
1742 :param bytes expl: override default tag with ``EXPLICIT`` one
1743 :param default: set default value. Type same as in ``value``
1744 :param bool optional: is object ``OPTIONAL`` in sequence
1746 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1748 specs = getattr(self, "schema", {}) if _specs is None else _specs
1749 self.specs = specs if isinstance(specs, dict) else dict(specs)
1750 self._bound_min, self._bound_max = getattr(
1753 (float("-inf"), float("+inf")),
1754 ) if bounds is None else bounds
1755 if value is not None:
1756 self._value = self._value_sanitize(value)
1757 if default is not None:
1758 default = self._value_sanitize(default)
1759 self.default = self.__class__(
1765 if self._value is None:
1766 self._value = default
1768 def _value_sanitize(self, value):
1769 if issubclass(value.__class__, Integer):
1770 value = value._value
1771 elif isinstance(value, integer_types):
1773 elif isinstance(value, str):
1774 value = self.specs.get(value)
1776 raise ObjUnknown("integer value: %s" % value)
1778 raise InvalidValueType((self.__class__, int, str))
1779 if not self._bound_min <= value <= self._bound_max:
1780 raise BoundsError(self._bound_min, value, self._bound_max)
1785 return self._value is not None
1788 obj = self.__class__(_specs=self.specs)
1789 obj._value = self._value
1790 obj._bound_min = self._bound_min
1791 obj._bound_max = self._bound_max
1793 obj._expl = self._expl
1794 obj.default = self.default
1795 obj.optional = self.optional
1796 obj.offset = self.offset
1797 obj.llen = self.llen
1798 obj.vlen = self.vlen
1802 self._assert_ready()
1803 return int(self._value)
1806 self._assert_ready()
1809 bytes(self._expl or b"") +
1810 str(self._value).encode("ascii"),
1813 def __eq__(self, their):
1814 if isinstance(their, integer_types):
1815 return self._value == their
1816 if not issubclass(their.__class__, Integer):
1819 self._value == their._value and
1820 self.tag == their.tag and
1821 self._expl == their._expl
1824 def __lt__(self, their):
1825 return self._value < their._value
1829 for name, value in self.specs.items():
1830 if value == self._value:
1842 return self.__class__(
1845 (self._bound_min, self._bound_max)
1846 if bounds is None else bounds
1848 impl=self.tag if impl is None else impl,
1849 expl=self._expl if expl is None else expl,
1850 default=self.default if default is None else default,
1851 optional=self.optional if optional is None else optional,
1856 self._assert_ready()
1860 octets = bytearray([0])
1864 octets = bytearray()
1866 octets.append((value & 0xFF) ^ 0xFF)
1868 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1871 octets = bytearray()
1873 octets.append(value & 0xFF)
1875 if octets[-1] & 0x80 > 0:
1878 octets = bytes(octets)
1880 bytes_len = ceil(value.bit_length() / 8) or 1
1883 octets = value.to_bytes(
1888 except OverflowError:
1892 return b"".join((self.tag, len_encode(len(octets)), octets))
1894 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1896 t, _, lv = tag_strip(tlv)
1897 except DecodeError as err:
1898 raise err.__class__(
1900 klass=self.__class__,
1901 decode_path=decode_path,
1906 klass=self.__class__,
1907 decode_path=decode_path,
1913 l, llen, v = len_decode(lv)
1914 except DecodeError as err:
1915 raise err.__class__(
1917 klass=self.__class__,
1918 decode_path=decode_path,
1922 raise NotEnoughData(
1923 "encoded length is longer than data",
1924 klass=self.__class__,
1925 decode_path=decode_path,
1929 raise NotEnoughData(
1931 klass=self.__class__,
1932 decode_path=decode_path,
1935 v, tail = v[:l], v[l:]
1936 first_octet = byte2int(v)
1938 second_octet = byte2int(v[1:])
1940 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1941 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1944 "non normalized integer",
1945 klass=self.__class__,
1946 decode_path=decode_path,
1951 if first_octet & 0x80 > 0:
1952 octets = bytearray()
1953 for octet in bytearray(v):
1954 octets.append(octet ^ 0xFF)
1955 for octet in octets:
1956 value = (value << 8) | octet
1960 for octet in bytearray(v):
1961 value = (value << 8) | octet
1963 value = int.from_bytes(v, byteorder="big", signed=True)
1965 obj = self.__class__(
1967 bounds=(self._bound_min, self._bound_max),
1970 default=self.default,
1971 optional=self.optional,
1973 _decoded=(offset, llen, l),
1975 except BoundsError as err:
1978 klass=self.__class__,
1979 decode_path=decode_path,
1985 return pp_console_row(next(self.pps()))
1987 def pps(self, decode_path=()):
1989 asn1_type_name=self.asn1_type_name,
1990 obj_name=self.__class__.__name__,
1991 decode_path=decode_path,
1992 value=(self.named or str(self._value)) if self.ready else None,
1993 optional=self.optional,
1994 default=self == self.default,
1995 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1996 expl=None if self._expl is None else tag_decode(self._expl),
2001 expl_offset=self.expl_offset if self.expled else None,
2002 expl_tlen=self.expl_tlen if self.expled else None,
2003 expl_llen=self.expl_llen if self.expled else None,
2004 expl_vlen=self.expl_vlen if self.expled else None,
2005 expl_lenindef=self.expl_lenindef,
2008 for pp in self.pps_lenindef(decode_path):
2012 class BitString(Obj):
2013 """``BIT STRING`` bit string type
2015 >>> BitString(b"hello world")
2016 BIT STRING 88 bits 68656c6c6f20776f726c64
2019 >>> b == b"hello world"
2024 >>> BitString("'0A3B5F291CD'H")
2025 BIT STRING 44 bits 0a3b5f291cd0
2026 >>> b = BitString("'010110000000'B")
2027 BIT STRING 12 bits 5800
2030 >>> b[0], b[1], b[2], b[3]
2031 (False, True, False, True)
2035 [False, True, False, True, True, False, False, False, False, False, False, False]
2039 class KeyUsage(BitString):
2041 ("digitalSignature", 0),
2042 ("nonRepudiation", 1),
2043 ("keyEncipherment", 2),
2046 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2047 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2049 ['nonRepudiation', 'keyEncipherment']
2051 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2055 Pay attention that BIT STRING can be encoded both in primitive
2056 and constructed forms. Decoder always checks constructed form tag
2057 additionally to specified primitive one. If BER decoding is
2058 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2059 of DER restrictions.
2061 __slots__ = ("tag_constructed", "specs", "defined")
2062 tag_default = tag_encode(3)
2063 asn1_type_name = "BIT STRING"
2076 :param value: set the value. Either binary type, tuple of named
2077 values (if ``schema`` is specified in the class),
2078 string in ``'XXX...'B`` form, or
2079 :py:class:`pyderasn.BitString` object
2080 :param bytes impl: override default tag with ``IMPLICIT`` one
2081 :param bytes expl: override default tag with ``EXPLICIT`` one
2082 :param default: set default value. Type same as in ``value``
2083 :param bool optional: is object ``OPTIONAL`` in sequence
2085 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2086 specs = getattr(self, "schema", {}) if _specs is None else _specs
2087 self.specs = specs if isinstance(specs, dict) else dict(specs)
2088 self._value = None if value is None else self._value_sanitize(value)
2089 if default is not None:
2090 default = self._value_sanitize(default)
2091 self.default = self.__class__(
2097 self._value = default
2099 tag_klass, _, tag_num = tag_decode(self.tag)
2100 self.tag_constructed = tag_encode(
2102 form=TagFormConstructed,
2106 def _bits2octets(self, bits):
2107 if len(self.specs) > 0:
2108 bits = bits.rstrip("0")
2110 bits += "0" * ((8 - (bit_len % 8)) % 8)
2111 octets = bytearray(len(bits) // 8)
2112 for i in six_xrange(len(octets)):
2113 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2114 return bit_len, bytes(octets)
2116 def _value_sanitize(self, value):
2117 if issubclass(value.__class__, BitString):
2119 if isinstance(value, (string_types, binary_type)):
2121 isinstance(value, string_types) and
2122 value.startswith("'")
2124 if value.endswith("'B"):
2126 if not set(value) <= set(("0", "1")):
2127 raise ValueError("B's coding contains unacceptable chars")
2128 return self._bits2octets(value)
2129 elif value.endswith("'H"):
2133 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2135 if isinstance(value, binary_type):
2136 return (len(value) * 8, value)
2138 raise InvalidValueType((self.__class__, string_types, binary_type))
2139 if isinstance(value, tuple):
2142 isinstance(value[0], integer_types) and
2143 isinstance(value[1], binary_type)
2148 bit = self.specs.get(name)
2150 raise ObjUnknown("BitString value: %s" % name)
2153 return self._bits2octets("")
2155 return self._bits2octets("".join(
2156 ("1" if bit in bits else "0")
2157 for bit in six_xrange(max(bits) + 1)
2159 raise InvalidValueType((self.__class__, binary_type, string_types))
2163 return self._value is not None
2166 obj = self.__class__(_specs=self.specs)
2168 if value is not None:
2169 value = (value[0], value[1])
2172 obj._expl = self._expl
2173 obj.default = self.default
2174 obj.optional = self.optional
2175 obj.offset = self.offset
2176 obj.llen = self.llen
2177 obj.vlen = self.vlen
2181 self._assert_ready()
2182 for i in six_xrange(self._value[0]):
2187 self._assert_ready()
2188 return self._value[0]
2190 def __bytes__(self):
2191 self._assert_ready()
2192 return self._value[1]
2194 def __eq__(self, their):
2195 if isinstance(their, bytes):
2196 return self._value[1] == their
2197 if not issubclass(their.__class__, BitString):
2200 self._value == their._value and
2201 self.tag == their.tag and
2202 self._expl == their._expl
2207 return [name for name, bit in self.specs.items() if self[bit]]
2217 return self.__class__(
2219 impl=self.tag if impl is None else impl,
2220 expl=self._expl if expl is None else expl,
2221 default=self.default if default is None else default,
2222 optional=self.optional if optional is None else optional,
2226 def __getitem__(self, key):
2227 if isinstance(key, int):
2228 bit_len, octets = self._value
2232 byte2int(memoryview(octets)[key // 8:]) >>
2235 if isinstance(key, string_types):
2236 value = self.specs.get(key)
2238 raise ObjUnknown("BitString value: %s" % key)
2240 raise InvalidValueType((int, str))
2243 self._assert_ready()
2244 bit_len, octets = self._value
2247 len_encode(len(octets) + 1),
2248 int2byte((8 - bit_len % 8) % 8),
2252 def _decode_chunk(self, lv, offset, decode_path, ctx):
2254 l, llen, v = len_decode(lv)
2255 except DecodeError as err:
2256 raise err.__class__(
2258 klass=self.__class__,
2259 decode_path=decode_path,
2263 raise NotEnoughData(
2264 "encoded length is longer than data",
2265 klass=self.__class__,
2266 decode_path=decode_path,
2270 raise NotEnoughData(
2272 klass=self.__class__,
2273 decode_path=decode_path,
2276 pad_size = byte2int(v)
2277 if l == 1 and pad_size != 0:
2279 "invalid empty value",
2280 klass=self.__class__,
2281 decode_path=decode_path,
2287 klass=self.__class__,
2288 decode_path=decode_path,
2291 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2294 klass=self.__class__,
2295 decode_path=decode_path,
2298 v, tail = v[:l], v[l:]
2299 obj = self.__class__(
2300 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2303 default=self.default,
2304 optional=self.optional,
2306 _decoded=(offset, llen, l),
2310 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2312 t, tlen, lv = tag_strip(tlv)
2313 except DecodeError as err:
2314 raise err.__class__(
2316 klass=self.__class__,
2317 decode_path=decode_path,
2321 if tag_only: # pragma: no cover
2323 return self._decode_chunk(lv, offset, decode_path, ctx)
2324 if t == self.tag_constructed:
2325 if not ctx.get("bered", False):
2327 "unallowed BER constructed encoding",
2328 klass=self.__class__,
2329 decode_path=decode_path,
2332 if tag_only: # pragma: no cover
2336 l, llen, v = len_decode(lv)
2337 except LenIndefForm:
2338 llen, l, v = 1, 0, lv[1:]
2340 except DecodeError as err:
2341 raise err.__class__(
2343 klass=self.__class__,
2344 decode_path=decode_path,
2348 raise NotEnoughData(
2349 "encoded length is longer than data",
2350 klass=self.__class__,
2351 decode_path=decode_path,
2354 if not lenindef and l == 0:
2355 raise NotEnoughData(
2357 klass=self.__class__,
2358 decode_path=decode_path,
2362 sub_offset = offset + tlen + llen
2366 if v[:EOC_LEN].tobytes() == EOC:
2373 "chunk out of bounds",
2374 klass=self.__class__,
2375 decode_path=decode_path + (str(len(chunks) - 1),),
2376 offset=chunks[-1].offset,
2378 sub_decode_path = decode_path + (str(len(chunks)),)
2380 chunk, v_tail = BitString().decode(
2383 decode_path=sub_decode_path,
2389 "expected BitString encoded chunk",
2390 klass=self.__class__,
2391 decode_path=sub_decode_path,
2394 chunks.append(chunk)
2395 sub_offset += chunk.tlvlen
2396 vlen += chunk.tlvlen
2398 if len(chunks) == 0:
2401 klass=self.__class__,
2402 decode_path=decode_path,
2407 for chunk_i, chunk in enumerate(chunks[:-1]):
2408 if chunk.bit_len % 8 != 0:
2410 "BitString chunk is not multiple of 8 bits",
2411 klass=self.__class__,
2412 decode_path=decode_path + (str(chunk_i),),
2413 offset=chunk.offset,
2415 values.append(bytes(chunk))
2416 bit_len += chunk.bit_len
2417 chunk_last = chunks[-1]
2418 values.append(bytes(chunk_last))
2419 bit_len += chunk_last.bit_len
2420 obj = self.__class__(
2421 value=(bit_len, b"".join(values)),
2424 default=self.default,
2425 optional=self.optional,
2427 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2429 obj.lenindef = lenindef
2430 obj.ber_encoded = True
2431 return obj, (v[EOC_LEN:] if lenindef else v)
2433 klass=self.__class__,
2434 decode_path=decode_path,
2439 return pp_console_row(next(self.pps()))
2441 def pps(self, decode_path=()):
2445 bit_len, blob = self._value
2446 value = "%d bits" % bit_len
2447 if len(self.specs) > 0:
2448 blob = tuple(self.named)
2450 asn1_type_name=self.asn1_type_name,
2451 obj_name=self.__class__.__name__,
2452 decode_path=decode_path,
2455 optional=self.optional,
2456 default=self == self.default,
2457 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2458 expl=None if self._expl is None else tag_decode(self._expl),
2463 expl_offset=self.expl_offset if self.expled else None,
2464 expl_tlen=self.expl_tlen if self.expled else None,
2465 expl_llen=self.expl_llen if self.expled else None,
2466 expl_vlen=self.expl_vlen if self.expled else None,
2467 expl_lenindef=self.expl_lenindef,
2468 lenindef=self.lenindef,
2469 ber_encoded=self.ber_encoded,
2472 defined_by, defined = self.defined or (None, None)
2473 if defined_by is not None:
2475 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2477 for pp in self.pps_lenindef(decode_path):
2481 class OctetString(Obj):
2482 """``OCTET STRING`` binary string type
2484 >>> s = OctetString(b"hello world")
2485 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2486 >>> s == OctetString(b"hello world")
2491 >>> OctetString(b"hello", bounds=(4, 4))
2492 Traceback (most recent call last):
2493 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2494 >>> OctetString(b"hell", bounds=(4, 4))
2495 OCTET STRING 4 bytes 68656c6c
2499 Pay attention that OCTET STRING can be encoded both in primitive
2500 and constructed forms. Decoder always checks constructed form tag
2501 additionally to specified primitive one. If BER decoding is
2502 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2503 of DER restrictions.
2505 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2506 tag_default = tag_encode(4)
2507 asn1_type_name = "OCTET STRING"
2520 :param value: set the value. Either binary type, or
2521 :py:class:`pyderasn.OctetString` object
2522 :param bounds: set ``(MIN, MAX)`` value size constraint.
2523 (-inf, +inf) by default
2524 :param bytes impl: override default tag with ``IMPLICIT`` one
2525 :param bytes expl: override default tag with ``EXPLICIT`` one
2526 :param default: set default value. Type same as in ``value``
2527 :param bool optional: is object ``OPTIONAL`` in sequence
2529 super(OctetString, self).__init__(
2537 self._bound_min, self._bound_max = getattr(
2541 ) if bounds is None else bounds
2542 if value is not None:
2543 self._value = self._value_sanitize(value)
2544 if default is not None:
2545 default = self._value_sanitize(default)
2546 self.default = self.__class__(
2551 if self._value is None:
2552 self._value = default
2554 tag_klass, _, tag_num = tag_decode(self.tag)
2555 self.tag_constructed = tag_encode(
2557 form=TagFormConstructed,
2561 def _value_sanitize(self, value):
2562 if issubclass(value.__class__, OctetString):
2563 value = value._value
2564 elif isinstance(value, binary_type):
2567 raise InvalidValueType((self.__class__, bytes))
2568 if not self._bound_min <= len(value) <= self._bound_max:
2569 raise BoundsError(self._bound_min, len(value), self._bound_max)
2574 return self._value is not None
2577 obj = self.__class__()
2578 obj._value = self._value
2579 obj._bound_min = self._bound_min
2580 obj._bound_max = self._bound_max
2582 obj._expl = self._expl
2583 obj.default = self.default
2584 obj.optional = self.optional
2585 obj.offset = self.offset
2586 obj.llen = self.llen
2587 obj.vlen = self.vlen
2590 def __bytes__(self):
2591 self._assert_ready()
2594 def __eq__(self, their):
2595 if isinstance(their, binary_type):
2596 return self._value == their
2597 if not issubclass(their.__class__, OctetString):
2600 self._value == their._value and
2601 self.tag == their.tag and
2602 self._expl == their._expl
2605 def __lt__(self, their):
2606 return self._value < their._value
2617 return self.__class__(
2620 (self._bound_min, self._bound_max)
2621 if bounds is None else bounds
2623 impl=self.tag if impl is None else impl,
2624 expl=self._expl if expl is None else expl,
2625 default=self.default if default is None else default,
2626 optional=self.optional if optional is None else optional,
2630 self._assert_ready()
2633 len_encode(len(self._value)),
2637 def _decode_chunk(self, lv, offset, decode_path, ctx):
2639 l, llen, v = len_decode(lv)
2640 except DecodeError as err:
2641 raise err.__class__(
2643 klass=self.__class__,
2644 decode_path=decode_path,
2648 raise NotEnoughData(
2649 "encoded length is longer than data",
2650 klass=self.__class__,
2651 decode_path=decode_path,
2654 v, tail = v[:l], v[l:]
2656 obj = self.__class__(
2658 bounds=(self._bound_min, self._bound_max),
2661 default=self.default,
2662 optional=self.optional,
2663 _decoded=(offset, llen, l),
2665 except DecodeError as err:
2668 klass=self.__class__,
2669 decode_path=decode_path,
2672 except BoundsError as err:
2675 klass=self.__class__,
2676 decode_path=decode_path,
2681 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2683 t, tlen, lv = tag_strip(tlv)
2684 except DecodeError as err:
2685 raise err.__class__(
2687 klass=self.__class__,
2688 decode_path=decode_path,
2694 return self._decode_chunk(lv, offset, decode_path, ctx)
2695 if t == self.tag_constructed:
2696 if not ctx.get("bered", False):
2698 "unallowed BER constructed encoding",
2699 klass=self.__class__,
2700 decode_path=decode_path,
2707 l, llen, v = len_decode(lv)
2708 except LenIndefForm:
2709 llen, l, v = 1, 0, lv[1:]
2711 except DecodeError as err:
2712 raise err.__class__(
2714 klass=self.__class__,
2715 decode_path=decode_path,
2719 raise NotEnoughData(
2720 "encoded length is longer than data",
2721 klass=self.__class__,
2722 decode_path=decode_path,
2726 sub_offset = offset + tlen + llen
2730 if v[:EOC_LEN].tobytes() == EOC:
2737 "chunk out of bounds",
2738 klass=self.__class__,
2739 decode_path=decode_path + (str(len(chunks) - 1),),
2740 offset=chunks[-1].offset,
2742 sub_decode_path = decode_path + (str(len(chunks)),)
2744 chunk, v_tail = OctetString().decode(
2747 decode_path=sub_decode_path,
2753 "expected OctetString encoded chunk",
2754 klass=self.__class__,
2755 decode_path=sub_decode_path,
2758 chunks.append(chunk)
2759 sub_offset += chunk.tlvlen
2760 vlen += chunk.tlvlen
2763 obj = self.__class__(
2764 value=b"".join(bytes(chunk) for chunk in chunks),
2765 bounds=(self._bound_min, self._bound_max),
2768 default=self.default,
2769 optional=self.optional,
2770 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2772 except DecodeError as err:
2775 klass=self.__class__,
2776 decode_path=decode_path,
2779 except BoundsError as err:
2782 klass=self.__class__,
2783 decode_path=decode_path,
2786 obj.lenindef = lenindef
2787 obj.ber_encoded = True
2788 return obj, (v[EOC_LEN:] if lenindef else v)
2790 klass=self.__class__,
2791 decode_path=decode_path,
2796 return pp_console_row(next(self.pps()))
2798 def pps(self, decode_path=()):
2800 asn1_type_name=self.asn1_type_name,
2801 obj_name=self.__class__.__name__,
2802 decode_path=decode_path,
2803 value=("%d bytes" % len(self._value)) if self.ready else None,
2804 blob=self._value if self.ready else None,
2805 optional=self.optional,
2806 default=self == self.default,
2807 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2808 expl=None if self._expl is None else tag_decode(self._expl),
2813 expl_offset=self.expl_offset if self.expled else None,
2814 expl_tlen=self.expl_tlen if self.expled else None,
2815 expl_llen=self.expl_llen if self.expled else None,
2816 expl_vlen=self.expl_vlen if self.expled else None,
2817 expl_lenindef=self.expl_lenindef,
2818 lenindef=self.lenindef,
2819 ber_encoded=self.ber_encoded,
2822 defined_by, defined = self.defined or (None, None)
2823 if defined_by is not None:
2825 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2827 for pp in self.pps_lenindef(decode_path):
2832 """``NULL`` null object
2840 tag_default = tag_encode(5)
2841 asn1_type_name = "NULL"
2845 value=None, # unused, but Sequence passes it
2852 :param bytes impl: override default tag with ``IMPLICIT`` one
2853 :param bytes expl: override default tag with ``EXPLICIT`` one
2854 :param bool optional: is object ``OPTIONAL`` in sequence
2856 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2864 obj = self.__class__()
2866 obj._expl = self._expl
2867 obj.default = self.default
2868 obj.optional = self.optional
2869 obj.offset = self.offset
2870 obj.llen = self.llen
2871 obj.vlen = self.vlen
2874 def __eq__(self, their):
2875 if not issubclass(their.__class__, Null):
2878 self.tag == their.tag and
2879 self._expl == their._expl
2889 return self.__class__(
2890 impl=self.tag if impl is None else impl,
2891 expl=self._expl if expl is None else expl,
2892 optional=self.optional if optional is None else optional,
2896 return self.tag + len_encode(0)
2898 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2900 t, _, lv = tag_strip(tlv)
2901 except DecodeError as err:
2902 raise err.__class__(
2904 klass=self.__class__,
2905 decode_path=decode_path,
2910 klass=self.__class__,
2911 decode_path=decode_path,
2914 if tag_only: # pragma: no cover
2917 l, _, v = len_decode(lv)
2918 except DecodeError as err:
2919 raise err.__class__(
2921 klass=self.__class__,
2922 decode_path=decode_path,
2926 raise InvalidLength(
2927 "Null must have zero length",
2928 klass=self.__class__,
2929 decode_path=decode_path,
2932 obj = self.__class__(
2935 optional=self.optional,
2936 _decoded=(offset, 1, 0),
2941 return pp_console_row(next(self.pps()))
2943 def pps(self, decode_path=()):
2945 asn1_type_name=self.asn1_type_name,
2946 obj_name=self.__class__.__name__,
2947 decode_path=decode_path,
2948 optional=self.optional,
2949 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2950 expl=None if self._expl is None else tag_decode(self._expl),
2955 expl_offset=self.expl_offset if self.expled else None,
2956 expl_tlen=self.expl_tlen if self.expled else None,
2957 expl_llen=self.expl_llen if self.expled else None,
2958 expl_vlen=self.expl_vlen if self.expled else None,
2959 expl_lenindef=self.expl_lenindef,
2962 for pp in self.pps_lenindef(decode_path):
2966 class ObjectIdentifier(Obj):
2967 """``OBJECT IDENTIFIER`` OID type
2969 >>> oid = ObjectIdentifier((1, 2, 3))
2970 OBJECT IDENTIFIER 1.2.3
2971 >>> oid == ObjectIdentifier("1.2.3")
2977 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2978 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2980 >>> str(ObjectIdentifier((3, 1)))
2981 Traceback (most recent call last):
2982 pyderasn.InvalidOID: unacceptable first arc value
2984 __slots__ = ("defines",)
2985 tag_default = tag_encode(6)
2986 asn1_type_name = "OBJECT IDENTIFIER"
2999 :param value: set the value. Either tuples of integers,
3000 string of "."-concatenated integers, or
3001 :py:class:`pyderasn.ObjectIdentifier` object
3002 :param defines: sequence of tuples. Each tuple has two elements.
3003 First one is relative to current one decode
3004 path, aiming to the field defined by that OID.
3005 Read about relative path in
3006 :py:func:`pyderasn.abs_decode_path`. Second
3007 tuple element is ``{OID: pyderasn.Obj()}``
3008 dictionary, mapping between current OID value
3009 and structure applied to defined field.
3010 :ref:`Read about DEFINED BY <definedby>`
3011 :param bytes impl: override default tag with ``IMPLICIT`` one
3012 :param bytes expl: override default tag with ``EXPLICIT`` one
3013 :param default: set default value. Type same as in ``value``
3014 :param bool optional: is object ``OPTIONAL`` in sequence
3016 super(ObjectIdentifier, self).__init__(
3024 if value is not None:
3025 self._value = self._value_sanitize(value)
3026 if default is not None:
3027 default = self._value_sanitize(default)
3028 self.default = self.__class__(
3033 if self._value is None:
3034 self._value = default
3035 self.defines = defines
3037 def __add__(self, their):
3038 if isinstance(their, self.__class__):
3039 return self.__class__(self._value + their._value)
3040 if isinstance(their, tuple):
3041 return self.__class__(self._value + their)
3042 raise InvalidValueType((self.__class__, tuple))
3044 def _value_sanitize(self, value):
3045 if issubclass(value.__class__, ObjectIdentifier):
3047 if isinstance(value, string_types):
3049 value = tuple(int(arc) for arc in value.split("."))
3051 raise InvalidOID("unacceptable arcs values")
3052 if isinstance(value, tuple):
3054 raise InvalidOID("less than 2 arcs")
3055 first_arc = value[0]
3056 if first_arc in (0, 1):
3057 if not (0 <= value[1] <= 39):
3058 raise InvalidOID("second arc is too wide")
3059 elif first_arc == 2:
3062 raise InvalidOID("unacceptable first arc value")
3064 raise InvalidValueType((self.__class__, str, tuple))
3068 return self._value is not None
3071 obj = self.__class__()
3072 obj._value = self._value
3073 obj.defines = self.defines
3075 obj._expl = self._expl
3076 obj.default = self.default
3077 obj.optional = self.optional
3078 obj.offset = self.offset
3079 obj.llen = self.llen
3080 obj.vlen = self.vlen
3084 self._assert_ready()
3085 return iter(self._value)
3088 return ".".join(str(arc) for arc in self._value or ())
3091 self._assert_ready()
3094 bytes(self._expl or b"") +
3095 str(self._value).encode("ascii"),
3098 def __eq__(self, their):
3099 if isinstance(their, tuple):
3100 return self._value == their
3101 if not issubclass(their.__class__, ObjectIdentifier):
3104 self.tag == their.tag and
3105 self._expl == their._expl and
3106 self._value == their._value
3109 def __lt__(self, their):
3110 return self._value < their._value
3121 return self.__class__(
3123 defines=self.defines if defines is None else defines,
3124 impl=self.tag if impl is None else impl,
3125 expl=self._expl if expl is None else expl,
3126 default=self.default if default is None else default,
3127 optional=self.optional if optional is None else optional,
3131 self._assert_ready()
3133 first_value = value[1]
3134 first_arc = value[0]
3137 elif first_arc == 1:
3139 elif first_arc == 2:
3141 else: # pragma: no cover
3142 raise RuntimeError("invalid arc is stored")
3143 octets = [zero_ended_encode(first_value)]
3144 for arc in value[2:]:
3145 octets.append(zero_ended_encode(arc))
3146 v = b"".join(octets)
3147 return b"".join((self.tag, len_encode(len(v)), v))
3149 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3151 t, _, lv = tag_strip(tlv)
3152 except DecodeError as err:
3153 raise err.__class__(
3155 klass=self.__class__,
3156 decode_path=decode_path,
3161 klass=self.__class__,
3162 decode_path=decode_path,
3165 if tag_only: # pragma: no cover
3168 l, llen, v = len_decode(lv)
3169 except DecodeError as err:
3170 raise err.__class__(
3172 klass=self.__class__,
3173 decode_path=decode_path,
3177 raise NotEnoughData(
3178 "encoded length is longer than data",
3179 klass=self.__class__,
3180 decode_path=decode_path,
3184 raise NotEnoughData(
3186 klass=self.__class__,
3187 decode_path=decode_path,
3190 v, tail = v[:l], v[l:]
3196 octet = indexbytes(v, i)
3197 arc = (arc << 7) | (octet & 0x7F)
3198 if octet & 0x80 == 0:
3206 klass=self.__class__,
3207 decode_path=decode_path,
3211 second_arc = arcs[0]
3212 if 0 <= second_arc <= 39:
3214 elif 40 <= second_arc <= 79:
3220 obj = self.__class__(
3221 value=tuple([first_arc, second_arc] + arcs[1:]),
3224 default=self.default,
3225 optional=self.optional,
3226 _decoded=(offset, llen, l),
3231 return pp_console_row(next(self.pps()))
3233 def pps(self, decode_path=()):
3235 asn1_type_name=self.asn1_type_name,
3236 obj_name=self.__class__.__name__,
3237 decode_path=decode_path,
3238 value=str(self) if self.ready else None,
3239 optional=self.optional,
3240 default=self == self.default,
3241 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3242 expl=None if self._expl is None else tag_decode(self._expl),
3247 expl_offset=self.expl_offset if self.expled else None,
3248 expl_tlen=self.expl_tlen if self.expled else None,
3249 expl_llen=self.expl_llen if self.expled else None,
3250 expl_vlen=self.expl_vlen if self.expled else None,
3251 expl_lenindef=self.expl_lenindef,
3254 for pp in self.pps_lenindef(decode_path):
3258 class Enumerated(Integer):
3259 """``ENUMERATED`` integer type
3261 This type is identical to :py:class:`pyderasn.Integer`, but requires
3262 schema to be specified and does not accept values missing from it.
3265 tag_default = tag_encode(10)
3266 asn1_type_name = "ENUMERATED"
3277 bounds=None, # dummy argument, workability for Integer.decode
3279 super(Enumerated, self).__init__(
3288 if len(self.specs) == 0:
3289 raise ValueError("schema must be specified")
3291 def _value_sanitize(self, value):
3292 if isinstance(value, self.__class__):
3293 value = value._value
3294 elif isinstance(value, integer_types):
3295 if value not in list(self.specs.values()):
3297 "unknown integer value: %s" % value,
3298 klass=self.__class__,
3300 elif isinstance(value, string_types):
3301 value = self.specs.get(value)
3303 raise ObjUnknown("integer value: %s" % value)
3305 raise InvalidValueType((self.__class__, int, str))
3309 obj = self.__class__(_specs=self.specs)
3310 obj._value = self._value
3311 obj._bound_min = self._bound_min
3312 obj._bound_max = self._bound_max
3314 obj._expl = self._expl
3315 obj.default = self.default
3316 obj.optional = self.optional
3317 obj.offset = self.offset
3318 obj.llen = self.llen
3319 obj.vlen = self.vlen
3331 return self.__class__(
3333 impl=self.tag if impl is None else impl,
3334 expl=self._expl if expl is None else expl,
3335 default=self.default if default is None else default,
3336 optional=self.optional if optional is None else optional,
3341 class CommonString(OctetString):
3342 """Common class for all strings
3344 Everything resembles :py:class:`pyderasn.OctetString`, except
3345 ability to deal with unicode text strings.
3347 >>> hexenc("привет мир".encode("utf-8"))
3348 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3349 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3351 >>> s = UTF8String("привет мир")
3352 UTF8String UTF8String привет мир
3354 'привет мир'
3355 >>> hexenc(bytes(s))
3356 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3358 >>> PrintableString("привет мир")
3359 Traceback (most recent call last):
3360 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3362 >>> BMPString("ада", bounds=(2, 2))
3363 Traceback (most recent call last):
3364 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3365 >>> s = BMPString("ад", bounds=(2, 2))
3368 >>> hexenc(bytes(s))
3376 * - :py:class:`pyderasn.UTF8String`
3378 * - :py:class:`pyderasn.NumericString`
3380 * - :py:class:`pyderasn.PrintableString`
3382 * - :py:class:`pyderasn.TeletexString`
3384 * - :py:class:`pyderasn.T61String`
3386 * - :py:class:`pyderasn.VideotexString`
3388 * - :py:class:`pyderasn.IA5String`
3390 * - :py:class:`pyderasn.GraphicString`
3392 * - :py:class:`pyderasn.VisibleString`
3394 * - :py:class:`pyderasn.ISO646String`
3396 * - :py:class:`pyderasn.GeneralString`
3398 * - :py:class:`pyderasn.UniversalString`
3400 * - :py:class:`pyderasn.BMPString`
3403 __slots__ = ("encoding",)
3405 def _value_sanitize(self, value):
3407 value_decoded = None
3408 if isinstance(value, self.__class__):
3409 value_raw = value._value
3410 elif isinstance(value, text_type):
3411 value_decoded = value
3412 elif isinstance(value, binary_type):
3415 raise InvalidValueType((self.__class__, text_type, binary_type))
3418 value_decoded.encode(self.encoding)
3419 if value_raw is None else value_raw
3422 value_raw.decode(self.encoding)
3423 if value_decoded is None else value_decoded
3425 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3426 raise DecodeError(str(err))
3427 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3435 def __eq__(self, their):
3436 if isinstance(their, binary_type):
3437 return self._value == their
3438 if isinstance(their, text_type):
3439 return self._value == their.encode(self.encoding)
3440 if not isinstance(their, self.__class__):
3443 self._value == their._value and
3444 self.tag == their.tag and
3445 self._expl == their._expl
3448 def __unicode__(self):
3450 return self._value.decode(self.encoding)
3451 return text_type(self._value)
3454 return pp_console_row(next(self.pps(no_unicode=PY2)))
3456 def pps(self, decode_path=(), no_unicode=False):
3459 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3461 asn1_type_name=self.asn1_type_name,
3462 obj_name=self.__class__.__name__,
3463 decode_path=decode_path,
3465 optional=self.optional,
3466 default=self == self.default,
3467 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3468 expl=None if self._expl is None else tag_decode(self._expl),
3473 expl_offset=self.expl_offset if self.expled else None,
3474 expl_tlen=self.expl_tlen if self.expled else None,
3475 expl_llen=self.expl_llen if self.expled else None,
3476 expl_vlen=self.expl_vlen if self.expled else None,
3477 expl_lenindef=self.expl_lenindef,
3478 ber_encoded=self.ber_encoded,
3481 for pp in self.pps_lenindef(decode_path):
3485 class UTF8String(CommonString):
3487 tag_default = tag_encode(12)
3489 asn1_type_name = "UTF8String"
3492 class NumericString(CommonString):
3495 Its value is properly sanitized: only ASCII digits with spaces can
3499 tag_default = tag_encode(18)
3501 asn1_type_name = "NumericString"
3502 allowable_chars = set(digits.encode("ascii") + b" ")
3504 def _value_sanitize(self, value):
3505 value = super(NumericString, self)._value_sanitize(value)
3506 if not set(value) <= self.allowable_chars:
3507 raise DecodeError("non-numeric value")
3511 class PrintableString(CommonString):
3514 Its value is properly sanitized: see X.680 41.4 table 10.
3517 tag_default = tag_encode(19)
3519 asn1_type_name = "PrintableString"
3520 allowable_chars = set((ascii_letters + digits + " '()+,-./:=?").encode("ascii"))
3522 def _value_sanitize(self, value):
3523 value = super(PrintableString, self)._value_sanitize(value)
3524 if not set(value) <= self.allowable_chars:
3525 raise DecodeError("non-printable value")
3529 class TeletexString(CommonString):
3531 tag_default = tag_encode(20)
3533 asn1_type_name = "TeletexString"
3536 class T61String(TeletexString):
3538 asn1_type_name = "T61String"
3541 class VideotexString(CommonString):
3543 tag_default = tag_encode(21)
3544 encoding = "iso-8859-1"
3545 asn1_type_name = "VideotexString"
3548 class IA5String(CommonString):
3550 tag_default = tag_encode(22)
3552 asn1_type_name = "IA5"
3555 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3556 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3557 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3560 class UTCTime(CommonString):
3561 """``UTCTime`` datetime type
3563 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3564 UTCTime UTCTime 2017-09-30T22:07:50
3570 datetime.datetime(2017, 9, 30, 22, 7, 50)
3571 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3572 datetime.datetime(1957, 9, 30, 22, 7, 50)
3575 tag_default = tag_encode(23)
3577 asn1_type_name = "UTCTime"
3579 fmt = "%y%m%d%H%M%SZ"
3589 bounds=None, # dummy argument, workability for OctetString.decode
3592 :param value: set the value. Either datetime type, or
3593 :py:class:`pyderasn.UTCTime` object
3594 :param bytes impl: override default tag with ``IMPLICIT`` one
3595 :param bytes expl: override default tag with ``EXPLICIT`` one
3596 :param default: set default value. Type same as in ``value``
3597 :param bool optional: is object ``OPTIONAL`` in sequence
3599 super(UTCTime, self).__init__(
3607 if value is not None:
3608 self._value = self._value_sanitize(value)
3609 if default is not None:
3610 default = self._value_sanitize(default)
3611 self.default = self.__class__(
3616 if self._value is None:
3617 self._value = default
3619 def _value_sanitize(self, value):
3620 if isinstance(value, self.__class__):
3622 if isinstance(value, datetime):
3623 return value.strftime(self.fmt).encode("ascii")
3624 if isinstance(value, binary_type):
3626 value_decoded = value.decode("ascii")
3627 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3628 raise DecodeError("invalid UTCTime encoding")
3629 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3631 datetime.strptime(value_decoded, self.fmt)
3632 except (TypeError, ValueError):
3633 raise DecodeError("invalid UTCTime format")
3636 raise DecodeError("invalid UTCTime length")
3637 raise InvalidValueType((self.__class__, datetime))
3639 def __eq__(self, their):
3640 if isinstance(their, binary_type):
3641 return self._value == their
3642 if isinstance(their, datetime):
3643 return self.todatetime() == their
3644 if not isinstance(their, self.__class__):
3647 self._value == their._value and
3648 self.tag == their.tag and
3649 self._expl == their._expl
3652 def todatetime(self):
3653 """Convert to datetime
3657 Pay attention that UTCTime can not hold full year, so all years
3658 having < 50 years are treated as 20xx, 19xx otherwise, according
3659 to X.509 recomendation.
3661 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3662 year = value.year % 100
3664 year=(2000 + year) if year < 50 else (1900 + year),
3668 minute=value.minute,
3669 second=value.second,
3673 return pp_console_row(next(self.pps()))
3675 def pps(self, decode_path=()):
3677 asn1_type_name=self.asn1_type_name,
3678 obj_name=self.__class__.__name__,
3679 decode_path=decode_path,
3680 value=self.todatetime().isoformat() if self.ready else None,
3681 optional=self.optional,
3682 default=self == self.default,
3683 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3684 expl=None if self._expl is None else tag_decode(self._expl),
3689 expl_offset=self.expl_offset if self.expled else None,
3690 expl_tlen=self.expl_tlen if self.expled else None,
3691 expl_llen=self.expl_llen if self.expled else None,
3692 expl_vlen=self.expl_vlen if self.expled else None,
3693 expl_lenindef=self.expl_lenindef,
3694 ber_encoded=self.ber_encoded,
3697 for pp in self.pps_lenindef(decode_path):
3701 class GeneralizedTime(UTCTime):
3702 """``GeneralizedTime`` datetime type
3704 This type is similar to :py:class:`pyderasn.UTCTime`.
3706 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3707 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3709 '20170930220750.000123Z'
3710 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3711 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3714 tag_default = tag_encode(24)
3715 asn1_type_name = "GeneralizedTime"
3717 fmt = "%Y%m%d%H%M%SZ"
3718 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3720 def _value_sanitize(self, value):
3721 if isinstance(value, self.__class__):
3723 if isinstance(value, datetime):
3724 return value.strftime(
3725 self.fmt_ms if value.microsecond > 0 else self.fmt
3727 if isinstance(value, binary_type):
3729 value_decoded = value.decode("ascii")
3730 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3731 raise DecodeError("invalid GeneralizedTime encoding")
3732 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3734 datetime.strptime(value_decoded, self.fmt)
3735 except (TypeError, ValueError):
3737 "invalid GeneralizedTime (without ms) format",
3740 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3742 datetime.strptime(value_decoded, self.fmt_ms)
3743 except (TypeError, ValueError):
3745 "invalid GeneralizedTime (with ms) format",
3750 "invalid GeneralizedTime length",
3751 klass=self.__class__,
3753 raise InvalidValueType((self.__class__, datetime))
3755 def todatetime(self):
3756 value = self._value.decode("ascii")
3757 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3758 return datetime.strptime(value, self.fmt)
3759 return datetime.strptime(value, self.fmt_ms)
3762 class GraphicString(CommonString):
3764 tag_default = tag_encode(25)
3765 encoding = "iso-8859-1"
3766 asn1_type_name = "GraphicString"
3769 class VisibleString(CommonString):
3771 tag_default = tag_encode(26)
3773 asn1_type_name = "VisibleString"
3776 class ISO646String(VisibleString):
3778 asn1_type_name = "ISO646String"
3781 class GeneralString(CommonString):
3783 tag_default = tag_encode(27)
3784 encoding = "iso-8859-1"
3785 asn1_type_name = "GeneralString"
3788 class UniversalString(CommonString):
3790 tag_default = tag_encode(28)
3791 encoding = "utf-32-be"
3792 asn1_type_name = "UniversalString"
3795 class BMPString(CommonString):
3797 tag_default = tag_encode(30)
3798 encoding = "utf-16-be"
3799 asn1_type_name = "BMPString"
3803 """``CHOICE`` special type
3807 class GeneralName(Choice):
3809 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3810 ("dNSName", IA5String(impl=tag_ctxp(2))),
3813 >>> gn = GeneralName()
3815 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3816 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3817 >>> gn["dNSName"] = IA5String("bar.baz")
3818 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3819 >>> gn["rfc822Name"]
3822 [2] IA5String IA5 bar.baz
3825 >>> gn.value == gn["dNSName"]
3828 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3830 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3831 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3833 __slots__ = ("specs",)
3835 asn1_type_name = "CHOICE"
3848 :param value: set the value. Either ``(choice, value)`` tuple, or
3849 :py:class:`pyderasn.Choice` object
3850 :param bytes impl: can not be set, do **not** use it
3851 :param bytes expl: override default tag with ``EXPLICIT`` one
3852 :param default: set default value. Type same as in ``value``
3853 :param bool optional: is object ``OPTIONAL`` in sequence
3855 if impl is not None:
3856 raise ValueError("no implicit tag allowed for CHOICE")
3857 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3859 schema = getattr(self, "schema", ())
3860 if len(schema) == 0:
3861 raise ValueError("schema must be specified")
3863 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3866 if value is not None:
3867 self._value = self._value_sanitize(value)
3868 if default is not None:
3869 default_value = self._value_sanitize(default)
3870 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3871 default_obj.specs = self.specs
3872 default_obj._value = default_value
3873 self.default = default_obj
3875 self._value = default_obj.copy()._value
3877 def _value_sanitize(self, value):
3878 if isinstance(value, self.__class__):
3880 if isinstance(value, tuple) and len(value) == 2:
3882 spec = self.specs.get(choice)
3884 raise ObjUnknown(choice)
3885 if not isinstance(obj, spec.__class__):
3886 raise InvalidValueType((spec,))
3887 return (choice, spec(obj))
3888 raise InvalidValueType((self.__class__, tuple))
3892 return self._value is not None and self._value[1].ready
3896 return self.expl_lenindef or (
3897 (self._value is not None) and
3898 self._value[1].bered
3902 obj = self.__class__(schema=self.specs)
3903 obj._expl = self._expl
3904 obj.default = self.default
3905 obj.optional = self.optional
3906 obj.offset = self.offset
3907 obj.llen = self.llen
3908 obj.vlen = self.vlen
3910 if value is not None:
3911 obj._value = (value[0], value[1].copy())
3914 def __eq__(self, their):
3915 if isinstance(their, tuple) and len(their) == 2:
3916 return self._value == their
3917 if not isinstance(their, self.__class__):
3920 self.specs == their.specs and
3921 self._value == their._value
3931 return self.__class__(
3934 expl=self._expl if expl is None else expl,
3935 default=self.default if default is None else default,
3936 optional=self.optional if optional is None else optional,
3941 self._assert_ready()
3942 return self._value[0]
3946 self._assert_ready()
3947 return self._value[1]
3949 def __getitem__(self, key):
3950 if key not in self.specs:
3951 raise ObjUnknown(key)
3952 if self._value is None:
3954 choice, value = self._value
3959 def __setitem__(self, key, value):
3960 spec = self.specs.get(key)
3962 raise ObjUnknown(key)
3963 if not isinstance(value, spec.__class__):
3964 raise InvalidValueType((spec.__class__,))
3965 self._value = (key, spec(value))
3973 return self._value[1].decoded if self.ready else False
3976 self._assert_ready()
3977 return self._value[1].encode()
3979 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3980 for choice, spec in self.specs.items():
3981 sub_decode_path = decode_path + (choice,)
3987 decode_path=sub_decode_path,
3996 klass=self.__class__,
3997 decode_path=decode_path,
4000 if tag_only: # pragma: no cover
4002 value, tail = spec.decode(
4006 decode_path=sub_decode_path,
4009 obj = self.__class__(
4012 default=self.default,
4013 optional=self.optional,
4014 _decoded=(offset, 0, value.fulllen),
4016 obj._value = (choice, value)
4020 value = pp_console_row(next(self.pps()))
4022 value = "%s[%r]" % (value, self.value)
4025 def pps(self, decode_path=()):
4027 asn1_type_name=self.asn1_type_name,
4028 obj_name=self.__class__.__name__,
4029 decode_path=decode_path,
4030 value=self.choice if self.ready else None,
4031 optional=self.optional,
4032 default=self == self.default,
4033 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4034 expl=None if self._expl is None else tag_decode(self._expl),
4039 expl_lenindef=self.expl_lenindef,
4043 yield self.value.pps(decode_path=decode_path + (self.choice,))
4044 for pp in self.pps_lenindef(decode_path):
4048 class PrimitiveTypes(Choice):
4049 """Predefined ``CHOICE`` for all generic primitive types
4051 It could be useful for general decoding of some unspecified values:
4053 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4054 OCTET STRING 3 bytes 666f6f
4055 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4059 schema = tuple((klass.__name__, klass()) for klass in (
4084 """``ANY`` special type
4086 >>> Any(Integer(-123))
4088 >>> a = Any(OctetString(b"hello world").encode())
4089 ANY 040b68656c6c6f20776f726c64
4090 >>> hexenc(bytes(a))
4091 b'0x040x0bhello world'
4093 __slots__ = ("defined",)
4094 tag_default = tag_encode(0)
4095 asn1_type_name = "ANY"
4105 :param value: set the value. Either any kind of pyderasn's
4106 **ready** object, or bytes. Pay attention that
4107 **no** validation is performed is raw binary value
4109 :param bytes expl: override default tag with ``EXPLICIT`` one
4110 :param bool optional: is object ``OPTIONAL`` in sequence
4112 super(Any, self).__init__(None, expl, None, optional, _decoded)
4113 self._value = None if value is None else self._value_sanitize(value)
4116 def _value_sanitize(self, value):
4117 if isinstance(value, self.__class__):
4119 if isinstance(value, Obj):
4120 return value.encode()
4121 if isinstance(value, binary_type):
4123 raise InvalidValueType((self.__class__, Obj, binary_type))
4127 return self._value is not None
4131 if self.expl_lenindef or self.lenindef:
4133 if self.defined is None:
4135 return self.defined[1].bered
4138 obj = self.__class__()
4139 obj._value = self._value
4141 obj._expl = self._expl
4142 obj.optional = self.optional
4143 obj.offset = self.offset
4144 obj.llen = self.llen
4145 obj.vlen = self.vlen
4148 def __eq__(self, their):
4149 if isinstance(their, binary_type):
4150 return self._value == their
4151 if issubclass(their.__class__, Any):
4152 return self._value == their._value
4161 return self.__class__(
4163 expl=self._expl if expl is None else expl,
4164 optional=self.optional if optional is None else optional,
4167 def __bytes__(self):
4168 self._assert_ready()
4176 self._assert_ready()
4179 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4181 t, tlen, lv = tag_strip(tlv)
4182 except DecodeError as err:
4183 raise err.__class__(
4185 klass=self.__class__,
4186 decode_path=decode_path,
4190 l, llen, v = len_decode(lv)
4191 except LenIndefForm as err:
4192 if not ctx.get("bered", False):
4193 raise err.__class__(
4195 klass=self.__class__,
4196 decode_path=decode_path,
4199 llen, vlen, v = 1, 0, lv[1:]
4200 sub_offset = offset + tlen + llen
4202 while v[:EOC_LEN].tobytes() != EOC:
4203 chunk, v = Any().decode(
4206 decode_path=decode_path + (str(chunk_i),),
4210 vlen += chunk.tlvlen
4211 sub_offset += chunk.tlvlen
4213 tlvlen = tlen + llen + vlen + EOC_LEN
4214 obj = self.__class__(
4215 value=tlv[:tlvlen].tobytes(),
4217 optional=self.optional,
4218 _decoded=(offset, 0, tlvlen),
4222 return obj, v[EOC_LEN:]
4223 except DecodeError as err:
4224 raise err.__class__(
4226 klass=self.__class__,
4227 decode_path=decode_path,
4231 raise NotEnoughData(
4232 "encoded length is longer than data",
4233 klass=self.__class__,
4234 decode_path=decode_path,
4237 tlvlen = tlen + llen + l
4238 v, tail = tlv[:tlvlen], v[l:]
4239 obj = self.__class__(
4242 optional=self.optional,
4243 _decoded=(offset, 0, tlvlen),
4249 return pp_console_row(next(self.pps()))
4251 def pps(self, decode_path=()):
4253 asn1_type_name=self.asn1_type_name,
4254 obj_name=self.__class__.__name__,
4255 decode_path=decode_path,
4256 blob=self._value if self.ready else None,
4257 optional=self.optional,
4258 default=self == self.default,
4259 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4260 expl=None if self._expl is None else tag_decode(self._expl),
4265 expl_offset=self.expl_offset if self.expled else None,
4266 expl_tlen=self.expl_tlen if self.expled else None,
4267 expl_llen=self.expl_llen if self.expled else None,
4268 expl_vlen=self.expl_vlen if self.expled else None,
4269 expl_lenindef=self.expl_lenindef,
4270 lenindef=self.lenindef,
4273 defined_by, defined = self.defined or (None, None)
4274 if defined_by is not None:
4276 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4278 for pp in self.pps_lenindef(decode_path):
4282 ########################################################################
4283 # ASN.1 constructed types
4284 ########################################################################
4286 def get_def_by_path(defines_by_path, sub_decode_path):
4287 """Get define by decode path
4289 for path, define in defines_by_path:
4290 if len(path) != len(sub_decode_path):
4292 for p1, p2 in zip(path, sub_decode_path):
4293 if (p1 != any) and (p1 != p2):
4299 def abs_decode_path(decode_path, rel_path):
4300 """Create an absolute decode path from current and relative ones
4302 :param decode_path: current decode path, starting point.
4304 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4305 If first tuple's element is "/", then treat it as
4306 an absolute path, ignoring ``decode_path`` as
4307 starting point. Also this tuple can contain ".."
4308 elements, stripping the leading element from
4311 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4312 ("foo", "bar", "baz", "whatever")
4313 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4315 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4318 if rel_path[0] == "/":
4320 if rel_path[0] == "..":
4321 return abs_decode_path(decode_path[:-1], rel_path[1:])
4322 return decode_path + rel_path
4325 class Sequence(Obj):
4326 """``SEQUENCE`` structure type
4328 You have to make specification of sequence::
4330 class Extension(Sequence):
4332 ("extnID", ObjectIdentifier()),
4333 ("critical", Boolean(default=False)),
4334 ("extnValue", OctetString()),
4337 Then, you can work with it as with dictionary.
4339 >>> ext = Extension()
4340 >>> Extension().specs
4342 ('extnID', OBJECT IDENTIFIER),
4343 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4344 ('extnValue', OCTET STRING),
4346 >>> ext["extnID"] = "1.2.3"
4347 Traceback (most recent call last):
4348 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4349 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4351 You can determine if sequence is ready to be encoded:
4356 Traceback (most recent call last):
4357 pyderasn.ObjNotReady: object is not ready: extnValue
4358 >>> ext["extnValue"] = OctetString(b"foobar")
4362 Value you want to assign, must have the same **type** as in
4363 corresponding specification, but it can have different tags,
4364 optional/default attributes -- they will be taken from specification
4367 class TBSCertificate(Sequence):
4369 ("version", Version(expl=tag_ctxc(0), default="v1")),
4372 >>> tbs = TBSCertificate()
4373 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4375 Assign ``None`` to remove value from sequence.
4377 You can set values in Sequence during its initialization:
4379 >>> AlgorithmIdentifier((
4380 ("algorithm", ObjectIdentifier("1.2.3")),
4381 ("parameters", Any(Null()))
4383 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4385 You can determine if value exists/set in the sequence and take its value:
4387 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4390 OBJECT IDENTIFIER 1.2.3
4392 But pay attention that if value has default, then it won't be (not
4393 in) in the sequence (because ``DEFAULT`` must not be encoded in
4394 DER), but you can read its value:
4396 >>> "critical" in ext, ext["critical"]
4397 (False, BOOLEAN False)
4398 >>> ext["critical"] = Boolean(True)
4399 >>> "critical" in ext, ext["critical"]
4400 (True, BOOLEAN True)
4402 All defaulted values are always optional.
4404 .. _allow_default_values_ctx:
4406 DER prohibits default value encoding and will raise an error if
4407 default value is unexpectedly met during decode.
4408 If :ref:`bered <bered_ctx>` context option is set, then no error
4409 will be raised, but ``bered`` attribute set. You can disable strict
4410 defaulted values existence validation by setting
4411 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4413 Two sequences are equal if they have equal specification (schema),
4414 implicit/explicit tagging and the same values.
4416 __slots__ = ("specs",)
4417 tag_default = tag_encode(form=TagFormConstructed, num=16)
4418 asn1_type_name = "SEQUENCE"
4430 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4432 schema = getattr(self, "schema", ())
4434 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4437 if value is not None:
4438 if issubclass(value.__class__, Sequence):
4439 self._value = value._value
4440 elif hasattr(value, "__iter__"):
4441 for seq_key, seq_value in value:
4442 self[seq_key] = seq_value
4444 raise InvalidValueType((Sequence,))
4445 if default is not None:
4446 if not issubclass(default.__class__, Sequence):
4447 raise InvalidValueType((Sequence,))
4448 default_value = default._value
4449 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4450 default_obj.specs = self.specs
4451 default_obj._value = default_value
4452 self.default = default_obj
4454 self._value = default_obj.copy()._value
4458 for name, spec in self.specs.items():
4459 value = self._value.get(name)
4471 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4473 return any(value.bered for value in self._value.values())
4476 obj = self.__class__(schema=self.specs)
4478 obj._expl = self._expl
4479 obj.default = self.default
4480 obj.optional = self.optional
4481 obj.offset = self.offset
4482 obj.llen = self.llen
4483 obj.vlen = self.vlen
4484 obj._value = {k: v.copy() for k, v in self._value.items()}
4487 def __eq__(self, their):
4488 if not isinstance(their, self.__class__):
4491 self.specs == their.specs and
4492 self.tag == their.tag and
4493 self._expl == their._expl and
4494 self._value == their._value
4505 return self.__class__(
4508 impl=self.tag if impl is None else impl,
4509 expl=self._expl if expl is None else expl,
4510 default=self.default if default is None else default,
4511 optional=self.optional if optional is None else optional,
4514 def __contains__(self, key):
4515 return key in self._value
4517 def __setitem__(self, key, value):
4518 spec = self.specs.get(key)
4520 raise ObjUnknown(key)
4522 self._value.pop(key, None)
4524 if not isinstance(value, spec.__class__):
4525 raise InvalidValueType((spec.__class__,))
4526 value = spec(value=value)
4527 if spec.default is not None and value == spec.default:
4528 self._value.pop(key, None)
4530 self._value[key] = value
4532 def __getitem__(self, key):
4533 value = self._value.get(key)
4534 if value is not None:
4536 spec = self.specs.get(key)
4538 raise ObjUnknown(key)
4539 if spec.default is not None:
4543 def _encoded_values(self):
4545 for name, spec in self.specs.items():
4546 value = self._value.get(name)
4550 raise ObjNotReady(name)
4551 raws.append(value.encode())
4555 v = b"".join(self._encoded_values())
4556 return b"".join((self.tag, len_encode(len(v)), v))
4558 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4560 t, tlen, lv = tag_strip(tlv)
4561 except DecodeError as err:
4562 raise err.__class__(
4564 klass=self.__class__,
4565 decode_path=decode_path,
4570 klass=self.__class__,
4571 decode_path=decode_path,
4574 if tag_only: # pragma: no cover
4577 ctx_bered = ctx.get("bered", False)
4579 l, llen, v = len_decode(lv)
4580 except LenIndefForm as err:
4582 raise err.__class__(
4584 klass=self.__class__,
4585 decode_path=decode_path,
4588 l, llen, v = 0, 1, lv[1:]
4590 except DecodeError as err:
4591 raise err.__class__(
4593 klass=self.__class__,
4594 decode_path=decode_path,
4598 raise NotEnoughData(
4599 "encoded length is longer than data",
4600 klass=self.__class__,
4601 decode_path=decode_path,
4605 v, tail = v[:l], v[l:]
4607 sub_offset = offset + tlen + llen
4610 ctx_allow_default_values = ctx.get("allow_default_values", False)
4611 for name, spec in self.specs.items():
4612 if spec.optional and (
4613 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4617 sub_decode_path = decode_path + (name,)
4619 value, v_tail = spec.decode(
4623 decode_path=sub_decode_path,
4631 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4632 if defined is not None:
4633 defined_by, defined_spec = defined
4634 if issubclass(value.__class__, SequenceOf):
4635 for i, _value in enumerate(value):
4636 sub_sub_decode_path = sub_decode_path + (
4638 DecodePathDefBy(defined_by),
4640 defined_value, defined_tail = defined_spec.decode(
4641 memoryview(bytes(_value)),
4643 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4644 if value.expled else (value.tlen + value.llen)
4647 decode_path=sub_sub_decode_path,
4650 if len(defined_tail) > 0:
4653 klass=self.__class__,
4654 decode_path=sub_sub_decode_path,
4657 _value.defined = (defined_by, defined_value)
4659 defined_value, defined_tail = defined_spec.decode(
4660 memoryview(bytes(value)),
4662 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4663 if value.expled else (value.tlen + value.llen)
4666 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4669 if len(defined_tail) > 0:
4672 klass=self.__class__,
4673 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4676 value.defined = (defined_by, defined_value)
4678 value_len = value.fulllen
4680 sub_offset += value_len
4682 if spec.default is not None and value == spec.default:
4683 if ctx_bered or ctx_allow_default_values:
4687 "DEFAULT value met",
4688 klass=self.__class__,
4689 decode_path=sub_decode_path,
4692 values[name] = value
4694 spec_defines = getattr(spec, "defines", ())
4695 if len(spec_defines) == 0:
4696 defines_by_path = ctx.get("defines_by_path", ())
4697 if len(defines_by_path) > 0:
4698 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4699 if spec_defines is not None and len(spec_defines) > 0:
4700 for rel_path, schema in spec_defines:
4701 defined = schema.get(value, None)
4702 if defined is not None:
4703 ctx.setdefault("_defines", []).append((
4704 abs_decode_path(sub_decode_path[:-1], rel_path),
4708 if v[:EOC_LEN].tobytes() != EOC:
4711 klass=self.__class__,
4712 decode_path=decode_path,
4720 klass=self.__class__,
4721 decode_path=decode_path,
4724 obj = self.__class__(
4728 default=self.default,
4729 optional=self.optional,
4730 _decoded=(offset, llen, vlen),
4733 obj.lenindef = lenindef
4734 obj.ber_encoded = ber_encoded
4738 value = pp_console_row(next(self.pps()))
4740 for name in self.specs:
4741 _value = self._value.get(name)
4744 cols.append("%s: %s" % (name, repr(_value)))
4745 return "%s[%s]" % (value, "; ".join(cols))
4747 def pps(self, decode_path=()):
4749 asn1_type_name=self.asn1_type_name,
4750 obj_name=self.__class__.__name__,
4751 decode_path=decode_path,
4752 optional=self.optional,
4753 default=self == self.default,
4754 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4755 expl=None if self._expl is None else tag_decode(self._expl),
4760 expl_offset=self.expl_offset if self.expled else None,
4761 expl_tlen=self.expl_tlen if self.expled else None,
4762 expl_llen=self.expl_llen if self.expled else None,
4763 expl_vlen=self.expl_vlen if self.expled else None,
4764 expl_lenindef=self.expl_lenindef,
4765 lenindef=self.lenindef,
4766 ber_encoded=self.ber_encoded,
4769 for name in self.specs:
4770 value = self._value.get(name)
4773 yield value.pps(decode_path=decode_path + (name,))
4774 for pp in self.pps_lenindef(decode_path):
4778 class Set(Sequence):
4779 """``SET`` structure type
4781 Its usage is identical to :py:class:`pyderasn.Sequence`.
4783 .. _allow_unordered_set_ctx:
4785 DER prohibits unordered values encoding and will raise an error
4786 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4787 then no error will occure. Also you can disable strict values
4788 ordering check by setting ``"allow_unordered_set": True``
4789 :ref:`context <ctx>` option.
4792 tag_default = tag_encode(form=TagFormConstructed, num=17)
4793 asn1_type_name = "SET"
4796 raws = self._encoded_values()
4799 return b"".join((self.tag, len_encode(len(v)), v))
4801 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4803 t, tlen, lv = tag_strip(tlv)
4804 except DecodeError as err:
4805 raise err.__class__(
4807 klass=self.__class__,
4808 decode_path=decode_path,
4813 klass=self.__class__,
4814 decode_path=decode_path,
4820 ctx_bered = ctx.get("bered", False)
4822 l, llen, v = len_decode(lv)
4823 except LenIndefForm as err:
4825 raise err.__class__(
4827 klass=self.__class__,
4828 decode_path=decode_path,
4831 l, llen, v = 0, 1, lv[1:]
4833 except DecodeError as err:
4834 raise err.__class__(
4836 klass=self.__class__,
4837 decode_path=decode_path,
4841 raise NotEnoughData(
4842 "encoded length is longer than data",
4843 klass=self.__class__,
4847 v, tail = v[:l], v[l:]
4849 sub_offset = offset + tlen + llen
4852 ctx_allow_default_values = ctx.get("allow_default_values", False)
4853 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4854 value_prev = memoryview(v[:0])
4855 specs_items = self.specs.items
4857 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4859 for name, spec in specs_items():
4860 sub_decode_path = decode_path + (name,)
4866 decode_path=sub_decode_path,
4875 klass=self.__class__,
4876 decode_path=decode_path,
4879 value, v_tail = spec.decode(
4883 decode_path=sub_decode_path,
4886 value_len = value.fulllen
4887 if value_prev.tobytes() > v[:value_len].tobytes():
4888 if ctx_bered or ctx_allow_unordered_set:
4892 "unordered " + self.asn1_type_name,
4893 klass=self.__class__,
4894 decode_path=sub_decode_path,
4897 if spec.default is None or value != spec.default:
4899 elif ctx_bered or ctx_allow_default_values:
4903 "DEFAULT value met",
4904 klass=self.__class__,
4905 decode_path=sub_decode_path,
4908 values[name] = value
4909 value_prev = v[:value_len]
4910 sub_offset += value_len
4913 obj = self.__class__(
4917 default=self.default,
4918 optional=self.optional,
4919 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4922 if v[:EOC_LEN].tobytes() != EOC:
4925 klass=self.__class__,
4926 decode_path=decode_path,
4934 "not all values are ready",
4935 klass=self.__class__,
4936 decode_path=decode_path,
4939 obj.ber_encoded = ber_encoded
4943 class SequenceOf(Obj):
4944 """``SEQUENCE OF`` sequence type
4946 For that kind of type you must specify the object it will carry on
4947 (bounds are for example here, not required)::
4949 class Ints(SequenceOf):
4954 >>> ints.append(Integer(123))
4955 >>> ints.append(Integer(234))
4957 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4958 >>> [int(i) for i in ints]
4960 >>> ints.append(Integer(345))
4961 Traceback (most recent call last):
4962 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4965 >>> ints[1] = Integer(345)
4967 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4969 Also you can initialize sequence with preinitialized values:
4971 >>> ints = Ints([Integer(123), Integer(234)])
4973 __slots__ = ("spec", "_bound_min", "_bound_max")
4974 tag_default = tag_encode(form=TagFormConstructed, num=16)
4975 asn1_type_name = "SEQUENCE OF"
4988 super(SequenceOf, self).__init__(
4996 schema = getattr(self, "schema", None)
4998 raise ValueError("schema must be specified")
5000 self._bound_min, self._bound_max = getattr(
5004 ) if bounds is None else bounds
5006 if value is not None:
5007 self._value = self._value_sanitize(value)
5008 if default is not None:
5009 default_value = self._value_sanitize(default)
5010 default_obj = self.__class__(
5015 default_obj._value = default_value
5016 self.default = default_obj
5018 self._value = default_obj.copy()._value
5020 def _value_sanitize(self, value):
5021 if issubclass(value.__class__, SequenceOf):
5022 value = value._value
5023 elif hasattr(value, "__iter__"):
5026 raise InvalidValueType((self.__class__, iter))
5027 if not self._bound_min <= len(value) <= self._bound_max:
5028 raise BoundsError(self._bound_min, len(value), self._bound_max)
5030 if not isinstance(v, self.spec.__class__):
5031 raise InvalidValueType((self.spec.__class__,))
5036 return all(v.ready for v in self._value)
5040 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5042 return any(v.bered for v in self._value)
5045 obj = self.__class__(schema=self.spec)
5046 obj._bound_min = self._bound_min
5047 obj._bound_max = self._bound_max
5049 obj._expl = self._expl
5050 obj.default = self.default
5051 obj.optional = self.optional
5052 obj.offset = self.offset
5053 obj.llen = self.llen
5054 obj.vlen = self.vlen
5055 obj._value = [v.copy() for v in self._value]
5058 def __eq__(self, their):
5059 if isinstance(their, self.__class__):
5061 self.spec == their.spec and
5062 self.tag == their.tag and
5063 self._expl == their._expl and
5064 self._value == their._value
5066 if hasattr(their, "__iter__"):
5067 return self._value == list(their)
5079 return self.__class__(
5083 (self._bound_min, self._bound_max)
5084 if bounds is None else bounds
5086 impl=self.tag if impl is None else impl,
5087 expl=self._expl if expl is None else expl,
5088 default=self.default if default is None else default,
5089 optional=self.optional if optional is None else optional,
5092 def __contains__(self, key):
5093 return key in self._value
5095 def append(self, value):
5096 if not isinstance(value, self.spec.__class__):
5097 raise InvalidValueType((self.spec.__class__,))
5098 if len(self._value) + 1 > self._bound_max:
5101 len(self._value) + 1,
5104 self._value.append(value)
5107 self._assert_ready()
5108 return iter(self._value)
5111 self._assert_ready()
5112 return len(self._value)
5114 def __setitem__(self, key, value):
5115 if not isinstance(value, self.spec.__class__):
5116 raise InvalidValueType((self.spec.__class__,))
5117 self._value[key] = self.spec(value=value)
5119 def __getitem__(self, key):
5120 return self._value[key]
5122 def _encoded_values(self):
5123 return [v.encode() for v in self._value]
5126 v = b"".join(self._encoded_values())
5127 return b"".join((self.tag, len_encode(len(v)), v))
5129 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5131 t, tlen, lv = tag_strip(tlv)
5132 except DecodeError as err:
5133 raise err.__class__(
5135 klass=self.__class__,
5136 decode_path=decode_path,
5141 klass=self.__class__,
5142 decode_path=decode_path,
5148 ctx_bered = ctx.get("bered", False)
5150 l, llen, v = len_decode(lv)
5151 except LenIndefForm as err:
5153 raise err.__class__(
5155 klass=self.__class__,
5156 decode_path=decode_path,
5159 l, llen, v = 0, 1, lv[1:]
5161 except DecodeError as err:
5162 raise err.__class__(
5164 klass=self.__class__,
5165 decode_path=decode_path,
5169 raise NotEnoughData(
5170 "encoded length is longer than data",
5171 klass=self.__class__,
5172 decode_path=decode_path,
5176 v, tail = v[:l], v[l:]
5178 sub_offset = offset + tlen + llen
5180 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5181 value_prev = memoryview(v[:0])
5185 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5187 sub_decode_path = decode_path + (str(len(_value)),)
5188 value, v_tail = spec.decode(
5192 decode_path=sub_decode_path,
5195 value_len = value.fulllen
5197 if value_prev.tobytes() > v[:value_len].tobytes():
5198 if ctx_bered or ctx_allow_unordered_set:
5202 "unordered " + self.asn1_type_name,
5203 klass=self.__class__,
5204 decode_path=sub_decode_path,
5207 value_prev = v[:value_len]
5208 _value.append(value)
5209 sub_offset += value_len
5213 obj = self.__class__(
5216 bounds=(self._bound_min, self._bound_max),
5219 default=self.default,
5220 optional=self.optional,
5221 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5223 except BoundsError as err:
5226 klass=self.__class__,
5227 decode_path=decode_path,
5231 if v[:EOC_LEN].tobytes() != EOC:
5234 klass=self.__class__,
5235 decode_path=decode_path,
5240 obj.ber_encoded = ber_encoded
5245 pp_console_row(next(self.pps())),
5246 ", ".join(repr(v) for v in self._value),
5249 def pps(self, decode_path=()):
5251 asn1_type_name=self.asn1_type_name,
5252 obj_name=self.__class__.__name__,
5253 decode_path=decode_path,
5254 optional=self.optional,
5255 default=self == self.default,
5256 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5257 expl=None if self._expl is None else tag_decode(self._expl),
5262 expl_offset=self.expl_offset if self.expled else None,
5263 expl_tlen=self.expl_tlen if self.expled else None,
5264 expl_llen=self.expl_llen if self.expled else None,
5265 expl_vlen=self.expl_vlen if self.expled else None,
5266 expl_lenindef=self.expl_lenindef,
5267 lenindef=self.lenindef,
5268 ber_encoded=self.ber_encoded,
5271 for i, value in enumerate(self._value):
5272 yield value.pps(decode_path=decode_path + (str(i),))
5273 for pp in self.pps_lenindef(decode_path):
5277 class SetOf(SequenceOf):
5278 """``SET OF`` sequence type
5280 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5283 tag_default = tag_encode(form=TagFormConstructed, num=17)
5284 asn1_type_name = "SET OF"
5287 raws = self._encoded_values()
5290 return b"".join((self.tag, len_encode(len(v)), v))
5292 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5293 return super(SetOf, self)._decode(
5299 ordering_check=True,
5303 def obj_by_path(pypath): # pragma: no cover
5304 """Import object specified as string Python path
5306 Modules must be separated from classes/functions with ``:``.
5308 >>> obj_by_path("foo.bar:Baz")
5309 <class 'foo.bar.Baz'>
5310 >>> obj_by_path("foo.bar:Baz.boo")
5311 <classmethod 'foo.bar.Baz.boo'>
5313 mod, objs = pypath.rsplit(":", 1)
5314 from importlib import import_module
5315 obj = import_module(mod)
5316 for obj_name in objs.split("."):
5317 obj = getattr(obj, obj_name)
5321 def generic_decoder(): # pragma: no cover
5322 # All of this below is a big hack with self references
5323 choice = PrimitiveTypes()
5324 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5325 choice.specs["SetOf"] = SetOf(schema=choice)
5327 choice.specs["SequenceOf%d" % i] = SequenceOf(
5331 choice.specs["Any"] = Any()
5333 # Class name equals to type name, to omit it from output
5334 class SEQUENCEOF(SequenceOf):
5342 with_decode_path=False,
5343 decode_path_only=(),
5345 def _pprint_pps(pps):
5347 if hasattr(pp, "_fields"):
5349 decode_path_only != () and
5350 pp.decode_path[:len(decode_path_only)] != decode_path_only
5353 if pp.asn1_type_name == Choice.asn1_type_name:
5355 pp_kwargs = pp._asdict()
5356 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5357 pp = _pp(**pp_kwargs)
5358 yield pp_console_row(
5363 with_colours=with_colours,
5364 with_decode_path=with_decode_path,
5365 decode_path_len_decrease=len(decode_path_only),
5367 for row in pp_console_blob(
5369 decode_path_len_decrease=len(decode_path_only),
5373 for row in _pprint_pps(pp):
5375 return "\n".join(_pprint_pps(obj.pps()))
5376 return SEQUENCEOF(), pprint_any
5379 def main(): # pragma: no cover
5381 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5382 parser.add_argument(
5386 help="Skip that number of bytes from the beginning",
5388 parser.add_argument(
5390 help="Python path to dictionary with OIDs",
5392 parser.add_argument(
5394 help="Python path to schema definition to use",
5396 parser.add_argument(
5397 "--defines-by-path",
5398 help="Python path to decoder's defines_by_path",
5400 parser.add_argument(
5402 action="store_true",
5403 help="Disallow BER encoding",
5405 parser.add_argument(
5406 "--print-decode-path",
5407 action="store_true",
5408 help="Print decode paths",
5410 parser.add_argument(
5411 "--decode-path-only",
5412 help="Print only specified decode path",
5414 parser.add_argument(
5416 action="store_true",
5417 help="Allow explicit tag out-of-bound",
5419 parser.add_argument(
5421 type=argparse.FileType("rb"),
5422 help="Path to DER file you want to decode",
5424 args = parser.parse_args()
5425 args.DERFile.seek(args.skip)
5426 der = memoryview(args.DERFile.read())
5427 args.DERFile.close()
5428 oids = obj_by_path(args.oids) if args.oids else {}
5430 schema = obj_by_path(args.schema)
5431 from functools import partial
5432 pprinter = partial(pprint, big_blobs=True)
5434 schema, pprinter = generic_decoder()
5436 "bered": not args.nobered,
5437 "allow_expl_oob": args.allow_expl_oob,
5439 if args.defines_by_path is not None:
5440 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5441 obj, tail = schema().decode(der, ctx=ctx)
5445 with_colours=True if environ.get("NO_COLOR") is None else False,
5446 with_decode_path=args.print_decode_path,
5448 () if args.decode_path_only is None else
5449 tuple(args.decode_path_only.split(":"))
5453 print("\nTrailing data: %s" % hexenc(tail))
5456 if __name__ == "__main__":