3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2019 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, version 3 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this program. If not, see <http://www.gnu.org/licenses/>.
17 """Python ASN.1 DER/BER codec with abstract structures
19 This library allows you to marshal various structures in ASN.1 DER
20 format, unmarshal them in BER/CER/DER ones.
24 >>> Integer().decode(raw) == i
27 There are primitive types, holding single values
28 (:py:class:`pyderasn.BitString`,
29 :py:class:`pyderasn.Boolean`,
30 :py:class:`pyderasn.Enumerated`,
31 :py:class:`pyderasn.GeneralizedTime`,
32 :py:class:`pyderasn.Integer`,
33 :py:class:`pyderasn.Null`,
34 :py:class:`pyderasn.ObjectIdentifier`,
35 :py:class:`pyderasn.OctetString`,
36 :py:class:`pyderasn.UTCTime`,
37 :py:class:`various strings <pyderasn.CommonString>`
38 (:py:class:`pyderasn.BMPString`,
39 :py:class:`pyderasn.GeneralString`,
40 :py:class:`pyderasn.GraphicString`,
41 :py:class:`pyderasn.IA5String`,
42 :py:class:`pyderasn.ISO646String`,
43 :py:class:`pyderasn.NumericString`,
44 :py:class:`pyderasn.PrintableString`,
45 :py:class:`pyderasn.T61String`,
46 :py:class:`pyderasn.TeletexString`,
47 :py:class:`pyderasn.UniversalString`,
48 :py:class:`pyderasn.UTF8String`,
49 :py:class:`pyderasn.VideotexString`,
50 :py:class:`pyderasn.VisibleString`)),
51 constructed types, holding multiple primitive types
52 (:py:class:`pyderasn.Sequence`,
53 :py:class:`pyderasn.SequenceOf`,
54 :py:class:`pyderasn.Set`,
55 :py:class:`pyderasn.SetOf`),
56 and special types like
57 :py:class:`pyderasn.Any` and
58 :py:class:`pyderasn.Choice`.
66 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
67 the default tag used during coding process. You can override it with
68 either ``IMPLICIT`` (using ``impl`` keyword argument), or
69 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
70 raw binary string, containing that tag. You can **not** set implicit and
71 explicit tags simultaneously.
73 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
74 functions, allowing you to easily create ``CONTEXT``
75 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
76 number. Pay attention that explicit tags always have *constructed* tag
77 (``tag_ctxc``), but implicit tags for primitive types are primitive
82 >>> Integer(impl=tag_ctxp(1))
84 >>> Integer(expl=tag_ctxc(2))
87 Implicit tag is not explicitly shown.
89 Two objects of the same type, but with different implicit/explicit tags
92 You can get object's effective tag (either default or implicited) through
93 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
96 >>> tag_decode(tag_ctxc(123))
98 >>> klass, form, num = tag_decode(tag_ctxc(123))
99 >>> klass == TagClassContext
101 >>> form == TagFormConstructed
104 To determine if object has explicit tag, use ``expled`` boolean property
105 and ``expl_tag`` property, returning explicit tag's value.
110 Many objects in sequences could be ``OPTIONAL`` and could have
111 ``DEFAULT`` value. You can specify that object's property using
112 corresponding keyword arguments.
114 >>> Integer(optional=True, default=123)
115 INTEGER 123 OPTIONAL DEFAULT
117 Those specifications do not play any role in primitive value encoding,
118 but are taken into account when dealing with sequences holding them. For
119 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
122 class Version(Integer):
128 class TBSCertificate(Sequence):
130 ("version", Version(expl=tag_ctxc(0), default="v1")),
133 When default argument is used and value is not specified, then it equals
141 Some objects give ability to set value size constraints. This is either
142 possible integer value, or allowed length of various strings and
143 sequences. Constraints are set in the following way::
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
150 For simplicity you can also set bounds the following way::
152 bounded_x = X(bounds=(MIN, MAX))
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
160 All objects have ``ready`` boolean property, that tells if object is
161 ready to be encoded. If that kind of action is performed on unready
162 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
164 All objects have ``copy()`` method, that returns their copy, that can be
172 Decoding is performed using ``decode()`` method. ``offset`` optional
173 argument could be used to set initial object's offset in the binary
174 data, for convenience. It returns decoded object and remaining
175 unmarshalled data (tail). Internally all work is done on
176 ``memoryview(data)``, and you can leave returning tail as a memoryview,
177 by specifying ``leavemm=True`` argument.
179 When object is decoded, ``decoded`` property is true and you can safely
180 use following properties:
182 * ``offset`` -- position including initial offset where object's tag starts
183 * ``tlen`` -- length of object's tag
184 * ``llen`` -- length of object's length value
185 * ``vlen`` -- length of object's value
186 * ``tlvlen`` -- length of the whole object
188 Pay attention that those values do **not** include anything related to
189 explicit tag. If you want to know information about it, then use:
191 * ``expled`` -- to know if explicit tag is set
192 * ``expl_offset`` (it is lesser than ``offset``)
195 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
196 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
198 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
201 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
208 You can specify so called context keyword argument during ``decode()``
209 invocation. It is dictionary containing various options governing
212 Currently available context options:
214 * :ref:`allow_default_values <allow_default_values_ctx>`
215 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
216 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
217 * :ref:`bered <bered_ctx>`
218 * :ref:`defines_by_path <defines_by_path_ctx>`
225 All objects have ``pps()`` method, that is a generator of
226 :py:class:`pyderasn.PP` namedtuple, holding various raw information
227 about the object. If ``pps`` is called on sequences, then all underlying
228 ``PP`` will be yielded.
230 You can use :py:func:`pyderasn.pp_console_row` function, converting
231 those ``PP`` to human readable string. Actually exactly it is used for
232 all object ``repr``. But it is easy to write custom formatters.
234 >>> from pyderasn import pprint
235 >>> encoded = Integer(-12345).encode()
236 >>> obj, tail = Integer().decode(encoded)
237 >>> print(pprint(obj))
238 0 [1,1, 2] INTEGER -12345
242 Example certificate::
244 >>> print(pprint(crt))
245 0 [1,3,1604] Certificate SEQUENCE
246 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
247 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
248 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
249 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
250 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
251 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
253 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
254 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
255 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
256 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
257 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
258 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
259 . . . . . . . 13:02:45:53
261 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
262 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
263 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
265 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
266 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
267 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
272 Let's parse that output, human::
274 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
275 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
276 0 1 2 3 4 5 6 7 8 9 10 11
280 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
286 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
292 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
297 Offset of the object, where its DER/BER encoding begins.
298 Pay attention that it does **not** include explicit tag.
300 If explicit tag exists, then this is its length (tag + encoded length).
302 Length of object's tag. For example CHOICE does not have its own tag,
305 Length of encoded length.
307 Length of encoded value.
309 Visual indentation to show the depth of object in the hierarchy.
311 Object's name inside SEQUENCE/CHOICE.
313 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
314 here. "IMPLICIT" is omitted.
316 Object's class name, if set. Omitted if it is just an ordinary simple
317 value (like with ``algorithm`` in example above).
321 Object's value, if set. Can consist of multiple words (like OCTET/BIT
322 STRINGs above). We see ``v3`` value in Version, because it is named.
323 ``rdnSequence`` is the choice of CHOICE type.
325 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
326 default one, specified in the schema.
328 Shows does object contains any kind of BER encoded data (possibly
329 Sequence holding BER-encoded underlying value).
331 Only applicable to BER encoded data. Indefinite length encoding mark.
333 Only applicable to BER encoded data. If object has BER-specific
334 encoding, then ``BER`` will be shown. It does not depend on indefinite
335 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
336 (and its derivatives), ``SET``, ``SET OF`` could be BERed.
344 ASN.1 structures often have ANY and OCTET STRING fields, that are
345 DEFINED BY some previously met ObjectIdentifier. This library provides
346 ability to specify mapping between some OID and field that must be
347 decoded with specific specification.
354 :py:class:`pyderasn.ObjectIdentifier` field inside
355 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
356 necessary for decoding structures. For example, CMS (:rfc:`5652`)
359 class ContentInfo(Sequence):
361 ("contentType", ContentType(defines=((("content",), {
362 id_digestedData: DigestedData(),
363 id_signedData: SignedData(),
365 ("content", Any(expl=tag_ctxc(0))),
368 ``contentType`` field tells that it defines that ``content`` must be
369 decoded with ``SignedData`` specification, if ``contentType`` equals to
370 ``id-signedData``. The same applies to ``DigestedData``. If
371 ``contentType`` contains unknown OID, then no automatic decoding is
374 You can specify multiple fields, that will be autodecoded -- that is why
375 ``defines`` kwarg is a sequence. You can specify defined field
376 relatively or absolutely to current decode path. For example ``defines``
377 for AlgorithmIdentifier of X.509's
378 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
382 id_ecPublicKey: ECParameters(),
383 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
385 (("..", "subjectPublicKey"), {
386 id_rsaEncryption: RSAPublicKey(),
387 id_GostR3410_2001: OctetString(),
391 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
392 autodecode its parameters inside SPKI's algorithm and its public key
395 Following types can be automatically decoded (DEFINED BY):
397 * :py:class:`pyderasn.Any`
398 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
399 * :py:class:`pyderasn.OctetString`
400 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
401 ``Any``/``BitString``/``OctetString``-s
403 When any of those fields is automatically decoded, then ``.defined``
404 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
405 was defined, ``value`` contains corresponding decoded value. For example
406 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
408 .. _defines_by_path_ctx:
410 defines_by_path context option
411 ______________________________
413 Sometimes you either can not or do not want to explicitly set *defines*
414 in the scheme. You can dynamically apply those definitions when calling
415 ``.decode()`` method.
417 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
418 value must be sequence of following tuples::
420 (decode_path, defines)
422 where ``decode_path`` is a tuple holding so-called decode path to the
423 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
424 ``defines``, holding exactly the same value as accepted in its
425 :ref:`keyword argument <defines>`.
427 For example, again for CMS, you want to automatically decode
428 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
429 structures it may hold. Also, automatically decode ``controlSequence``
432 content_info, tail = ContentInfo().decode(data, ctx={"defines_by_path": (
435 ((("content",), {id_signedData: SignedData()}),),
440 DecodePathDefBy(id_signedData),
445 id_cct_PKIData: PKIData(),
446 id_cct_PKIResponse: PKIResponse(),
452 DecodePathDefBy(id_signedData),
455 DecodePathDefBy(id_cct_PKIResponse),
461 id_cmc_recipientNonce: RecipientNonce(),
462 id_cmc_senderNonce: SenderNonce(),
463 id_cmc_statusInfoV2: CMCStatusInfoV2(),
464 id_cmc_transactionId: TransactionId(),
469 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
470 First function is useful for path construction when some automatic
471 decoding is already done. ``any`` means literally any value it meet --
472 useful for SEQUENCE/SET OF-s.
479 By default PyDERASN accepts only DER encoded data. It always encodes to
480 DER. But you can optionally enable BER decoding with setting ``bered``
481 :ref:`context <ctx>` argument to True. Indefinite lengths and
482 constructed primitive types should be parsed successfully.
484 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
485 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
486 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``
488 * If object has an indefinite length encoding, then its ``lenindef``
489 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
490 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
492 * If object has an indefinite length encoded explicit tag, then
493 ``expl_lenindef`` is set to True.
494 * If object has either any of BER-related encoding (explicit tag
495 indefinite length, object's indefinite length, BER-encoding) or any
496 underlying component has that kind of encoding, then ``bered``
497 attribute is set to True. For example SignedData CMS can have
498 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
499 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
501 EOC (end-of-contents) token's length is taken in advance in object's
504 .. _allow_expl_oob_ctx:
506 Allow explicit tag out-of-bound
507 -------------------------------
509 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
510 one value, more than one object. If you set ``allow_expl_oob`` context
511 option to True, then no error will be raised and that invalid encoding
512 will be silently further processed. But pay attention that offsets and
513 lengths will be invalid in that case.
517 This option should be used only for skipping some decode errors, just
518 to see the decoded structure somehow.
525 .. autoclass:: pyderasn.Boolean
530 .. autoclass:: pyderasn.Integer
535 .. autoclass:: pyderasn.BitString
540 .. autoclass:: pyderasn.OctetString
545 .. autoclass:: pyderasn.Null
550 .. autoclass:: pyderasn.ObjectIdentifier
555 .. autoclass:: pyderasn.Enumerated
559 .. autoclass:: pyderasn.CommonString
563 .. autoclass:: pyderasn.NumericString
567 .. autoclass:: pyderasn.PrintableString
571 .. autoclass:: pyderasn.UTCTime
572 :members: __init__, todatetime
576 .. autoclass:: pyderasn.GeneralizedTime
583 .. autoclass:: pyderasn.Choice
588 .. autoclass:: PrimitiveTypes
592 .. autoclass:: pyderasn.Any
600 .. autoclass:: pyderasn.Sequence
605 .. autoclass:: pyderasn.Set
610 .. autoclass:: pyderasn.SequenceOf
615 .. autoclass:: pyderasn.SetOf
621 .. autofunction:: pyderasn.abs_decode_path
622 .. autofunction:: pyderasn.colonize_hex
623 .. autofunction:: pyderasn.hexenc
624 .. autofunction:: pyderasn.hexdec
625 .. autofunction:: pyderasn.tag_encode
626 .. autofunction:: pyderasn.tag_decode
627 .. autofunction:: pyderasn.tag_ctxp
628 .. autofunction:: pyderasn.tag_ctxc
629 .. autoclass:: pyderasn.Obj
630 .. autoclass:: pyderasn.DecodeError
632 .. autoclass:: pyderasn.NotEnoughData
633 .. autoclass:: pyderasn.LenIndefForm
634 .. autoclass:: pyderasn.TagMismatch
635 .. autoclass:: pyderasn.InvalidLength
636 .. autoclass:: pyderasn.InvalidOID
637 .. autoclass:: pyderasn.ObjUnknown
638 .. autoclass:: pyderasn.ObjNotReady
639 .. autoclass:: pyderasn.InvalidValueType
640 .. autoclass:: pyderasn.BoundsError
643 from codecs import getdecoder
644 from codecs import getencoder
645 from collections import namedtuple
646 from collections import OrderedDict
647 from copy import copy
648 from datetime import datetime
649 from math import ceil
650 from os import environ
651 from string import ascii_letters
652 from string import digits
654 from six import add_metaclass
655 from six import binary_type
656 from six import byte2int
657 from six import indexbytes
658 from six import int2byte
659 from six import integer_types
660 from six import iterbytes
661 from six import iteritems
662 from six import itervalues
664 from six import string_types
665 from six import text_type
666 from six import unichr as six_unichr
667 from six.moves import xrange as six_xrange
671 from termcolor import colored
672 except ImportError: # pragma: no cover
673 def colored(what, *args, **kwargs):
717 "TagClassApplication",
721 "TagFormConstructed",
732 TagClassUniversal = 0
733 TagClassApplication = 1 << 6
734 TagClassContext = 1 << 7
735 TagClassPrivate = 1 << 6 | 1 << 7
737 TagFormConstructed = 1 << 5
740 TagClassApplication: "APPLICATION ",
741 TagClassPrivate: "PRIVATE ",
742 TagClassUniversal: "UNIV ",
746 LENINDEF = b"\x80" # length indefinite mark
747 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
750 ########################################################################
752 ########################################################################
754 class ASN1Error(ValueError):
758 class DecodeError(ASN1Error):
759 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
761 :param str msg: reason of decode failing
762 :param klass: optional exact DecodeError inherited class (like
763 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
764 :py:exc:`InvalidLength`)
765 :param decode_path: tuple of strings. It contains human
766 readable names of the fields through which
767 decoding process has passed
768 :param int offset: binary offset where failure happened
770 super(DecodeError, self).__init__()
773 self.decode_path = decode_path
779 "" if self.klass is None else self.klass.__name__,
781 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
782 if len(self.decode_path) > 0 else ""
784 ("(at %d)" % self.offset) if self.offset > 0 else "",
790 return "%s(%s)" % (self.__class__.__name__, self)
793 class NotEnoughData(DecodeError):
797 class LenIndefForm(DecodeError):
801 class TagMismatch(DecodeError):
805 class InvalidLength(DecodeError):
809 class InvalidOID(DecodeError):
813 class ObjUnknown(ASN1Error):
814 def __init__(self, name):
815 super(ObjUnknown, self).__init__()
819 return "object is unknown: %s" % self.name
822 return "%s(%s)" % (self.__class__.__name__, self)
825 class ObjNotReady(ASN1Error):
826 def __init__(self, name):
827 super(ObjNotReady, self).__init__()
831 return "object is not ready: %s" % self.name
834 return "%s(%s)" % (self.__class__.__name__, self)
837 class InvalidValueType(ASN1Error):
838 def __init__(self, expected_types):
839 super(InvalidValueType, self).__init__()
840 self.expected_types = expected_types
843 return "invalid value type, expected: %s" % ", ".join(
844 [repr(t) for t in self.expected_types]
848 return "%s(%s)" % (self.__class__.__name__, self)
851 class BoundsError(ASN1Error):
852 def __init__(self, bound_min, value, bound_max):
853 super(BoundsError, self).__init__()
854 self.bound_min = bound_min
856 self.bound_max = bound_max
859 return "unsatisfied bounds: %s <= %s <= %s" % (
866 return "%s(%s)" % (self.__class__.__name__, self)
869 ########################################################################
871 ########################################################################
873 _hexdecoder = getdecoder("hex")
874 _hexencoder = getencoder("hex")
878 """Binary data to hexadecimal string convert
880 return _hexdecoder(data)[0]
884 """Hexadecimal string to binary data convert
886 return _hexencoder(data)[0].decode("ascii")
889 def int_bytes_len(num, byte_len=8):
892 return int(ceil(float(num.bit_length()) / byte_len))
895 def zero_ended_encode(num):
896 octets = bytearray(int_bytes_len(num, 7))
898 octets[i] = num & 0x7F
902 octets[i] = 0x80 | (num & 0x7F)
908 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
909 """Encode tag to binary form
911 :param int num: tag's number
912 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
913 :py:data:`pyderasn.TagClassContext`,
914 :py:data:`pyderasn.TagClassApplication`,
915 :py:data:`pyderasn.TagClassPrivate`)
916 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
917 :py:data:`pyderasn.TagFormConstructed`)
921 return int2byte(klass | form | num)
922 # [XX|X|11111][1.......][1.......] ... [0.......]
923 return int2byte(klass | form | 31) + zero_ended_encode(num)
927 """Decode tag from binary form
931 No validation is performed, assuming that it has already passed.
933 It returns tuple with three integers, as
934 :py:func:`pyderasn.tag_encode` accepts.
936 first_octet = byte2int(tag)
937 klass = first_octet & 0xC0
938 form = first_octet & 0x20
939 if first_octet & 0x1F < 0x1F:
940 return (klass, form, first_octet & 0x1F)
942 for octet in iterbytes(tag[1:]):
945 return (klass, form, num)
949 """Create CONTEXT PRIMITIVE tag
951 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
955 """Create CONTEXT CONSTRUCTED tag
957 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
961 """Take off tag from the data
963 :returns: (encoded tag, tag length, remaining data)
966 raise NotEnoughData("no data at all")
967 if byte2int(data) & 0x1F < 31:
968 return data[:1], 1, data[1:]
973 raise DecodeError("unfinished tag")
974 if indexbytes(data, i) & 0x80 == 0:
977 return data[:i], i, data[i:]
983 octets = bytearray(int_bytes_len(l) + 1)
984 octets[0] = 0x80 | (len(octets) - 1)
985 for i in six_xrange(len(octets) - 1, 0, -1):
991 def len_decode(data):
994 :returns: (decoded length, length's length, remaining data)
995 :raises LenIndefForm: if indefinite form encoding is met
998 raise NotEnoughData("no data at all")
999 first_octet = byte2int(data)
1000 if first_octet & 0x80 == 0:
1001 return first_octet, 1, data[1:]
1002 octets_num = first_octet & 0x7F
1003 if octets_num + 1 > len(data):
1004 raise NotEnoughData("encoded length is longer than data")
1006 raise LenIndefForm()
1007 if byte2int(data[1:]) == 0:
1008 raise DecodeError("leading zeros")
1010 for v in iterbytes(data[1:1 + octets_num]):
1013 raise DecodeError("long form instead of short one")
1014 return l, 1 + octets_num, data[1 + octets_num:]
1017 ########################################################################
1019 ########################################################################
1021 class AutoAddSlots(type):
1022 def __new__(mcs, name, bases, _dict):
1023 _dict["__slots__"] = _dict.get("__slots__", ())
1024 return type.__new__(mcs, name, bases, _dict)
1027 @add_metaclass(AutoAddSlots)
1029 """Common ASN.1 object class
1031 All ASN.1 types are inherited from it. It has metaclass that
1032 automatically adds ``__slots__`` to all inherited classes.
1056 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1057 self._expl = getattr(self, "expl", None) if expl is None else expl
1058 if self.tag != self.tag_default and self._expl is not None:
1059 raise ValueError("implicit and explicit tags can not be set simultaneously")
1060 if default is not None:
1062 self.optional = optional
1063 self.offset, self.llen, self.vlen = _decoded
1065 self.expl_lenindef = False
1066 self.lenindef = False
1067 self.ber_encoded = False
1070 def ready(self): # pragma: no cover
1071 """Is object ready to be encoded?
1073 raise NotImplementedError()
1075 def _assert_ready(self):
1077 raise ObjNotReady(self.__class__.__name__)
1081 """Is either object or any elements inside is BER encoded?
1083 return self.expl_lenindef or self.lenindef or self.ber_encoded
1087 """Is object decoded?
1089 return (self.llen + self.vlen) > 0
1091 def copy(self): # pragma: no cover
1092 """Make a copy of object, safe to be mutated
1094 raise NotImplementedError()
1098 return len(self.tag)
1102 return self.tlen + self.llen + self.vlen
1104 def __str__(self): # pragma: no cover
1105 return self.__bytes__() if PY2 else self.__unicode__()
1107 def __ne__(self, their):
1108 return not(self == their)
1110 def __gt__(self, their): # pragma: no cover
1111 return not(self < their)
1113 def __le__(self, their): # pragma: no cover
1114 return (self == their) or (self < their)
1116 def __ge__(self, their): # pragma: no cover
1117 return (self == their) or (self > their)
1119 def _encode(self): # pragma: no cover
1120 raise NotImplementedError()
1122 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1123 raise NotImplementedError()
1126 raw = self._encode()
1127 if self._expl is None:
1129 return b"".join((self._expl, len_encode(len(raw)), raw))
1139 _ctx_immutable=True,
1143 :param data: either binary or memoryview
1144 :param int offset: initial data's offset
1145 :param bool leavemm: do we need to leave memoryview of remaining
1146 data as is, or convert it to bytes otherwise
1147 :param ctx: optional :ref:`context <ctx>` governing decoding process
1148 :param tag_only: decode only the tag, without length and contents
1149 (used only in Choice and Set structures, trying to
1150 determine if tag satisfies the scheme)
1151 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1152 :returns: (Obj, remaining data)
1156 elif _ctx_immutable:
1158 tlv = memoryview(data)
1159 if self._expl is None:
1160 result = self._decode(
1163 decode_path=decode_path,
1172 t, tlen, lv = tag_strip(tlv)
1173 except DecodeError as err:
1174 raise err.__class__(
1176 klass=self.__class__,
1177 decode_path=decode_path,
1182 klass=self.__class__,
1183 decode_path=decode_path,
1187 l, llen, v = len_decode(lv)
1188 except LenIndefForm as err:
1189 if not ctx.get("bered", False):
1190 raise err.__class__(
1192 klass=self.__class__,
1193 decode_path=decode_path,
1197 offset += tlen + llen
1198 result = self._decode(
1201 decode_path=decode_path,
1205 if tag_only: # pragma: no cover
1208 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1209 if eoc_expected.tobytes() != EOC:
1212 klass=self.__class__,
1213 decode_path=decode_path,
1217 obj.expl_lenindef = True
1218 except DecodeError as err:
1219 raise err.__class__(
1221 klass=self.__class__,
1222 decode_path=decode_path,
1227 raise NotEnoughData(
1228 "encoded length is longer than data",
1229 klass=self.__class__,
1230 decode_path=decode_path,
1233 result = self._decode(
1235 offset=offset + tlen + llen,
1236 decode_path=decode_path,
1240 if tag_only: # pragma: no cover
1243 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1245 "explicit tag out-of-bound, longer than data",
1246 klass=self.__class__,
1247 decode_path=decode_path,
1250 return obj, (tail if leavemm else tail.tobytes())
1254 return self._expl is not None
1261 def expl_tlen(self):
1262 return len(self._expl)
1265 def expl_llen(self):
1266 if self.expl_lenindef:
1268 return len(len_encode(self.tlvlen))
1271 def expl_offset(self):
1272 return self.offset - self.expl_tlen - self.expl_llen
1275 def expl_vlen(self):
1279 def expl_tlvlen(self):
1280 return self.expl_tlen + self.expl_llen + self.expl_vlen
1283 def fulloffset(self):
1284 return self.expl_offset if self.expled else self.offset
1288 return self.expl_tlvlen if self.expled else self.tlvlen
1290 def pps_lenindef(self, decode_path):
1291 if self.lenindef and not (
1292 getattr(self, "defined", None) is not None and
1293 self.defined[1].lenindef
1296 asn1_type_name="EOC",
1298 decode_path=decode_path,
1300 self.offset + self.tlvlen -
1301 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1309 if self.expl_lenindef:
1311 asn1_type_name="EOC",
1312 obj_name="EXPLICIT",
1313 decode_path=decode_path,
1314 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1323 class DecodePathDefBy(object):
1324 """DEFINED BY representation inside decode path
1326 __slots__ = ("defined_by",)
1328 def __init__(self, defined_by):
1329 self.defined_by = defined_by
1331 def __ne__(self, their):
1332 return not(self == their)
1334 def __eq__(self, their):
1335 if not isinstance(their, self.__class__):
1337 return self.defined_by == their.defined_by
1340 return "DEFINED BY " + str(self.defined_by)
1343 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1346 ########################################################################
1348 ########################################################################
1350 PP = namedtuple("PP", (
1378 asn1_type_name="unknown",
1395 expl_lenindef=False,
1426 def _colourize(what, colour, with_colours, attrs=("bold",)):
1427 return colored(what, colour, attrs=attrs) if with_colours else what
1430 def colonize_hex(hexed):
1431 """Separate hexadecimal string with colons
1433 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1442 with_decode_path=False,
1443 decode_path_len_decrease=0,
1450 " " if pp.expl_offset is None else
1451 ("-%d" % (pp.offset - pp.expl_offset))
1453 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1455 col = _colourize(col, "red", with_colours, ())
1456 col += _colourize("B", "red", with_colours) if pp.bered else " "
1458 col = "[%d,%d,%4d]%s" % (
1462 LENINDEF_PP_CHAR if pp.lenindef else " "
1464 col = _colourize(col, "green", with_colours, ())
1466 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1467 if decode_path_len > 0:
1468 cols.append(" ." * decode_path_len)
1469 ent = pp.decode_path[-1]
1470 if isinstance(ent, DecodePathDefBy):
1471 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1472 value = str(ent.defined_by)
1475 len(oid_maps) > 0 and
1476 ent.defined_by.asn1_type_name ==
1477 ObjectIdentifier.asn1_type_name
1479 for oid_map in oid_maps:
1480 oid_name = oid_map.get(value)
1481 if oid_name is not None:
1482 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1484 if oid_name is None:
1485 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1487 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1488 if pp.expl is not None:
1489 klass, _, num = pp.expl
1490 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1491 cols.append(_colourize(col, "blue", with_colours))
1492 if pp.impl is not None:
1493 klass, _, num = pp.impl
1494 col = "[%s%d]" % (TagClassReprs[klass], num)
1495 cols.append(_colourize(col, "blue", with_colours))
1496 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1497 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1499 cols.append(_colourize("BER", "red", with_colours))
1500 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1501 if pp.value is not None:
1503 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1505 len(oid_maps) > 0 and
1506 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1508 for oid_map in oid_maps:
1509 oid_name = oid_map.get(value)
1510 if oid_name is not None:
1511 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1513 if pp.asn1_type_name == Integer.asn1_type_name:
1514 hex_repr = hex(int(pp.obj._value))[2:].upper()
1515 if len(hex_repr) % 2 != 0:
1516 hex_repr = "0" + hex_repr
1517 cols.append(_colourize(
1518 "(%s)" % colonize_hex(hex_repr),
1523 if isinstance(pp.blob, binary_type):
1524 cols.append(hexenc(pp.blob))
1525 elif isinstance(pp.blob, tuple):
1526 cols.append(", ".join(pp.blob))
1528 cols.append(_colourize("OPTIONAL", "red", with_colours))
1530 cols.append(_colourize("DEFAULT", "red", with_colours))
1531 if with_decode_path:
1532 cols.append(_colourize(
1533 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1537 return " ".join(cols)
1540 def pp_console_blob(pp, decode_path_len_decrease=0):
1541 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1542 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1543 if decode_path_len > 0:
1544 cols.append(" ." * (decode_path_len + 1))
1545 if isinstance(pp.blob, binary_type):
1546 blob = hexenc(pp.blob).upper()
1547 for i in six_xrange(0, len(blob), 32):
1548 chunk = blob[i:i + 32]
1549 yield " ".join(cols + [colonize_hex(chunk)])
1550 elif isinstance(pp.blob, tuple):
1551 yield " ".join(cols + [", ".join(pp.blob)])
1559 with_decode_path=False,
1560 decode_path_only=(),
1562 """Pretty print object
1564 :param Obj obj: object you want to pretty print
1565 :param oid_maps: list of ``OID <-> humand readable string`` dictionary.
1566 When OID from it is met, then its humand readable form
1568 :param big_blobs: if large binary objects are met (like OctetString
1569 values), do we need to print them too, on separate
1571 :param with_colours: colourize output, if ``termcolor`` library
1573 :param with_decode_path: print decode path
1574 :param decode_path_only: print only that specified decode path
1576 def _pprint_pps(pps):
1578 if hasattr(pp, "_fields"):
1580 decode_path_only != () and
1582 str(p) for p in pp.decode_path[:len(decode_path_only)]
1583 ) != decode_path_only
1587 yield pp_console_row(
1592 with_colours=with_colours,
1593 with_decode_path=with_decode_path,
1594 decode_path_len_decrease=len(decode_path_only),
1596 for row in pp_console_blob(
1598 decode_path_len_decrease=len(decode_path_only),
1602 yield pp_console_row(
1607 with_colours=with_colours,
1608 with_decode_path=with_decode_path,
1609 decode_path_len_decrease=len(decode_path_only),
1612 for row in _pprint_pps(pp):
1614 return "\n".join(_pprint_pps(obj.pps()))
1617 ########################################################################
1618 # ASN.1 primitive types
1619 ########################################################################
1622 """``BOOLEAN`` boolean type
1624 >>> b = Boolean(True)
1626 >>> b == Boolean(True)
1632 tag_default = tag_encode(1)
1633 asn1_type_name = "BOOLEAN"
1645 :param value: set the value. Either boolean type, or
1646 :py:class:`pyderasn.Boolean` object
1647 :param bytes impl: override default tag with ``IMPLICIT`` one
1648 :param bytes expl: override default tag with ``EXPLICIT`` one
1649 :param default: set default value. Type same as in ``value``
1650 :param bool optional: is object ``OPTIONAL`` in sequence
1652 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1653 self._value = None if value is None else self._value_sanitize(value)
1654 if default is not None:
1655 default = self._value_sanitize(default)
1656 self.default = self.__class__(
1662 self._value = default
1664 def _value_sanitize(self, value):
1665 if isinstance(value, bool):
1667 if issubclass(value.__class__, Boolean):
1669 raise InvalidValueType((self.__class__, bool))
1673 return self._value is not None
1676 obj = self.__class__()
1677 obj._value = self._value
1679 obj._expl = self._expl
1680 obj.default = self.default
1681 obj.optional = self.optional
1682 obj.offset = self.offset
1683 obj.llen = self.llen
1684 obj.vlen = self.vlen
1685 obj.expl_lenindef = self.expl_lenindef
1686 obj.lenindef = self.lenindef
1687 obj.ber_encoded = self.ber_encoded
1690 def __nonzero__(self):
1691 self._assert_ready()
1695 self._assert_ready()
1698 def __eq__(self, their):
1699 if isinstance(their, bool):
1700 return self._value == their
1701 if not issubclass(their.__class__, Boolean):
1704 self._value == their._value and
1705 self.tag == their.tag and
1706 self._expl == their._expl
1717 return self.__class__(
1719 impl=self.tag if impl is None else impl,
1720 expl=self._expl if expl is None else expl,
1721 default=self.default if default is None else default,
1722 optional=self.optional if optional is None else optional,
1726 self._assert_ready()
1730 (b"\xFF" if self._value else b"\x00"),
1733 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1735 t, _, lv = tag_strip(tlv)
1736 except DecodeError as err:
1737 raise err.__class__(
1739 klass=self.__class__,
1740 decode_path=decode_path,
1745 klass=self.__class__,
1746 decode_path=decode_path,
1752 l, _, v = len_decode(lv)
1753 except DecodeError as err:
1754 raise err.__class__(
1756 klass=self.__class__,
1757 decode_path=decode_path,
1761 raise InvalidLength(
1762 "Boolean's length must be equal to 1",
1763 klass=self.__class__,
1764 decode_path=decode_path,
1768 raise NotEnoughData(
1769 "encoded length is longer than data",
1770 klass=self.__class__,
1771 decode_path=decode_path,
1774 first_octet = byte2int(v)
1776 if first_octet == 0:
1778 elif first_octet == 0xFF:
1780 elif ctx.get("bered", False):
1785 "unacceptable Boolean value",
1786 klass=self.__class__,
1787 decode_path=decode_path,
1790 obj = self.__class__(
1794 default=self.default,
1795 optional=self.optional,
1796 _decoded=(offset, 1, 1),
1798 obj.ber_encoded = ber_encoded
1802 return pp_console_row(next(self.pps()))
1804 def pps(self, decode_path=()):
1807 asn1_type_name=self.asn1_type_name,
1808 obj_name=self.__class__.__name__,
1809 decode_path=decode_path,
1810 value=str(self._value) if self.ready else None,
1811 optional=self.optional,
1812 default=self == self.default,
1813 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1814 expl=None if self._expl is None else tag_decode(self._expl),
1819 expl_offset=self.expl_offset if self.expled else None,
1820 expl_tlen=self.expl_tlen if self.expled else None,
1821 expl_llen=self.expl_llen if self.expled else None,
1822 expl_vlen=self.expl_vlen if self.expled else None,
1823 expl_lenindef=self.expl_lenindef,
1824 ber_encoded=self.ber_encoded,
1827 for pp in self.pps_lenindef(decode_path):
1832 """``INTEGER`` integer type
1834 >>> b = Integer(-123)
1836 >>> b == Integer(-123)
1841 >>> Integer(2, bounds=(1, 3))
1843 >>> Integer(5, bounds=(1, 3))
1844 Traceback (most recent call last):
1845 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1849 class Version(Integer):
1856 >>> v = Version("v1")
1863 {'v3': 2, 'v1': 0, 'v2': 1}
1865 __slots__ = ("specs", "_bound_min", "_bound_max")
1866 tag_default = tag_encode(2)
1867 asn1_type_name = "INTEGER"
1881 :param value: set the value. Either integer type, named value
1882 (if ``schema`` is specified in the class), or
1883 :py:class:`pyderasn.Integer` object
1884 :param bounds: set ``(MIN, MAX)`` value constraint.
1885 (-inf, +inf) by default
1886 :param bytes impl: override default tag with ``IMPLICIT`` one
1887 :param bytes expl: override default tag with ``EXPLICIT`` one
1888 :param default: set default value. Type same as in ``value``
1889 :param bool optional: is object ``OPTIONAL`` in sequence
1891 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1893 specs = getattr(self, "schema", {}) if _specs is None else _specs
1894 self.specs = specs if isinstance(specs, dict) else dict(specs)
1895 self._bound_min, self._bound_max = getattr(
1898 (float("-inf"), float("+inf")),
1899 ) if bounds is None else bounds
1900 if value is not None:
1901 self._value = self._value_sanitize(value)
1902 if default is not None:
1903 default = self._value_sanitize(default)
1904 self.default = self.__class__(
1910 if self._value is None:
1911 self._value = default
1913 def _value_sanitize(self, value):
1914 if isinstance(value, integer_types):
1916 elif issubclass(value.__class__, Integer):
1917 value = value._value
1918 elif isinstance(value, str):
1919 value = self.specs.get(value)
1921 raise ObjUnknown("integer value: %s" % value)
1923 raise InvalidValueType((self.__class__, int, str))
1924 if not self._bound_min <= value <= self._bound_max:
1925 raise BoundsError(self._bound_min, value, self._bound_max)
1930 return self._value is not None
1933 obj = self.__class__(_specs=self.specs)
1934 obj._value = self._value
1935 obj._bound_min = self._bound_min
1936 obj._bound_max = self._bound_max
1938 obj._expl = self._expl
1939 obj.default = self.default
1940 obj.optional = self.optional
1941 obj.offset = self.offset
1942 obj.llen = self.llen
1943 obj.vlen = self.vlen
1944 obj.expl_lenindef = self.expl_lenindef
1945 obj.lenindef = self.lenindef
1946 obj.ber_encoded = self.ber_encoded
1950 self._assert_ready()
1951 return int(self._value)
1954 self._assert_ready()
1957 bytes(self._expl or b"") +
1958 str(self._value).encode("ascii"),
1961 def __eq__(self, their):
1962 if isinstance(their, integer_types):
1963 return self._value == their
1964 if not issubclass(their.__class__, Integer):
1967 self._value == their._value and
1968 self.tag == their.tag and
1969 self._expl == their._expl
1972 def __lt__(self, their):
1973 return self._value < their._value
1977 for name, value in iteritems(self.specs):
1978 if value == self._value:
1990 return self.__class__(
1993 (self._bound_min, self._bound_max)
1994 if bounds is None else bounds
1996 impl=self.tag if impl is None else impl,
1997 expl=self._expl if expl is None else expl,
1998 default=self.default if default is None else default,
1999 optional=self.optional if optional is None else optional,
2004 self._assert_ready()
2008 octets = bytearray([0])
2012 octets = bytearray()
2014 octets.append((value & 0xFF) ^ 0xFF)
2016 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2019 octets = bytearray()
2021 octets.append(value & 0xFF)
2023 if octets[-1] & 0x80 > 0:
2026 octets = bytes(octets)
2028 bytes_len = ceil(value.bit_length() / 8) or 1
2031 octets = value.to_bytes(
2036 except OverflowError:
2040 return b"".join((self.tag, len_encode(len(octets)), octets))
2042 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2044 t, _, lv = tag_strip(tlv)
2045 except DecodeError as err:
2046 raise err.__class__(
2048 klass=self.__class__,
2049 decode_path=decode_path,
2054 klass=self.__class__,
2055 decode_path=decode_path,
2061 l, llen, v = len_decode(lv)
2062 except DecodeError as err:
2063 raise err.__class__(
2065 klass=self.__class__,
2066 decode_path=decode_path,
2070 raise NotEnoughData(
2071 "encoded length is longer than data",
2072 klass=self.__class__,
2073 decode_path=decode_path,
2077 raise NotEnoughData(
2079 klass=self.__class__,
2080 decode_path=decode_path,
2083 v, tail = v[:l], v[l:]
2084 first_octet = byte2int(v)
2086 second_octet = byte2int(v[1:])
2088 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2089 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2092 "non normalized integer",
2093 klass=self.__class__,
2094 decode_path=decode_path,
2099 if first_octet & 0x80 > 0:
2100 octets = bytearray()
2101 for octet in bytearray(v):
2102 octets.append(octet ^ 0xFF)
2103 for octet in octets:
2104 value = (value << 8) | octet
2108 for octet in bytearray(v):
2109 value = (value << 8) | octet
2111 value = int.from_bytes(v, byteorder="big", signed=True)
2113 obj = self.__class__(
2115 bounds=(self._bound_min, self._bound_max),
2118 default=self.default,
2119 optional=self.optional,
2121 _decoded=(offset, llen, l),
2123 except BoundsError as err:
2126 klass=self.__class__,
2127 decode_path=decode_path,
2133 return pp_console_row(next(self.pps()))
2135 def pps(self, decode_path=()):
2138 asn1_type_name=self.asn1_type_name,
2139 obj_name=self.__class__.__name__,
2140 decode_path=decode_path,
2141 value=(self.named or str(self._value)) if self.ready else None,
2142 optional=self.optional,
2143 default=self == self.default,
2144 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2145 expl=None if self._expl is None else tag_decode(self._expl),
2150 expl_offset=self.expl_offset if self.expled else None,
2151 expl_tlen=self.expl_tlen if self.expled else None,
2152 expl_llen=self.expl_llen if self.expled else None,
2153 expl_vlen=self.expl_vlen if self.expled else None,
2154 expl_lenindef=self.expl_lenindef,
2157 for pp in self.pps_lenindef(decode_path):
2161 SET01 = frozenset(("0", "1"))
2164 class BitString(Obj):
2165 """``BIT STRING`` bit string type
2167 >>> BitString(b"hello world")
2168 BIT STRING 88 bits 68656c6c6f20776f726c64
2171 >>> b == b"hello world"
2176 >>> BitString("'0A3B5F291CD'H")
2177 BIT STRING 44 bits 0a3b5f291cd0
2178 >>> b = BitString("'010110000000'B")
2179 BIT STRING 12 bits 5800
2182 >>> b[0], b[1], b[2], b[3]
2183 (False, True, False, True)
2187 [False, True, False, True, True, False, False, False, False, False, False, False]
2191 class KeyUsage(BitString):
2193 ("digitalSignature", 0),
2194 ("nonRepudiation", 1),
2195 ("keyEncipherment", 2),
2198 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2199 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2201 ['nonRepudiation', 'keyEncipherment']
2203 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2207 Pay attention that BIT STRING can be encoded both in primitive
2208 and constructed forms. Decoder always checks constructed form tag
2209 additionally to specified primitive one. If BER decoding is
2210 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2211 of DER restrictions.
2213 __slots__ = ("tag_constructed", "specs", "defined")
2214 tag_default = tag_encode(3)
2215 asn1_type_name = "BIT STRING"
2228 :param value: set the value. Either binary type, tuple of named
2229 values (if ``schema`` is specified in the class),
2230 string in ``'XXX...'B`` form, or
2231 :py:class:`pyderasn.BitString` object
2232 :param bytes impl: override default tag with ``IMPLICIT`` one
2233 :param bytes expl: override default tag with ``EXPLICIT`` one
2234 :param default: set default value. Type same as in ``value``
2235 :param bool optional: is object ``OPTIONAL`` in sequence
2237 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2238 specs = getattr(self, "schema", {}) if _specs is None else _specs
2239 self.specs = specs if isinstance(specs, dict) else dict(specs)
2240 self._value = None if value is None else self._value_sanitize(value)
2241 if default is not None:
2242 default = self._value_sanitize(default)
2243 self.default = self.__class__(
2249 self._value = default
2251 tag_klass, _, tag_num = tag_decode(self.tag)
2252 self.tag_constructed = tag_encode(
2254 form=TagFormConstructed,
2258 def _bits2octets(self, bits):
2259 if len(self.specs) > 0:
2260 bits = bits.rstrip("0")
2262 bits += "0" * ((8 - (bit_len % 8)) % 8)
2263 octets = bytearray(len(bits) // 8)
2264 for i in six_xrange(len(octets)):
2265 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2266 return bit_len, bytes(octets)
2268 def _value_sanitize(self, value):
2269 if isinstance(value, (string_types, binary_type)):
2271 isinstance(value, string_types) and
2272 value.startswith("'")
2274 if value.endswith("'B"):
2276 if not frozenset(value) <= SET01:
2277 raise ValueError("B's coding contains unacceptable chars")
2278 return self._bits2octets(value)
2279 elif value.endswith("'H"):
2283 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2285 if isinstance(value, binary_type):
2286 return (len(value) * 8, value)
2288 raise InvalidValueType((self.__class__, string_types, binary_type))
2289 if isinstance(value, tuple):
2292 isinstance(value[0], integer_types) and
2293 isinstance(value[1], binary_type)
2298 bit = self.specs.get(name)
2300 raise ObjUnknown("BitString value: %s" % name)
2303 return self._bits2octets("")
2304 bits = frozenset(bits)
2305 return self._bits2octets("".join(
2306 ("1" if bit in bits else "0")
2307 for bit in six_xrange(max(bits) + 1)
2309 if issubclass(value.__class__, BitString):
2311 raise InvalidValueType((self.__class__, binary_type, string_types))
2315 return self._value is not None
2318 obj = self.__class__(_specs=self.specs)
2320 if value is not None:
2321 value = (value[0], value[1])
2324 obj._expl = self._expl
2325 obj.default = self.default
2326 obj.optional = self.optional
2327 obj.offset = self.offset
2328 obj.llen = self.llen
2329 obj.vlen = self.vlen
2330 obj.expl_lenindef = self.expl_lenindef
2331 obj.lenindef = self.lenindef
2332 obj.ber_encoded = self.ber_encoded
2336 self._assert_ready()
2337 for i in six_xrange(self._value[0]):
2342 self._assert_ready()
2343 return self._value[0]
2345 def __bytes__(self):
2346 self._assert_ready()
2347 return self._value[1]
2349 def __eq__(self, their):
2350 if isinstance(their, bytes):
2351 return self._value[1] == their
2352 if not issubclass(their.__class__, BitString):
2355 self._value == their._value and
2356 self.tag == their.tag and
2357 self._expl == their._expl
2362 return [name for name, bit in iteritems(self.specs) if self[bit]]
2372 return self.__class__(
2374 impl=self.tag if impl is None else impl,
2375 expl=self._expl if expl is None else expl,
2376 default=self.default if default is None else default,
2377 optional=self.optional if optional is None else optional,
2381 def __getitem__(self, key):
2382 if isinstance(key, int):
2383 bit_len, octets = self._value
2387 byte2int(memoryview(octets)[key // 8:]) >>
2390 if isinstance(key, string_types):
2391 value = self.specs.get(key)
2393 raise ObjUnknown("BitString value: %s" % key)
2395 raise InvalidValueType((int, str))
2398 self._assert_ready()
2399 bit_len, octets = self._value
2402 len_encode(len(octets) + 1),
2403 int2byte((8 - bit_len % 8) % 8),
2407 def _decode_chunk(self, lv, offset, decode_path, ctx):
2409 l, llen, v = len_decode(lv)
2410 except DecodeError as err:
2411 raise err.__class__(
2413 klass=self.__class__,
2414 decode_path=decode_path,
2418 raise NotEnoughData(
2419 "encoded length is longer than data",
2420 klass=self.__class__,
2421 decode_path=decode_path,
2425 raise NotEnoughData(
2427 klass=self.__class__,
2428 decode_path=decode_path,
2431 pad_size = byte2int(v)
2432 if l == 1 and pad_size != 0:
2434 "invalid empty value",
2435 klass=self.__class__,
2436 decode_path=decode_path,
2442 klass=self.__class__,
2443 decode_path=decode_path,
2446 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2449 klass=self.__class__,
2450 decode_path=decode_path,
2453 v, tail = v[:l], v[l:]
2454 obj = self.__class__(
2455 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2458 default=self.default,
2459 optional=self.optional,
2461 _decoded=(offset, llen, l),
2465 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2467 t, tlen, lv = tag_strip(tlv)
2468 except DecodeError as err:
2469 raise err.__class__(
2471 klass=self.__class__,
2472 decode_path=decode_path,
2476 if tag_only: # pragma: no cover
2478 return self._decode_chunk(lv, offset, decode_path, ctx)
2479 if t == self.tag_constructed:
2480 if not ctx.get("bered", False):
2482 "unallowed BER constructed encoding",
2483 klass=self.__class__,
2484 decode_path=decode_path,
2487 if tag_only: # pragma: no cover
2491 l, llen, v = len_decode(lv)
2492 except LenIndefForm:
2493 llen, l, v = 1, 0, lv[1:]
2495 except DecodeError as err:
2496 raise err.__class__(
2498 klass=self.__class__,
2499 decode_path=decode_path,
2503 raise NotEnoughData(
2504 "encoded length is longer than data",
2505 klass=self.__class__,
2506 decode_path=decode_path,
2509 if not lenindef and l == 0:
2510 raise NotEnoughData(
2512 klass=self.__class__,
2513 decode_path=decode_path,
2517 sub_offset = offset + tlen + llen
2521 if v[:EOC_LEN].tobytes() == EOC:
2528 "chunk out of bounds",
2529 klass=self.__class__,
2530 decode_path=decode_path + (str(len(chunks) - 1),),
2531 offset=chunks[-1].offset,
2533 sub_decode_path = decode_path + (str(len(chunks)),)
2535 chunk, v_tail = BitString().decode(
2538 decode_path=sub_decode_path,
2541 _ctx_immutable=False,
2545 "expected BitString encoded chunk",
2546 klass=self.__class__,
2547 decode_path=sub_decode_path,
2550 chunks.append(chunk)
2551 sub_offset += chunk.tlvlen
2552 vlen += chunk.tlvlen
2554 if len(chunks) == 0:
2557 klass=self.__class__,
2558 decode_path=decode_path,
2563 for chunk_i, chunk in enumerate(chunks[:-1]):
2564 if chunk.bit_len % 8 != 0:
2566 "BitString chunk is not multiple of 8 bits",
2567 klass=self.__class__,
2568 decode_path=decode_path + (str(chunk_i),),
2569 offset=chunk.offset,
2571 values.append(bytes(chunk))
2572 bit_len += chunk.bit_len
2573 chunk_last = chunks[-1]
2574 values.append(bytes(chunk_last))
2575 bit_len += chunk_last.bit_len
2576 obj = self.__class__(
2577 value=(bit_len, b"".join(values)),
2580 default=self.default,
2581 optional=self.optional,
2583 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2585 obj.lenindef = lenindef
2586 obj.ber_encoded = True
2587 return obj, (v[EOC_LEN:] if lenindef else v)
2589 klass=self.__class__,
2590 decode_path=decode_path,
2595 return pp_console_row(next(self.pps()))
2597 def pps(self, decode_path=()):
2601 bit_len, blob = self._value
2602 value = "%d bits" % bit_len
2603 if len(self.specs) > 0:
2604 blob = tuple(self.named)
2607 asn1_type_name=self.asn1_type_name,
2608 obj_name=self.__class__.__name__,
2609 decode_path=decode_path,
2612 optional=self.optional,
2613 default=self == self.default,
2614 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2615 expl=None if self._expl is None else tag_decode(self._expl),
2620 expl_offset=self.expl_offset if self.expled else None,
2621 expl_tlen=self.expl_tlen if self.expled else None,
2622 expl_llen=self.expl_llen if self.expled else None,
2623 expl_vlen=self.expl_vlen if self.expled else None,
2624 expl_lenindef=self.expl_lenindef,
2625 lenindef=self.lenindef,
2626 ber_encoded=self.ber_encoded,
2629 defined_by, defined = self.defined or (None, None)
2630 if defined_by is not None:
2632 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2634 for pp in self.pps_lenindef(decode_path):
2638 class OctetString(Obj):
2639 """``OCTET STRING`` binary string type
2641 >>> s = OctetString(b"hello world")
2642 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2643 >>> s == OctetString(b"hello world")
2648 >>> OctetString(b"hello", bounds=(4, 4))
2649 Traceback (most recent call last):
2650 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2651 >>> OctetString(b"hell", bounds=(4, 4))
2652 OCTET STRING 4 bytes 68656c6c
2656 Pay attention that OCTET STRING can be encoded both in primitive
2657 and constructed forms. Decoder always checks constructed form tag
2658 additionally to specified primitive one. If BER decoding is
2659 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2660 of DER restrictions.
2662 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2663 tag_default = tag_encode(4)
2664 asn1_type_name = "OCTET STRING"
2677 :param value: set the value. Either binary type, or
2678 :py:class:`pyderasn.OctetString` object
2679 :param bounds: set ``(MIN, MAX)`` value size constraint.
2680 (-inf, +inf) by default
2681 :param bytes impl: override default tag with ``IMPLICIT`` one
2682 :param bytes expl: override default tag with ``EXPLICIT`` one
2683 :param default: set default value. Type same as in ``value``
2684 :param bool optional: is object ``OPTIONAL`` in sequence
2686 super(OctetString, self).__init__(
2694 self._bound_min, self._bound_max = getattr(
2698 ) if bounds is None else bounds
2699 if value is not None:
2700 self._value = self._value_sanitize(value)
2701 if default is not None:
2702 default = self._value_sanitize(default)
2703 self.default = self.__class__(
2708 if self._value is None:
2709 self._value = default
2711 tag_klass, _, tag_num = tag_decode(self.tag)
2712 self.tag_constructed = tag_encode(
2714 form=TagFormConstructed,
2718 def _value_sanitize(self, value):
2719 if isinstance(value, binary_type):
2721 elif issubclass(value.__class__, OctetString):
2722 value = value._value
2724 raise InvalidValueType((self.__class__, bytes))
2725 if not self._bound_min <= len(value) <= self._bound_max:
2726 raise BoundsError(self._bound_min, len(value), self._bound_max)
2731 return self._value is not None
2734 obj = self.__class__()
2735 obj._value = self._value
2736 obj._bound_min = self._bound_min
2737 obj._bound_max = self._bound_max
2739 obj._expl = self._expl
2740 obj.default = self.default
2741 obj.optional = self.optional
2742 obj.offset = self.offset
2743 obj.llen = self.llen
2744 obj.vlen = self.vlen
2745 obj.expl_lenindef = self.expl_lenindef
2746 obj.lenindef = self.lenindef
2747 obj.ber_encoded = self.ber_encoded
2750 def __bytes__(self):
2751 self._assert_ready()
2754 def __eq__(self, their):
2755 if isinstance(their, binary_type):
2756 return self._value == their
2757 if not issubclass(their.__class__, OctetString):
2760 self._value == their._value and
2761 self.tag == their.tag and
2762 self._expl == their._expl
2765 def __lt__(self, their):
2766 return self._value < their._value
2777 return self.__class__(
2780 (self._bound_min, self._bound_max)
2781 if bounds is None else bounds
2783 impl=self.tag if impl is None else impl,
2784 expl=self._expl if expl is None else expl,
2785 default=self.default if default is None else default,
2786 optional=self.optional if optional is None else optional,
2790 self._assert_ready()
2793 len_encode(len(self._value)),
2797 def _decode_chunk(self, lv, offset, decode_path, ctx):
2799 l, llen, v = len_decode(lv)
2800 except DecodeError as err:
2801 raise err.__class__(
2803 klass=self.__class__,
2804 decode_path=decode_path,
2808 raise NotEnoughData(
2809 "encoded length is longer than data",
2810 klass=self.__class__,
2811 decode_path=decode_path,
2814 v, tail = v[:l], v[l:]
2816 obj = self.__class__(
2818 bounds=(self._bound_min, self._bound_max),
2821 default=self.default,
2822 optional=self.optional,
2823 _decoded=(offset, llen, l),
2825 except DecodeError as err:
2828 klass=self.__class__,
2829 decode_path=decode_path,
2832 except BoundsError as err:
2835 klass=self.__class__,
2836 decode_path=decode_path,
2841 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2843 t, tlen, lv = tag_strip(tlv)
2844 except DecodeError as err:
2845 raise err.__class__(
2847 klass=self.__class__,
2848 decode_path=decode_path,
2854 return self._decode_chunk(lv, offset, decode_path, ctx)
2855 if t == self.tag_constructed:
2856 if not ctx.get("bered", False):
2858 "unallowed BER constructed encoding",
2859 klass=self.__class__,
2860 decode_path=decode_path,
2867 l, llen, v = len_decode(lv)
2868 except LenIndefForm:
2869 llen, l, v = 1, 0, lv[1:]
2871 except DecodeError as err:
2872 raise err.__class__(
2874 klass=self.__class__,
2875 decode_path=decode_path,
2879 raise NotEnoughData(
2880 "encoded length is longer than data",
2881 klass=self.__class__,
2882 decode_path=decode_path,
2886 sub_offset = offset + tlen + llen
2890 if v[:EOC_LEN].tobytes() == EOC:
2897 "chunk out of bounds",
2898 klass=self.__class__,
2899 decode_path=decode_path + (str(len(chunks) - 1),),
2900 offset=chunks[-1].offset,
2902 sub_decode_path = decode_path + (str(len(chunks)),)
2904 chunk, v_tail = OctetString().decode(
2907 decode_path=sub_decode_path,
2910 _ctx_immutable=False,
2914 "expected OctetString encoded chunk",
2915 klass=self.__class__,
2916 decode_path=sub_decode_path,
2919 chunks.append(chunk)
2920 sub_offset += chunk.tlvlen
2921 vlen += chunk.tlvlen
2924 obj = self.__class__(
2925 value=b"".join(bytes(chunk) for chunk in chunks),
2926 bounds=(self._bound_min, self._bound_max),
2929 default=self.default,
2930 optional=self.optional,
2931 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2933 except DecodeError as err:
2936 klass=self.__class__,
2937 decode_path=decode_path,
2940 except BoundsError as err:
2943 klass=self.__class__,
2944 decode_path=decode_path,
2947 obj.lenindef = lenindef
2948 obj.ber_encoded = True
2949 return obj, (v[EOC_LEN:] if lenindef else v)
2951 klass=self.__class__,
2952 decode_path=decode_path,
2957 return pp_console_row(next(self.pps()))
2959 def pps(self, decode_path=()):
2962 asn1_type_name=self.asn1_type_name,
2963 obj_name=self.__class__.__name__,
2964 decode_path=decode_path,
2965 value=("%d bytes" % len(self._value)) if self.ready else None,
2966 blob=self._value if self.ready else None,
2967 optional=self.optional,
2968 default=self == self.default,
2969 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2970 expl=None if self._expl is None else tag_decode(self._expl),
2975 expl_offset=self.expl_offset if self.expled else None,
2976 expl_tlen=self.expl_tlen if self.expled else None,
2977 expl_llen=self.expl_llen if self.expled else None,
2978 expl_vlen=self.expl_vlen if self.expled else None,
2979 expl_lenindef=self.expl_lenindef,
2980 lenindef=self.lenindef,
2981 ber_encoded=self.ber_encoded,
2984 defined_by, defined = self.defined or (None, None)
2985 if defined_by is not None:
2987 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2989 for pp in self.pps_lenindef(decode_path):
2994 """``NULL`` null object
3002 tag_default = tag_encode(5)
3003 asn1_type_name = "NULL"
3007 value=None, # unused, but Sequence passes it
3014 :param bytes impl: override default tag with ``IMPLICIT`` one
3015 :param bytes expl: override default tag with ``EXPLICIT`` one
3016 :param bool optional: is object ``OPTIONAL`` in sequence
3018 super(Null, self).__init__(impl, expl, None, optional, _decoded)
3026 obj = self.__class__()
3028 obj._expl = self._expl
3029 obj.default = self.default
3030 obj.optional = self.optional
3031 obj.offset = self.offset
3032 obj.llen = self.llen
3033 obj.vlen = self.vlen
3034 obj.expl_lenindef = self.expl_lenindef
3035 obj.lenindef = self.lenindef
3036 obj.ber_encoded = self.ber_encoded
3039 def __eq__(self, their):
3040 if not issubclass(their.__class__, Null):
3043 self.tag == their.tag and
3044 self._expl == their._expl
3054 return self.__class__(
3055 impl=self.tag if impl is None else impl,
3056 expl=self._expl if expl is None else expl,
3057 optional=self.optional if optional is None else optional,
3061 return self.tag + len_encode(0)
3063 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3065 t, _, lv = tag_strip(tlv)
3066 except DecodeError as err:
3067 raise err.__class__(
3069 klass=self.__class__,
3070 decode_path=decode_path,
3075 klass=self.__class__,
3076 decode_path=decode_path,
3079 if tag_only: # pragma: no cover
3082 l, _, v = len_decode(lv)
3083 except DecodeError as err:
3084 raise err.__class__(
3086 klass=self.__class__,
3087 decode_path=decode_path,
3091 raise InvalidLength(
3092 "Null must have zero length",
3093 klass=self.__class__,
3094 decode_path=decode_path,
3097 obj = self.__class__(
3100 optional=self.optional,
3101 _decoded=(offset, 1, 0),
3106 return pp_console_row(next(self.pps()))
3108 def pps(self, decode_path=()):
3111 asn1_type_name=self.asn1_type_name,
3112 obj_name=self.__class__.__name__,
3113 decode_path=decode_path,
3114 optional=self.optional,
3115 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3116 expl=None if self._expl is None else tag_decode(self._expl),
3121 expl_offset=self.expl_offset if self.expled else None,
3122 expl_tlen=self.expl_tlen if self.expled else None,
3123 expl_llen=self.expl_llen if self.expled else None,
3124 expl_vlen=self.expl_vlen if self.expled else None,
3125 expl_lenindef=self.expl_lenindef,
3128 for pp in self.pps_lenindef(decode_path):
3132 class ObjectIdentifier(Obj):
3133 """``OBJECT IDENTIFIER`` OID type
3135 >>> oid = ObjectIdentifier((1, 2, 3))
3136 OBJECT IDENTIFIER 1.2.3
3137 >>> oid == ObjectIdentifier("1.2.3")
3143 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3144 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3146 >>> str(ObjectIdentifier((3, 1)))
3147 Traceback (most recent call last):
3148 pyderasn.InvalidOID: unacceptable first arc value
3150 __slots__ = ("defines",)
3151 tag_default = tag_encode(6)
3152 asn1_type_name = "OBJECT IDENTIFIER"
3165 :param value: set the value. Either tuples of integers,
3166 string of "."-concatenated integers, or
3167 :py:class:`pyderasn.ObjectIdentifier` object
3168 :param defines: sequence of tuples. Each tuple has two elements.
3169 First one is relative to current one decode
3170 path, aiming to the field defined by that OID.
3171 Read about relative path in
3172 :py:func:`pyderasn.abs_decode_path`. Second
3173 tuple element is ``{OID: pyderasn.Obj()}``
3174 dictionary, mapping between current OID value
3175 and structure applied to defined field.
3176 :ref:`Read about DEFINED BY <definedby>`
3177 :param bytes impl: override default tag with ``IMPLICIT`` one
3178 :param bytes expl: override default tag with ``EXPLICIT`` one
3179 :param default: set default value. Type same as in ``value``
3180 :param bool optional: is object ``OPTIONAL`` in sequence
3182 super(ObjectIdentifier, self).__init__(
3190 if value is not None:
3191 self._value = self._value_sanitize(value)
3192 if default is not None:
3193 default = self._value_sanitize(default)
3194 self.default = self.__class__(
3199 if self._value is None:
3200 self._value = default
3201 self.defines = defines
3203 def __add__(self, their):
3204 if isinstance(their, self.__class__):
3205 return self.__class__(self._value + their._value)
3206 if isinstance(their, tuple):
3207 return self.__class__(self._value + their)
3208 raise InvalidValueType((self.__class__, tuple))
3210 def _value_sanitize(self, value):
3211 if issubclass(value.__class__, ObjectIdentifier):
3213 if isinstance(value, string_types):
3215 value = tuple(int(arc) for arc in value.split("."))
3217 raise InvalidOID("unacceptable arcs values")
3218 if isinstance(value, tuple):
3220 raise InvalidOID("less than 2 arcs")
3221 first_arc = value[0]
3222 if first_arc in (0, 1):
3223 if not (0 <= value[1] <= 39):
3224 raise InvalidOID("second arc is too wide")
3225 elif first_arc == 2:
3228 raise InvalidOID("unacceptable first arc value")
3230 raise InvalidValueType((self.__class__, str, tuple))
3234 return self._value is not None
3237 obj = self.__class__()
3238 obj._value = self._value
3239 obj.defines = self.defines
3241 obj._expl = self._expl
3242 obj.default = self.default
3243 obj.optional = self.optional
3244 obj.offset = self.offset
3245 obj.llen = self.llen
3246 obj.vlen = self.vlen
3247 obj.expl_lenindef = self.expl_lenindef
3248 obj.lenindef = self.lenindef
3249 obj.ber_encoded = self.ber_encoded
3253 self._assert_ready()
3254 return iter(self._value)
3257 return ".".join(str(arc) for arc in self._value or ())
3260 self._assert_ready()
3263 bytes(self._expl or b"") +
3264 str(self._value).encode("ascii"),
3267 def __eq__(self, their):
3268 if isinstance(their, tuple):
3269 return self._value == their
3270 if not issubclass(their.__class__, ObjectIdentifier):
3273 self.tag == their.tag and
3274 self._expl == their._expl and
3275 self._value == their._value
3278 def __lt__(self, their):
3279 return self._value < their._value
3290 return self.__class__(
3292 defines=self.defines if defines is None else defines,
3293 impl=self.tag if impl is None else impl,
3294 expl=self._expl if expl is None else expl,
3295 default=self.default if default is None else default,
3296 optional=self.optional if optional is None else optional,
3300 self._assert_ready()
3302 first_value = value[1]
3303 first_arc = value[0]
3306 elif first_arc == 1:
3308 elif first_arc == 2:
3310 else: # pragma: no cover
3311 raise RuntimeError("invalid arc is stored")
3312 octets = [zero_ended_encode(first_value)]
3313 for arc in value[2:]:
3314 octets.append(zero_ended_encode(arc))
3315 v = b"".join(octets)
3316 return b"".join((self.tag, len_encode(len(v)), v))
3318 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3320 t, _, lv = tag_strip(tlv)
3321 except DecodeError as err:
3322 raise err.__class__(
3324 klass=self.__class__,
3325 decode_path=decode_path,
3330 klass=self.__class__,
3331 decode_path=decode_path,
3334 if tag_only: # pragma: no cover
3337 l, llen, v = len_decode(lv)
3338 except DecodeError as err:
3339 raise err.__class__(
3341 klass=self.__class__,
3342 decode_path=decode_path,
3346 raise NotEnoughData(
3347 "encoded length is longer than data",
3348 klass=self.__class__,
3349 decode_path=decode_path,
3353 raise NotEnoughData(
3355 klass=self.__class__,
3356 decode_path=decode_path,
3359 v, tail = v[:l], v[l:]
3366 octet = indexbytes(v, i)
3367 if i == 0 and octet == 0x80:
3368 if ctx.get("bered", False):
3371 raise DecodeError("non normalized arc encoding")
3372 arc = (arc << 7) | (octet & 0x7F)
3373 if octet & 0x80 == 0:
3381 klass=self.__class__,
3382 decode_path=decode_path,
3386 second_arc = arcs[0]
3387 if 0 <= second_arc <= 39:
3389 elif 40 <= second_arc <= 79:
3395 obj = self.__class__(
3396 value=tuple([first_arc, second_arc] + arcs[1:]),
3399 default=self.default,
3400 optional=self.optional,
3401 _decoded=(offset, llen, l),
3404 obj.ber_encoded = True
3408 return pp_console_row(next(self.pps()))
3410 def pps(self, decode_path=()):
3413 asn1_type_name=self.asn1_type_name,
3414 obj_name=self.__class__.__name__,
3415 decode_path=decode_path,
3416 value=str(self) if self.ready else None,
3417 optional=self.optional,
3418 default=self == self.default,
3419 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3420 expl=None if self._expl is None else tag_decode(self._expl),
3425 expl_offset=self.expl_offset if self.expled else None,
3426 expl_tlen=self.expl_tlen if self.expled else None,
3427 expl_llen=self.expl_llen if self.expled else None,
3428 expl_vlen=self.expl_vlen if self.expled else None,
3429 expl_lenindef=self.expl_lenindef,
3430 ber_encoded=self.ber_encoded,
3433 for pp in self.pps_lenindef(decode_path):
3437 class Enumerated(Integer):
3438 """``ENUMERATED`` integer type
3440 This type is identical to :py:class:`pyderasn.Integer`, but requires
3441 schema to be specified and does not accept values missing from it.
3444 tag_default = tag_encode(10)
3445 asn1_type_name = "ENUMERATED"
3456 bounds=None, # dummy argument, workability for Integer.decode
3458 super(Enumerated, self).__init__(
3467 if len(self.specs) == 0:
3468 raise ValueError("schema must be specified")
3470 def _value_sanitize(self, value):
3471 if isinstance(value, self.__class__):
3472 value = value._value
3473 elif isinstance(value, integer_types):
3474 for _value in itervalues(self.specs):
3479 "unknown integer value: %s" % value,
3480 klass=self.__class__,
3482 elif isinstance(value, string_types):
3483 value = self.specs.get(value)
3485 raise ObjUnknown("integer value: %s" % value)
3487 raise InvalidValueType((self.__class__, int, str))
3491 obj = self.__class__(_specs=self.specs)
3492 obj._value = self._value
3493 obj._bound_min = self._bound_min
3494 obj._bound_max = self._bound_max
3496 obj._expl = self._expl
3497 obj.default = self.default
3498 obj.optional = self.optional
3499 obj.offset = self.offset
3500 obj.llen = self.llen
3501 obj.vlen = self.vlen
3502 obj.expl_lenindef = self.expl_lenindef
3503 obj.lenindef = self.lenindef
3504 obj.ber_encoded = self.ber_encoded
3516 return self.__class__(
3518 impl=self.tag if impl is None else impl,
3519 expl=self._expl if expl is None else expl,
3520 default=self.default if default is None else default,
3521 optional=self.optional if optional is None else optional,
3526 class CommonString(OctetString):
3527 """Common class for all strings
3529 Everything resembles :py:class:`pyderasn.OctetString`, except
3530 ability to deal with unicode text strings.
3532 >>> hexenc("привет мир".encode("utf-8"))
3533 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3534 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3536 >>> s = UTF8String("привет мир")
3537 UTF8String UTF8String привет мир
3539 'привет мир'
3540 >>> hexenc(bytes(s))
3541 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3543 >>> PrintableString("привет мир")
3544 Traceback (most recent call last):
3545 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3547 >>> BMPString("ада", bounds=(2, 2))
3548 Traceback (most recent call last):
3549 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3550 >>> s = BMPString("ад", bounds=(2, 2))
3553 >>> hexenc(bytes(s))
3561 * - :py:class:`pyderasn.UTF8String`
3563 * - :py:class:`pyderasn.NumericString`
3565 * - :py:class:`pyderasn.PrintableString`
3567 * - :py:class:`pyderasn.TeletexString`
3569 * - :py:class:`pyderasn.T61String`
3571 * - :py:class:`pyderasn.VideotexString`
3573 * - :py:class:`pyderasn.IA5String`
3575 * - :py:class:`pyderasn.GraphicString`
3577 * - :py:class:`pyderasn.VisibleString`
3579 * - :py:class:`pyderasn.ISO646String`
3581 * - :py:class:`pyderasn.GeneralString`
3583 * - :py:class:`pyderasn.UniversalString`
3585 * - :py:class:`pyderasn.BMPString`
3588 __slots__ = ("encoding",)
3590 def _value_sanitize(self, value):
3592 value_decoded = None
3593 if isinstance(value, self.__class__):
3594 value_raw = value._value
3595 elif isinstance(value, text_type):
3596 value_decoded = value
3597 elif isinstance(value, binary_type):
3600 raise InvalidValueType((self.__class__, text_type, binary_type))
3603 value_decoded.encode(self.encoding)
3604 if value_raw is None else value_raw
3607 value_raw.decode(self.encoding)
3608 if value_decoded is None else value_decoded
3610 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3611 raise DecodeError(str(err))
3612 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3620 def __eq__(self, their):
3621 if isinstance(their, binary_type):
3622 return self._value == their
3623 if isinstance(their, text_type):
3624 return self._value == their.encode(self.encoding)
3625 if not isinstance(their, self.__class__):
3628 self._value == their._value and
3629 self.tag == their.tag and
3630 self._expl == their._expl
3633 def __unicode__(self):
3635 return self._value.decode(self.encoding)
3636 return text_type(self._value)
3639 return pp_console_row(next(self.pps(no_unicode=PY2)))
3641 def pps(self, decode_path=(), no_unicode=False):
3644 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3647 asn1_type_name=self.asn1_type_name,
3648 obj_name=self.__class__.__name__,
3649 decode_path=decode_path,
3651 optional=self.optional,
3652 default=self == self.default,
3653 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3654 expl=None if self._expl is None else tag_decode(self._expl),
3659 expl_offset=self.expl_offset if self.expled else None,
3660 expl_tlen=self.expl_tlen if self.expled else None,
3661 expl_llen=self.expl_llen if self.expled else None,
3662 expl_vlen=self.expl_vlen if self.expled else None,
3663 expl_lenindef=self.expl_lenindef,
3664 ber_encoded=self.ber_encoded,
3667 for pp in self.pps_lenindef(decode_path):
3671 class UTF8String(CommonString):
3673 tag_default = tag_encode(12)
3675 asn1_type_name = "UTF8String"
3678 class AllowableCharsMixin(object):
3680 def allowable_chars(self):
3682 return self._allowable_chars
3683 return frozenset(six_unichr(c) for c in self._allowable_chars)
3686 class NumericString(AllowableCharsMixin, CommonString):
3689 Its value is properly sanitized: only ASCII digits with spaces can
3692 >>> NumericString().allowable_chars
3693 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
3696 tag_default = tag_encode(18)
3698 asn1_type_name = "NumericString"
3699 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3701 def _value_sanitize(self, value):
3702 value = super(NumericString, self)._value_sanitize(value)
3703 if not frozenset(value) <= self._allowable_chars:
3704 raise DecodeError("non-numeric value")
3708 class PrintableString(AllowableCharsMixin, CommonString):
3711 Its value is properly sanitized: see X.680 41.4 table 10.
3713 >>> PrintableString().allowable_chars
3714 frozenset([' ', "'", ..., 'z'])
3717 tag_default = tag_encode(19)
3719 asn1_type_name = "PrintableString"
3720 _allowable_chars = frozenset(
3721 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3724 def _value_sanitize(self, value):
3725 value = super(PrintableString, self)._value_sanitize(value)
3726 if not frozenset(value) <= self._allowable_chars:
3727 raise DecodeError("non-printable value")
3731 class TeletexString(CommonString):
3733 tag_default = tag_encode(20)
3735 asn1_type_name = "TeletexString"
3738 class T61String(TeletexString):
3740 asn1_type_name = "T61String"
3743 class VideotexString(CommonString):
3745 tag_default = tag_encode(21)
3746 encoding = "iso-8859-1"
3747 asn1_type_name = "VideotexString"
3750 class IA5String(CommonString):
3752 tag_default = tag_encode(22)
3754 asn1_type_name = "IA5"
3757 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3758 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3759 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3762 class UTCTime(CommonString):
3763 """``UTCTime`` datetime type
3765 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3766 UTCTime UTCTime 2017-09-30T22:07:50
3772 datetime.datetime(2017, 9, 30, 22, 7, 50)
3773 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3774 datetime.datetime(1957, 9, 30, 22, 7, 50)
3778 BER encoding is unsupported.
3781 tag_default = tag_encode(23)
3783 asn1_type_name = "UTCTime"
3793 bounds=None, # dummy argument, workability for OctetString.decode
3796 :param value: set the value. Either datetime type, or
3797 :py:class:`pyderasn.UTCTime` object
3798 :param bytes impl: override default tag with ``IMPLICIT`` one
3799 :param bytes expl: override default tag with ``EXPLICIT`` one
3800 :param default: set default value. Type same as in ``value``
3801 :param bool optional: is object ``OPTIONAL`` in sequence
3803 super(UTCTime, self).__init__(
3811 if value is not None:
3812 self._value = self._value_sanitize(value)
3813 if default is not None:
3814 default = self._value_sanitize(default)
3815 self.default = self.__class__(
3820 if self._value is None:
3821 self._value = default
3823 def _strptime(self, value):
3824 # datetime.strptime's format: %y%m%d%H%M%SZ
3825 if len(value) != LEN_YYMMDDHHMMSSZ:
3826 raise ValueError("invalid UTCTime length")
3827 if value[-1] != "Z":
3828 raise ValueError("non UTC timezone")
3830 2000 + int(value[:2]), # %y
3831 int(value[2:4]), # %m
3832 int(value[4:6]), # %d
3833 int(value[6:8]), # %H
3834 int(value[8:10]), # %M
3835 int(value[10:12]), # %S
3838 def _value_sanitize(self, value):
3839 if isinstance(value, binary_type):
3841 value_decoded = value.decode("ascii")
3842 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3843 raise DecodeError("invalid UTCTime encoding: %r" % err)
3845 self._strptime(value_decoded)
3846 except (TypeError, ValueError) as err:
3847 raise DecodeError("invalid UTCTime format: %r" % err)
3849 if isinstance(value, self.__class__):
3851 if isinstance(value, datetime):
3852 return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
3853 raise InvalidValueType((self.__class__, datetime))
3855 def __eq__(self, their):
3856 if isinstance(their, binary_type):
3857 return self._value == their
3858 if isinstance(their, datetime):
3859 return self.todatetime() == their
3860 if not isinstance(their, self.__class__):
3863 self._value == their._value and
3864 self.tag == their.tag and
3865 self._expl == their._expl
3868 def todatetime(self):
3869 """Convert to datetime
3873 Pay attention that UTCTime can not hold full year, so all years
3874 having < 50 years are treated as 20xx, 19xx otherwise, according
3875 to X.509 recomendation.
3877 value = self._strptime(self._value.decode("ascii"))
3878 year = value.year % 100
3880 year=(2000 + year) if year < 50 else (1900 + year),
3884 minute=value.minute,
3885 second=value.second,
3889 return pp_console_row(next(self.pps()))
3891 def pps(self, decode_path=()):
3894 asn1_type_name=self.asn1_type_name,
3895 obj_name=self.__class__.__name__,
3896 decode_path=decode_path,
3897 value=self.todatetime().isoformat() if self.ready else None,
3898 optional=self.optional,
3899 default=self == self.default,
3900 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3901 expl=None if self._expl is None else tag_decode(self._expl),
3906 expl_offset=self.expl_offset if self.expled else None,
3907 expl_tlen=self.expl_tlen if self.expled else None,
3908 expl_llen=self.expl_llen if self.expled else None,
3909 expl_vlen=self.expl_vlen if self.expled else None,
3910 expl_lenindef=self.expl_lenindef,
3911 ber_encoded=self.ber_encoded,
3914 for pp in self.pps_lenindef(decode_path):
3918 class GeneralizedTime(UTCTime):
3919 """``GeneralizedTime`` datetime type
3921 This type is similar to :py:class:`pyderasn.UTCTime`.
3923 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3924 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3926 '20170930220750.000123Z'
3927 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3928 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3932 BER encoding is unsupported.
3936 Only microsecond fractions are supported.
3937 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
3938 higher precision values.
3941 tag_default = tag_encode(24)
3942 asn1_type_name = "GeneralizedTime"
3944 def _strptime(self, value):
3946 if l == LEN_YYYYMMDDHHMMSSZ:
3947 # datetime.strptime's format: %y%m%d%H%M%SZ
3948 if value[-1] != "Z":
3949 raise ValueError("non UTC timezone")
3951 int(value[:4]), # %Y
3952 int(value[4:6]), # %m
3953 int(value[6:8]), # %d
3954 int(value[8:10]), # %H
3955 int(value[10:12]), # %M
3956 int(value[12:14]), # %S
3958 if l >= LEN_YYYYMMDDHHMMSSDMZ:
3959 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
3960 if value[-1] != "Z":
3961 raise ValueError("non UTC timezone")
3962 if value[14] != ".":
3963 raise ValueError("no fractions separator")
3966 raise ValueError("trailing zero")
3969 raise ValueError("only microsecond fractions are supported")
3970 us = int(us + ("0" * (6 - us_len)))
3972 int(value[:4]), # %Y
3973 int(value[4:6]), # %m
3974 int(value[6:8]), # %d
3975 int(value[8:10]), # %H
3976 int(value[10:12]), # %M
3977 int(value[12:14]), # %S
3981 raise ValueError("invalid GeneralizedTime length")
3983 def _value_sanitize(self, value):
3984 if isinstance(value, binary_type):
3986 value_decoded = value.decode("ascii")
3987 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3988 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
3990 self._strptime(value_decoded)
3991 except (TypeError, ValueError) as err:
3993 "invalid GeneralizedTime format: %r" % err,
3994 klass=self.__class__,
3997 if isinstance(value, self.__class__):
3999 if isinstance(value, datetime):
4000 encoded = value.strftime("%Y%m%d%H%M%S")
4001 if value.microsecond > 0:
4002 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4003 return (encoded + "Z").encode("ascii")
4004 raise InvalidValueType((self.__class__, datetime))
4006 def todatetime(self):
4007 return self._strptime(self._value.decode("ascii"))
4010 class GraphicString(CommonString):
4012 tag_default = tag_encode(25)
4013 encoding = "iso-8859-1"
4014 asn1_type_name = "GraphicString"
4017 class VisibleString(CommonString):
4019 tag_default = tag_encode(26)
4021 asn1_type_name = "VisibleString"
4024 class ISO646String(VisibleString):
4026 asn1_type_name = "ISO646String"
4029 class GeneralString(CommonString):
4031 tag_default = tag_encode(27)
4032 encoding = "iso-8859-1"
4033 asn1_type_name = "GeneralString"
4036 class UniversalString(CommonString):
4038 tag_default = tag_encode(28)
4039 encoding = "utf-32-be"
4040 asn1_type_name = "UniversalString"
4043 class BMPString(CommonString):
4045 tag_default = tag_encode(30)
4046 encoding = "utf-16-be"
4047 asn1_type_name = "BMPString"
4051 """``CHOICE`` special type
4055 class GeneralName(Choice):
4057 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4058 ("dNSName", IA5String(impl=tag_ctxp(2))),
4061 >>> gn = GeneralName()
4063 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4064 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4065 >>> gn["dNSName"] = IA5String("bar.baz")
4066 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4067 >>> gn["rfc822Name"]
4070 [2] IA5String IA5 bar.baz
4073 >>> gn.value == gn["dNSName"]
4076 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4078 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4079 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4081 __slots__ = ("specs",)
4083 asn1_type_name = "CHOICE"
4096 :param value: set the value. Either ``(choice, value)`` tuple, or
4097 :py:class:`pyderasn.Choice` object
4098 :param bytes impl: can not be set, do **not** use it
4099 :param bytes expl: override default tag with ``EXPLICIT`` one
4100 :param default: set default value. Type same as in ``value``
4101 :param bool optional: is object ``OPTIONAL`` in sequence
4103 if impl is not None:
4104 raise ValueError("no implicit tag allowed for CHOICE")
4105 super(Choice, self).__init__(None, expl, default, optional, _decoded)
4107 schema = getattr(self, "schema", ())
4108 if len(schema) == 0:
4109 raise ValueError("schema must be specified")
4111 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4114 if value is not None:
4115 self._value = self._value_sanitize(value)
4116 if default is not None:
4117 default_value = self._value_sanitize(default)
4118 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4119 default_obj.specs = self.specs
4120 default_obj._value = default_value
4121 self.default = default_obj
4123 self._value = default_obj.copy()._value
4125 def _value_sanitize(self, value):
4126 if isinstance(value, tuple) and len(value) == 2:
4128 spec = self.specs.get(choice)
4130 raise ObjUnknown(choice)
4131 if not isinstance(obj, spec.__class__):
4132 raise InvalidValueType((spec,))
4133 return (choice, spec(obj))
4134 if isinstance(value, self.__class__):
4136 raise InvalidValueType((self.__class__, tuple))
4140 return self._value is not None and self._value[1].ready
4144 return self.expl_lenindef or (
4145 (self._value is not None) and
4146 self._value[1].bered
4150 obj = self.__class__(schema=self.specs)
4151 obj._expl = self._expl
4152 obj.default = self.default
4153 obj.optional = self.optional
4154 obj.offset = self.offset
4155 obj.llen = self.llen
4156 obj.vlen = self.vlen
4157 obj.expl_lenindef = self.expl_lenindef
4158 obj.lenindef = self.lenindef
4159 obj.ber_encoded = self.ber_encoded
4161 if value is not None:
4162 obj._value = (value[0], value[1].copy())
4165 def __eq__(self, their):
4166 if isinstance(their, tuple) and len(their) == 2:
4167 return self._value == their
4168 if not isinstance(their, self.__class__):
4171 self.specs == their.specs and
4172 self._value == their._value
4182 return self.__class__(
4185 expl=self._expl if expl is None else expl,
4186 default=self.default if default is None else default,
4187 optional=self.optional if optional is None else optional,
4192 self._assert_ready()
4193 return self._value[0]
4197 self._assert_ready()
4198 return self._value[1]
4200 def __getitem__(self, key):
4201 if key not in self.specs:
4202 raise ObjUnknown(key)
4203 if self._value is None:
4205 choice, value = self._value
4210 def __setitem__(self, key, value):
4211 spec = self.specs.get(key)
4213 raise ObjUnknown(key)
4214 if not isinstance(value, spec.__class__):
4215 raise InvalidValueType((spec.__class__,))
4216 self._value = (key, spec(value))
4224 return self._value[1].decoded if self.ready else False
4227 self._assert_ready()
4228 return self._value[1].encode()
4230 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4231 for choice, spec in iteritems(self.specs):
4232 sub_decode_path = decode_path + (choice,)
4238 decode_path=sub_decode_path,
4241 _ctx_immutable=False,
4248 klass=self.__class__,
4249 decode_path=decode_path,
4252 if tag_only: # pragma: no cover
4254 value, tail = spec.decode(
4258 decode_path=sub_decode_path,
4260 _ctx_immutable=False,
4262 obj = self.__class__(
4265 default=self.default,
4266 optional=self.optional,
4267 _decoded=(offset, 0, value.fulllen),
4269 obj._value = (choice, value)
4273 value = pp_console_row(next(self.pps()))
4275 value = "%s[%r]" % (value, self.value)
4278 def pps(self, decode_path=()):
4281 asn1_type_name=self.asn1_type_name,
4282 obj_name=self.__class__.__name__,
4283 decode_path=decode_path,
4284 value=self.choice if self.ready else None,
4285 optional=self.optional,
4286 default=self == self.default,
4287 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4288 expl=None if self._expl is None else tag_decode(self._expl),
4293 expl_lenindef=self.expl_lenindef,
4297 yield self.value.pps(decode_path=decode_path + (self.choice,))
4298 for pp in self.pps_lenindef(decode_path):
4302 class PrimitiveTypes(Choice):
4303 """Predefined ``CHOICE`` for all generic primitive types
4305 It could be useful for general decoding of some unspecified values:
4307 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4308 OCTET STRING 3 bytes 666f6f
4309 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4313 schema = tuple((klass.__name__, klass()) for klass in (
4338 """``ANY`` special type
4340 >>> Any(Integer(-123))
4342 >>> a = Any(OctetString(b"hello world").encode())
4343 ANY 040b68656c6c6f20776f726c64
4344 >>> hexenc(bytes(a))
4345 b'0x040x0bhello world'
4347 __slots__ = ("defined",)
4348 tag_default = tag_encode(0)
4349 asn1_type_name = "ANY"
4359 :param value: set the value. Either any kind of pyderasn's
4360 **ready** object, or bytes. Pay attention that
4361 **no** validation is performed is raw binary value
4363 :param bytes expl: override default tag with ``EXPLICIT`` one
4364 :param bool optional: is object ``OPTIONAL`` in sequence
4366 super(Any, self).__init__(None, expl, None, optional, _decoded)
4367 self._value = None if value is None else self._value_sanitize(value)
4370 def _value_sanitize(self, value):
4371 if isinstance(value, binary_type):
4373 if isinstance(value, self.__class__):
4375 if isinstance(value, Obj):
4376 return value.encode()
4377 raise InvalidValueType((self.__class__, Obj, binary_type))
4381 return self._value is not None
4385 if self.expl_lenindef or self.lenindef:
4387 if self.defined is None:
4389 return self.defined[1].bered
4392 obj = self.__class__()
4393 obj._value = self._value
4395 obj._expl = self._expl
4396 obj.optional = self.optional
4397 obj.offset = self.offset
4398 obj.llen = self.llen
4399 obj.vlen = self.vlen
4400 obj.expl_lenindef = self.expl_lenindef
4401 obj.lenindef = self.lenindef
4402 obj.ber_encoded = self.ber_encoded
4405 def __eq__(self, their):
4406 if isinstance(their, binary_type):
4407 return self._value == their
4408 if issubclass(their.__class__, Any):
4409 return self._value == their._value
4418 return self.__class__(
4420 expl=self._expl if expl is None else expl,
4421 optional=self.optional if optional is None else optional,
4424 def __bytes__(self):
4425 self._assert_ready()
4433 self._assert_ready()
4436 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4438 t, tlen, lv = tag_strip(tlv)
4439 except DecodeError as err:
4440 raise err.__class__(
4442 klass=self.__class__,
4443 decode_path=decode_path,
4447 l, llen, v = len_decode(lv)
4448 except LenIndefForm as err:
4449 if not ctx.get("bered", False):
4450 raise err.__class__(
4452 klass=self.__class__,
4453 decode_path=decode_path,
4456 llen, vlen, v = 1, 0, lv[1:]
4457 sub_offset = offset + tlen + llen
4459 while v[:EOC_LEN].tobytes() != EOC:
4460 chunk, v = Any().decode(
4463 decode_path=decode_path + (str(chunk_i),),
4466 _ctx_immutable=False,
4468 vlen += chunk.tlvlen
4469 sub_offset += chunk.tlvlen
4471 tlvlen = tlen + llen + vlen + EOC_LEN
4472 obj = self.__class__(
4473 value=tlv[:tlvlen].tobytes(),
4475 optional=self.optional,
4476 _decoded=(offset, 0, tlvlen),
4480 return obj, v[EOC_LEN:]
4481 except DecodeError as err:
4482 raise err.__class__(
4484 klass=self.__class__,
4485 decode_path=decode_path,
4489 raise NotEnoughData(
4490 "encoded length is longer than data",
4491 klass=self.__class__,
4492 decode_path=decode_path,
4495 tlvlen = tlen + llen + l
4496 v, tail = tlv[:tlvlen], v[l:]
4497 obj = self.__class__(
4500 optional=self.optional,
4501 _decoded=(offset, 0, tlvlen),
4507 return pp_console_row(next(self.pps()))
4509 def pps(self, decode_path=()):
4512 asn1_type_name=self.asn1_type_name,
4513 obj_name=self.__class__.__name__,
4514 decode_path=decode_path,
4515 blob=self._value if self.ready else None,
4516 optional=self.optional,
4517 default=self == self.default,
4518 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4519 expl=None if self._expl is None else tag_decode(self._expl),
4524 expl_offset=self.expl_offset if self.expled else None,
4525 expl_tlen=self.expl_tlen if self.expled else None,
4526 expl_llen=self.expl_llen if self.expled else None,
4527 expl_vlen=self.expl_vlen if self.expled else None,
4528 expl_lenindef=self.expl_lenindef,
4529 lenindef=self.lenindef,
4532 defined_by, defined = self.defined or (None, None)
4533 if defined_by is not None:
4535 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4537 for pp in self.pps_lenindef(decode_path):
4541 ########################################################################
4542 # ASN.1 constructed types
4543 ########################################################################
4545 def get_def_by_path(defines_by_path, sub_decode_path):
4546 """Get define by decode path
4548 for path, define in defines_by_path:
4549 if len(path) != len(sub_decode_path):
4551 for p1, p2 in zip(path, sub_decode_path):
4552 if (p1 != any) and (p1 != p2):
4558 def abs_decode_path(decode_path, rel_path):
4559 """Create an absolute decode path from current and relative ones
4561 :param decode_path: current decode path, starting point. Tuple of strings
4562 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4563 If first tuple's element is "/", then treat it as
4564 an absolute path, ignoring ``decode_path`` as
4565 starting point. Also this tuple can contain ".."
4566 elements, stripping the leading element from
4569 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4570 ("foo", "bar", "baz", "whatever")
4571 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4573 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4576 if rel_path[0] == "/":
4578 if rel_path[0] == "..":
4579 return abs_decode_path(decode_path[:-1], rel_path[1:])
4580 return decode_path + rel_path
4583 class Sequence(Obj):
4584 """``SEQUENCE`` structure type
4586 You have to make specification of sequence::
4588 class Extension(Sequence):
4590 ("extnID", ObjectIdentifier()),
4591 ("critical", Boolean(default=False)),
4592 ("extnValue", OctetString()),
4595 Then, you can work with it as with dictionary.
4597 >>> ext = Extension()
4598 >>> Extension().specs
4600 ('extnID', OBJECT IDENTIFIER),
4601 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4602 ('extnValue', OCTET STRING),
4604 >>> ext["extnID"] = "1.2.3"
4605 Traceback (most recent call last):
4606 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4607 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4609 You can determine if sequence is ready to be encoded:
4614 Traceback (most recent call last):
4615 pyderasn.ObjNotReady: object is not ready: extnValue
4616 >>> ext["extnValue"] = OctetString(b"foobar")
4620 Value you want to assign, must have the same **type** as in
4621 corresponding specification, but it can have different tags,
4622 optional/default attributes -- they will be taken from specification
4625 class TBSCertificate(Sequence):
4627 ("version", Version(expl=tag_ctxc(0), default="v1")),
4630 >>> tbs = TBSCertificate()
4631 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4633 Assign ``None`` to remove value from sequence.
4635 You can set values in Sequence during its initialization:
4637 >>> AlgorithmIdentifier((
4638 ("algorithm", ObjectIdentifier("1.2.3")),
4639 ("parameters", Any(Null()))
4641 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4643 You can determine if value exists/set in the sequence and take its value:
4645 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4648 OBJECT IDENTIFIER 1.2.3
4650 But pay attention that if value has default, then it won't be (not
4651 in) in the sequence (because ``DEFAULT`` must not be encoded in
4652 DER), but you can read its value:
4654 >>> "critical" in ext, ext["critical"]
4655 (False, BOOLEAN False)
4656 >>> ext["critical"] = Boolean(True)
4657 >>> "critical" in ext, ext["critical"]
4658 (True, BOOLEAN True)
4660 All defaulted values are always optional.
4662 .. _allow_default_values_ctx:
4664 DER prohibits default value encoding and will raise an error if
4665 default value is unexpectedly met during decode.
4666 If :ref:`bered <bered_ctx>` context option is set, then no error
4667 will be raised, but ``bered`` attribute set. You can disable strict
4668 defaulted values existence validation by setting
4669 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4671 Two sequences are equal if they have equal specification (schema),
4672 implicit/explicit tagging and the same values.
4674 __slots__ = ("specs",)
4675 tag_default = tag_encode(form=TagFormConstructed, num=16)
4676 asn1_type_name = "SEQUENCE"
4688 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4690 schema = getattr(self, "schema", ())
4692 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4695 if value is not None:
4696 if issubclass(value.__class__, Sequence):
4697 self._value = value._value
4698 elif hasattr(value, "__iter__"):
4699 for seq_key, seq_value in value:
4700 self[seq_key] = seq_value
4702 raise InvalidValueType((Sequence,))
4703 if default is not None:
4704 if not issubclass(default.__class__, Sequence):
4705 raise InvalidValueType((Sequence,))
4706 default_value = default._value
4707 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4708 default_obj.specs = self.specs
4709 default_obj._value = default_value
4710 self.default = default_obj
4712 self._value = default_obj.copy()._value
4716 for name, spec in iteritems(self.specs):
4717 value = self._value.get(name)
4729 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4731 return any(value.bered for value in itervalues(self._value))
4734 obj = self.__class__(schema=self.specs)
4736 obj._expl = self._expl
4737 obj.default = self.default
4738 obj.optional = self.optional
4739 obj.offset = self.offset
4740 obj.llen = self.llen
4741 obj.vlen = self.vlen
4742 obj.expl_lenindef = self.expl_lenindef
4743 obj.lenindef = self.lenindef
4744 obj.ber_encoded = self.ber_encoded
4745 obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4748 def __eq__(self, their):
4749 if not isinstance(their, self.__class__):
4752 self.specs == their.specs and
4753 self.tag == their.tag and
4754 self._expl == their._expl and
4755 self._value == their._value
4766 return self.__class__(
4769 impl=self.tag if impl is None else impl,
4770 expl=self._expl if expl is None else expl,
4771 default=self.default if default is None else default,
4772 optional=self.optional if optional is None else optional,
4775 def __contains__(self, key):
4776 return key in self._value
4778 def __setitem__(self, key, value):
4779 spec = self.specs.get(key)
4781 raise ObjUnknown(key)
4783 self._value.pop(key, None)
4785 if not isinstance(value, spec.__class__):
4786 raise InvalidValueType((spec.__class__,))
4787 value = spec(value=value)
4788 if spec.default is not None and value == spec.default:
4789 self._value.pop(key, None)
4791 self._value[key] = value
4793 def __getitem__(self, key):
4794 value = self._value.get(key)
4795 if value is not None:
4797 spec = self.specs.get(key)
4799 raise ObjUnknown(key)
4800 if spec.default is not None:
4804 def _encoded_values(self):
4806 for name, spec in iteritems(self.specs):
4807 value = self._value.get(name)
4811 raise ObjNotReady(name)
4812 raws.append(value.encode())
4816 v = b"".join(self._encoded_values())
4817 return b"".join((self.tag, len_encode(len(v)), v))
4819 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4821 t, tlen, lv = tag_strip(tlv)
4822 except DecodeError as err:
4823 raise err.__class__(
4825 klass=self.__class__,
4826 decode_path=decode_path,
4831 klass=self.__class__,
4832 decode_path=decode_path,
4835 if tag_only: # pragma: no cover
4838 ctx_bered = ctx.get("bered", False)
4840 l, llen, v = len_decode(lv)
4841 except LenIndefForm as err:
4843 raise err.__class__(
4845 klass=self.__class__,
4846 decode_path=decode_path,
4849 l, llen, v = 0, 1, lv[1:]
4851 except DecodeError as err:
4852 raise err.__class__(
4854 klass=self.__class__,
4855 decode_path=decode_path,
4859 raise NotEnoughData(
4860 "encoded length is longer than data",
4861 klass=self.__class__,
4862 decode_path=decode_path,
4866 v, tail = v[:l], v[l:]
4868 sub_offset = offset + tlen + llen
4871 ctx_allow_default_values = ctx.get("allow_default_values", False)
4872 for name, spec in iteritems(self.specs):
4873 if spec.optional and (
4874 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4878 sub_decode_path = decode_path + (name,)
4880 value, v_tail = spec.decode(
4884 decode_path=sub_decode_path,
4886 _ctx_immutable=False,
4893 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4894 if defined is not None:
4895 defined_by, defined_spec = defined
4896 if issubclass(value.__class__, SequenceOf):
4897 for i, _value in enumerate(value):
4898 sub_sub_decode_path = sub_decode_path + (
4900 DecodePathDefBy(defined_by),
4902 defined_value, defined_tail = defined_spec.decode(
4903 memoryview(bytes(_value)),
4905 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4906 if value.expled else (value.tlen + value.llen)
4909 decode_path=sub_sub_decode_path,
4911 _ctx_immutable=False,
4913 if len(defined_tail) > 0:
4916 klass=self.__class__,
4917 decode_path=sub_sub_decode_path,
4920 _value.defined = (defined_by, defined_value)
4922 defined_value, defined_tail = defined_spec.decode(
4923 memoryview(bytes(value)),
4925 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4926 if value.expled else (value.tlen + value.llen)
4929 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4931 _ctx_immutable=False,
4933 if len(defined_tail) > 0:
4936 klass=self.__class__,
4937 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4940 value.defined = (defined_by, defined_value)
4942 value_len = value.fulllen
4944 sub_offset += value_len
4946 if spec.default is not None and value == spec.default:
4947 if ctx_bered or ctx_allow_default_values:
4951 "DEFAULT value met",
4952 klass=self.__class__,
4953 decode_path=sub_decode_path,
4956 values[name] = value
4958 spec_defines = getattr(spec, "defines", ())
4959 if len(spec_defines) == 0:
4960 defines_by_path = ctx.get("defines_by_path", ())
4961 if len(defines_by_path) > 0:
4962 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4963 if spec_defines is not None and len(spec_defines) > 0:
4964 for rel_path, schema in spec_defines:
4965 defined = schema.get(value, None)
4966 if defined is not None:
4967 ctx.setdefault("_defines", []).append((
4968 abs_decode_path(sub_decode_path[:-1], rel_path),
4972 if v[:EOC_LEN].tobytes() != EOC:
4975 klass=self.__class__,
4976 decode_path=decode_path,
4984 klass=self.__class__,
4985 decode_path=decode_path,
4988 obj = self.__class__(
4992 default=self.default,
4993 optional=self.optional,
4994 _decoded=(offset, llen, vlen),
4997 obj.lenindef = lenindef
4998 obj.ber_encoded = ber_encoded
5002 value = pp_console_row(next(self.pps()))
5004 for name in self.specs:
5005 _value = self._value.get(name)
5008 cols.append("%s: %s" % (name, repr(_value)))
5009 return "%s[%s]" % (value, "; ".join(cols))
5011 def pps(self, decode_path=()):
5014 asn1_type_name=self.asn1_type_name,
5015 obj_name=self.__class__.__name__,
5016 decode_path=decode_path,
5017 optional=self.optional,
5018 default=self == self.default,
5019 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5020 expl=None if self._expl is None else tag_decode(self._expl),
5025 expl_offset=self.expl_offset if self.expled else None,
5026 expl_tlen=self.expl_tlen if self.expled else None,
5027 expl_llen=self.expl_llen if self.expled else None,
5028 expl_vlen=self.expl_vlen if self.expled else None,
5029 expl_lenindef=self.expl_lenindef,
5030 lenindef=self.lenindef,
5031 ber_encoded=self.ber_encoded,
5034 for name in self.specs:
5035 value = self._value.get(name)
5038 yield value.pps(decode_path=decode_path + (name,))
5039 for pp in self.pps_lenindef(decode_path):
5043 class Set(Sequence):
5044 """``SET`` structure type
5046 Its usage is identical to :py:class:`pyderasn.Sequence`.
5048 .. _allow_unordered_set_ctx:
5050 DER prohibits unordered values encoding and will raise an error
5051 during decode. If If :ref:`bered <bered_ctx>` context option is set,
5052 then no error will occure. Also you can disable strict values
5053 ordering check by setting ``"allow_unordered_set": True``
5054 :ref:`context <ctx>` option.
5057 tag_default = tag_encode(form=TagFormConstructed, num=17)
5058 asn1_type_name = "SET"
5061 raws = self._encoded_values()
5064 return b"".join((self.tag, len_encode(len(v)), v))
5066 def _specs_items(self):
5067 return iteritems(self.specs)
5069 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5071 t, tlen, lv = tag_strip(tlv)
5072 except DecodeError as err:
5073 raise err.__class__(
5075 klass=self.__class__,
5076 decode_path=decode_path,
5081 klass=self.__class__,
5082 decode_path=decode_path,
5088 ctx_bered = ctx.get("bered", False)
5090 l, llen, v = len_decode(lv)
5091 except LenIndefForm as err:
5093 raise err.__class__(
5095 klass=self.__class__,
5096 decode_path=decode_path,
5099 l, llen, v = 0, 1, lv[1:]
5101 except DecodeError as err:
5102 raise err.__class__(
5104 klass=self.__class__,
5105 decode_path=decode_path,
5109 raise NotEnoughData(
5110 "encoded length is longer than data",
5111 klass=self.__class__,
5115 v, tail = v[:l], v[l:]
5117 sub_offset = offset + tlen + llen
5120 ctx_allow_default_values = ctx.get("allow_default_values", False)
5121 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5122 value_prev = memoryview(v[:0])
5125 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5127 for name, spec in self._specs_items():
5128 sub_decode_path = decode_path + (name,)
5134 decode_path=sub_decode_path,
5137 _ctx_immutable=False,
5144 klass=self.__class__,
5145 decode_path=decode_path,
5148 value, v_tail = spec.decode(
5152 decode_path=sub_decode_path,
5154 _ctx_immutable=False,
5156 value_len = value.fulllen
5157 if value_prev.tobytes() > v[:value_len].tobytes():
5158 if ctx_bered or ctx_allow_unordered_set:
5162 "unordered " + self.asn1_type_name,
5163 klass=self.__class__,
5164 decode_path=sub_decode_path,
5167 if spec.default is None or value != spec.default:
5169 elif ctx_bered or ctx_allow_default_values:
5173 "DEFAULT value met",
5174 klass=self.__class__,
5175 decode_path=sub_decode_path,
5178 values[name] = value
5179 value_prev = v[:value_len]
5180 sub_offset += value_len
5183 obj = self.__class__(
5187 default=self.default,
5188 optional=self.optional,
5189 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5192 if v[:EOC_LEN].tobytes() != EOC:
5195 klass=self.__class__,
5196 decode_path=decode_path,
5204 "not all values are ready",
5205 klass=self.__class__,
5206 decode_path=decode_path,
5209 obj.ber_encoded = ber_encoded
5213 class SequenceOf(Obj):
5214 """``SEQUENCE OF`` sequence type
5216 For that kind of type you must specify the object it will carry on
5217 (bounds are for example here, not required)::
5219 class Ints(SequenceOf):
5224 >>> ints.append(Integer(123))
5225 >>> ints.append(Integer(234))
5227 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5228 >>> [int(i) for i in ints]
5230 >>> ints.append(Integer(345))
5231 Traceback (most recent call last):
5232 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5235 >>> ints[1] = Integer(345)
5237 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5239 Also you can initialize sequence with preinitialized values:
5241 >>> ints = Ints([Integer(123), Integer(234)])
5243 __slots__ = ("spec", "_bound_min", "_bound_max")
5244 tag_default = tag_encode(form=TagFormConstructed, num=16)
5245 asn1_type_name = "SEQUENCE OF"
5258 super(SequenceOf, self).__init__(
5266 schema = getattr(self, "schema", None)
5268 raise ValueError("schema must be specified")
5270 self._bound_min, self._bound_max = getattr(
5274 ) if bounds is None else bounds
5276 if value is not None:
5277 self._value = self._value_sanitize(value)
5278 if default is not None:
5279 default_value = self._value_sanitize(default)
5280 default_obj = self.__class__(
5285 default_obj._value = default_value
5286 self.default = default_obj
5288 self._value = default_obj.copy()._value
5290 def _value_sanitize(self, value):
5291 if issubclass(value.__class__, SequenceOf):
5292 value = value._value
5293 elif hasattr(value, "__iter__"):
5296 raise InvalidValueType((self.__class__, iter))
5297 if not self._bound_min <= len(value) <= self._bound_max:
5298 raise BoundsError(self._bound_min, len(value), self._bound_max)
5300 if not isinstance(v, self.spec.__class__):
5301 raise InvalidValueType((self.spec.__class__,))
5306 return all(v.ready for v in self._value)
5310 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5312 return any(v.bered for v in self._value)
5315 obj = self.__class__(schema=self.spec)
5316 obj._bound_min = self._bound_min
5317 obj._bound_max = self._bound_max
5319 obj._expl = self._expl
5320 obj.default = self.default
5321 obj.optional = self.optional
5322 obj.offset = self.offset
5323 obj.llen = self.llen
5324 obj.vlen = self.vlen
5325 obj.expl_lenindef = self.expl_lenindef
5326 obj.lenindef = self.lenindef
5327 obj.ber_encoded = self.ber_encoded
5328 obj._value = [v.copy() for v in self._value]
5331 def __eq__(self, their):
5332 if isinstance(their, self.__class__):
5334 self.spec == their.spec and
5335 self.tag == their.tag and
5336 self._expl == their._expl and
5337 self._value == their._value
5339 if hasattr(their, "__iter__"):
5340 return self._value == list(their)
5352 return self.__class__(
5356 (self._bound_min, self._bound_max)
5357 if bounds is None else bounds
5359 impl=self.tag if impl is None else impl,
5360 expl=self._expl if expl is None else expl,
5361 default=self.default if default is None else default,
5362 optional=self.optional if optional is None else optional,
5365 def __contains__(self, key):
5366 return key in self._value
5368 def append(self, value):
5369 if not isinstance(value, self.spec.__class__):
5370 raise InvalidValueType((self.spec.__class__,))
5371 if len(self._value) + 1 > self._bound_max:
5374 len(self._value) + 1,
5377 self._value.append(value)
5380 self._assert_ready()
5381 return iter(self._value)
5384 self._assert_ready()
5385 return len(self._value)
5387 def __setitem__(self, key, value):
5388 if not isinstance(value, self.spec.__class__):
5389 raise InvalidValueType((self.spec.__class__,))
5390 self._value[key] = self.spec(value=value)
5392 def __getitem__(self, key):
5393 return self._value[key]
5395 def _encoded_values(self):
5396 return [v.encode() for v in self._value]
5399 v = b"".join(self._encoded_values())
5400 return b"".join((self.tag, len_encode(len(v)), v))
5402 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5404 t, tlen, lv = tag_strip(tlv)
5405 except DecodeError as err:
5406 raise err.__class__(
5408 klass=self.__class__,
5409 decode_path=decode_path,
5414 klass=self.__class__,
5415 decode_path=decode_path,
5421 ctx_bered = ctx.get("bered", False)
5423 l, llen, v = len_decode(lv)
5424 except LenIndefForm as err:
5426 raise err.__class__(
5428 klass=self.__class__,
5429 decode_path=decode_path,
5432 l, llen, v = 0, 1, lv[1:]
5434 except DecodeError as err:
5435 raise err.__class__(
5437 klass=self.__class__,
5438 decode_path=decode_path,
5442 raise NotEnoughData(
5443 "encoded length is longer than data",
5444 klass=self.__class__,
5445 decode_path=decode_path,
5449 v, tail = v[:l], v[l:]
5451 sub_offset = offset + tlen + llen
5453 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5454 value_prev = memoryview(v[:0])
5458 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5460 sub_decode_path = decode_path + (str(len(_value)),)
5461 value, v_tail = spec.decode(
5465 decode_path=sub_decode_path,
5467 _ctx_immutable=False,
5469 value_len = value.fulllen
5471 if value_prev.tobytes() > v[:value_len].tobytes():
5472 if ctx_bered or ctx_allow_unordered_set:
5476 "unordered " + self.asn1_type_name,
5477 klass=self.__class__,
5478 decode_path=sub_decode_path,
5481 value_prev = v[:value_len]
5482 _value.append(value)
5483 sub_offset += value_len
5487 obj = self.__class__(
5490 bounds=(self._bound_min, self._bound_max),
5493 default=self.default,
5494 optional=self.optional,
5495 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5497 except BoundsError as err:
5500 klass=self.__class__,
5501 decode_path=decode_path,
5505 if v[:EOC_LEN].tobytes() != EOC:
5508 klass=self.__class__,
5509 decode_path=decode_path,
5514 obj.ber_encoded = ber_encoded
5519 pp_console_row(next(self.pps())),
5520 ", ".join(repr(v) for v in self._value),
5523 def pps(self, decode_path=()):
5526 asn1_type_name=self.asn1_type_name,
5527 obj_name=self.__class__.__name__,
5528 decode_path=decode_path,
5529 optional=self.optional,
5530 default=self == self.default,
5531 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5532 expl=None if self._expl is None else tag_decode(self._expl),
5537 expl_offset=self.expl_offset if self.expled else None,
5538 expl_tlen=self.expl_tlen if self.expled else None,
5539 expl_llen=self.expl_llen if self.expled else None,
5540 expl_vlen=self.expl_vlen if self.expled else None,
5541 expl_lenindef=self.expl_lenindef,
5542 lenindef=self.lenindef,
5543 ber_encoded=self.ber_encoded,
5546 for i, value in enumerate(self._value):
5547 yield value.pps(decode_path=decode_path + (str(i),))
5548 for pp in self.pps_lenindef(decode_path):
5552 class SetOf(SequenceOf):
5553 """``SET OF`` sequence type
5555 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5558 tag_default = tag_encode(form=TagFormConstructed, num=17)
5559 asn1_type_name = "SET OF"
5562 raws = self._encoded_values()
5565 return b"".join((self.tag, len_encode(len(v)), v))
5567 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5568 return super(SetOf, self)._decode(
5574 ordering_check=True,
5578 def obj_by_path(pypath): # pragma: no cover
5579 """Import object specified as string Python path
5581 Modules must be separated from classes/functions with ``:``.
5583 >>> obj_by_path("foo.bar:Baz")
5584 <class 'foo.bar.Baz'>
5585 >>> obj_by_path("foo.bar:Baz.boo")
5586 <classmethod 'foo.bar.Baz.boo'>
5588 mod, objs = pypath.rsplit(":", 1)
5589 from importlib import import_module
5590 obj = import_module(mod)
5591 for obj_name in objs.split("."):
5592 obj = getattr(obj, obj_name)
5596 def generic_decoder(): # pragma: no cover
5597 # All of this below is a big hack with self references
5598 choice = PrimitiveTypes()
5599 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5600 choice.specs["SetOf"] = SetOf(schema=choice)
5601 for i in six_xrange(31):
5602 choice.specs["SequenceOf%d" % i] = SequenceOf(
5606 choice.specs["Any"] = Any()
5608 # Class name equals to type name, to omit it from output
5609 class SEQUENCEOF(SequenceOf):
5617 with_decode_path=False,
5618 decode_path_only=(),
5620 def _pprint_pps(pps):
5622 if hasattr(pp, "_fields"):
5624 decode_path_only != () and
5625 pp.decode_path[:len(decode_path_only)] != decode_path_only
5628 if pp.asn1_type_name == Choice.asn1_type_name:
5630 pp_kwargs = pp._asdict()
5631 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5632 pp = _pp(**pp_kwargs)
5633 yield pp_console_row(
5638 with_colours=with_colours,
5639 with_decode_path=with_decode_path,
5640 decode_path_len_decrease=len(decode_path_only),
5642 for row in pp_console_blob(
5644 decode_path_len_decrease=len(decode_path_only),
5648 for row in _pprint_pps(pp):
5650 return "\n".join(_pprint_pps(obj.pps()))
5651 return SEQUENCEOF(), pprint_any
5654 def main(): # pragma: no cover
5656 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5657 parser.add_argument(
5661 help="Skip that number of bytes from the beginning",
5663 parser.add_argument(
5665 help="Python paths to dictionary with OIDs, comma separated",
5667 parser.add_argument(
5669 help="Python path to schema definition to use",
5671 parser.add_argument(
5672 "--defines-by-path",
5673 help="Python path to decoder's defines_by_path",
5675 parser.add_argument(
5677 action="store_true",
5678 help="Disallow BER encoding",
5680 parser.add_argument(
5681 "--print-decode-path",
5682 action="store_true",
5683 help="Print decode paths",
5685 parser.add_argument(
5686 "--decode-path-only",
5687 help="Print only specified decode path",
5689 parser.add_argument(
5691 action="store_true",
5692 help="Allow explicit tag out-of-bound",
5694 parser.add_argument(
5696 type=argparse.FileType("rb"),
5697 help="Path to DER file you want to decode",
5699 args = parser.parse_args()
5700 args.DERFile.seek(args.skip)
5701 der = memoryview(args.DERFile.read())
5702 args.DERFile.close()
5704 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
5705 if args.oids else ()
5708 schema = obj_by_path(args.schema)
5709 from functools import partial
5710 pprinter = partial(pprint, big_blobs=True)
5712 schema, pprinter = generic_decoder()
5714 "bered": not args.nobered,
5715 "allow_expl_oob": args.allow_expl_oob,
5717 if args.defines_by_path is not None:
5718 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5719 obj, tail = schema().decode(der, ctx=ctx)
5723 with_colours=True if environ.get("NO_COLOR") is None else False,
5724 with_decode_path=args.print_decode_path,
5726 () if args.decode_path_only is None else
5727 tuple(args.decode_path_only.split(":"))
5731 print("\nTrailing data: %s" % hexenc(tail))
5734 if __name__ == "__main__":