]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Simpler boolean expression
[pyderasn.git] / pyderasn.py
1 #!/usr/bin/env python
2 # coding: utf-8
3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
5 #
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.
9 #
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.
14 #
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
18
19 This library allows you to marshal various structures in ASN.1 DER
20 format, unmarshal them in BER/CER/DER ones.
21
22     >>> i = Integer(123)
23     >>> raw = i.encode()
24     >>> Integer().decode(raw) == i
25     True
26
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`.
59
60 Common for most types
61 ---------------------
62
63 Tags
64 ____
65
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.
72
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
78 (``tag_ctxp``).
79
80 ::
81
82     >>> Integer(impl=tag_ctxp(1))
83     [1] INTEGER
84     >>> Integer(expl=tag_ctxc(2))
85     [2] EXPLICIT INTEGER
86
87 Implicit tag is not explicitly shown.
88
89 Two objects of the same type, but with different implicit/explicit tags
90 are **not** equal.
91
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`
94 function::
95
96     >>> tag_decode(tag_ctxc(123))
97     (128, 32, 123)
98     >>> klass, form, num = tag_decode(tag_ctxc(123))
99     >>> klass == TagClassContext
100     True
101     >>> form == TagFormConstructed
102     True
103
104 To determine if object has explicit tag, use ``expled`` boolean property
105 and ``expl_tag`` property, returning explicit tag's value.
106
107 Default/optional
108 ________________
109
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.
113
114     >>> Integer(optional=True, default=123)
115     INTEGER 123 OPTIONAL DEFAULT
116
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
120 ``version`` field::
121
122     class Version(Integer):
123         schema = (
124             ("v1", 0),
125             ("v2", 1),
126             ("v3", 2),
127         )
128     class TBSCertificate(Sequence):
129         schema = (
130             ("version", Version(expl=tag_ctxc(0), default="v1")),
131         [...]
132
133 When default argument is used and value is not specified, then it equals
134 to default one.
135
136 .. _bounds:
137
138 Size constraints
139 ________________
140
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::
144
145     class X(...):
146         bounds = (MIN, MAX)
147
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
149
150 For simplicity you can also set bounds the following way::
151
152     bounded_x = X(bounds=(MIN, MAX))
153
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
155 raised.
156
157 Common methods
158 ______________
159
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.
163
164 All objects have ``copy()`` method, that returns their copy, that can be
165 safely mutated.
166
167 .. _decoding:
168
169 Decoding
170 --------
171
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.
178
179 When object is decoded, ``decoded`` property is true and you can safely
180 use following properties:
181
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
187
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:
190
191 * ``expled`` -- to know if explicit tag is set
192 * ``expl_offset`` (it is lesser than ``offset``)
193 * ``expl_tlen``,
194 * ``expl_llen``
195 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
196 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
197   ``offset`` otherwise
198 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
199   ``tlvlen`` otherwise
200
201 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
202
203 .. _ctx:
204
205 Context
206 _______
207
208 You can specify so called context keyword argument during ``decode()``
209 invocation. It is dictionary containing various options governing
210 decoding process.
211
212 Currently available context options:
213
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>`
219
220 .. _pprinting:
221
222 Pretty printing
223 ---------------
224
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.
229
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.
233
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
239
240 .. _pprint_example:
241
242 Example certificate::
243
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
252                         . . . . 05:00
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
260     [...]
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
264                         . . . 05:00
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
268      [...]
269
270     Trailing data: 0a
271
272 Let's parse that output, human::
273
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
277
278 ::
279
280        20   [1,1,   9]    . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
281        ^     ^ ^    ^     ^     ^          ^                 ^
282        0     2 3    4     5     6          9                 10
283
284 ::
285
286        33   [0,0, 278]    . . issuer: Name CHOICE rdnSequence
287        ^     ^ ^    ^     ^   ^       ^    ^      ^
288        0     2 3    4     5   6       8    9      10
289
290 ::
291
292        52-2∞ B [1,1,1054]∞  . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
293              ^           ^                                 ^   ^            ^
294             12          13                                14   9            10
295
296 :0:
297  Offset of the object, where its DER/BER encoding begins.
298  Pay attention that it does **not** include explicit tag.
299 :1:
300  If explicit tag exists, then this is its length (tag + encoded length).
301 :2:
302  Length of object's tag. For example CHOICE does not have its own tag,
303  so it is zero.
304 :3:
305  Length of encoded length.
306 :4:
307  Length of encoded value.
308 :5:
309  Visual indentation to show the depth of object in the hierarchy.
310 :6:
311  Object's name inside SEQUENCE/CHOICE.
312 :7:
313  If either IMPLICIT or EXPLICIT tag is set, then it will be shown
314  here. "IMPLICIT" is omitted.
315 :8:
316  Object's class name, if set. Omitted if it is just an ordinary simple
317  value (like with ``algorithm`` in example above).
318 :9:
319  Object's ASN.1 type.
320 :10:
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.
324 :11:
325  Possible other flags like OPTIONAL and DEFAULT, if value equals to the
326  default one, specified in the schema.
327 :12:
328  Shows does object contains any kind of BER encoded data (possibly
329  Sequence holding BER-encoded underlying value).
330 :13:
331  Only applicable to BER encoded data. Indefinite length encoding mark.
332 :14:
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.
337
338
339 .. _definedby:
340
341 DEFINED BY
342 ----------
343
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.
348
349 .. _defines:
350
351 defines kwarg
352 _____________
353
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`)
357 container::
358
359     class ContentInfo(Sequence):
360         schema = (
361             ("contentType", ContentType(defines=((("content",), {
362                 id_digestedData: DigestedData(),
363                 id_signedData: SignedData(),
364             }),))),
365             ("content", Any(expl=tag_ctxc(0))),
366         )
367
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
372 done.
373
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``::
379
380         (
381             (("parameters",), {
382                 id_ecPublicKey: ECParameters(),
383                 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
384             }),
385             (("..", "subjectPublicKey"), {
386                 id_rsaEncryption: RSAPublicKey(),
387                 id_GostR3410_2001: OctetString(),
388             }),
389         ),
390
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
393 itself.
394
395 Following types can be automatically decoded (DEFINED BY):
396
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
402
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)``.
407
408 .. _defines_by_path_ctx:
409
410 defines_by_path context option
411 ______________________________
412
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.
416
417 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
418 value must be sequence of following tuples::
419
420     (decode_path, defines)
421
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>`.
426
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``
430 of ``PKIResponse``::
431
432     content_info, tail = ContentInfo().decode(data, ctx={"defines_by_path": (
433         (
434             ("contentType",),
435             ((("content",), {id_signedData: SignedData()}),),
436         ),
437         (
438             (
439                 "content",
440                 DecodePathDefBy(id_signedData),
441                 "encapContentInfo",
442                 "eContentType",
443             ),
444             ((("eContent",), {
445                 id_cct_PKIData: PKIData(),
446                 id_cct_PKIResponse: PKIResponse(),
447             })),
448         ),
449         (
450             (
451                 "content",
452                 DecodePathDefBy(id_signedData),
453                 "encapContentInfo",
454                 "eContent",
455                 DecodePathDefBy(id_cct_PKIResponse),
456                 "controlSequence",
457                 any,
458                 "attrType",
459             ),
460             ((("attrValues",), {
461                 id_cmc_recipientNonce: RecipientNonce(),
462                 id_cmc_senderNonce: SenderNonce(),
463                 id_cmc_statusInfoV2: CMCStatusInfoV2(),
464                 id_cmc_transactionId: TransactionId(),
465             })),
466         ),
467     )})
468
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.
473
474 .. _bered_ctx:
475
476 BER encoding
477 ------------
478
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.
483
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``
487   can contain it.
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
491   contain it.
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.
500
501 EOC (end-of-contents) token's length is taken in advance in object's
502 value length.
503
504 .. _allow_expl_oob_ctx:
505
506 Allow explicit tag out-of-bound
507 -------------------------------
508
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.
514
515 .. warning::
516
517    This option should be used only for skipping some decode errors, just
518    to see the decoded structure somehow.
519
520 Base Obj
521 --------
522 .. autoclass:: pyderasn.Obj
523    :members:
524
525 Primitive types
526 ---------------
527
528 Boolean
529 _______
530 .. autoclass:: pyderasn.Boolean
531    :members: __init__
532
533 Integer
534 _______
535 .. autoclass:: pyderasn.Integer
536    :members: __init__
537
538 BitString
539 _________
540 .. autoclass:: pyderasn.BitString
541    :members: __init__
542
543 OctetString
544 ___________
545 .. autoclass:: pyderasn.OctetString
546    :members: __init__
547
548 Null
549 ____
550 .. autoclass:: pyderasn.Null
551    :members: __init__
552
553 ObjectIdentifier
554 ________________
555 .. autoclass:: pyderasn.ObjectIdentifier
556    :members: __init__
557
558 Enumerated
559 __________
560 .. autoclass:: pyderasn.Enumerated
561
562 CommonString
563 ____________
564 .. autoclass:: pyderasn.CommonString
565
566 NumericString
567 _____________
568 .. autoclass:: pyderasn.NumericString
569
570 PrintableString
571 _______________
572 .. autoclass:: pyderasn.PrintableString
573
574 UTCTime
575 _______
576 .. autoclass:: pyderasn.UTCTime
577    :members: __init__, todatetime
578
579 GeneralizedTime
580 _______________
581 .. autoclass:: pyderasn.GeneralizedTime
582
583 Special types
584 -------------
585
586 Choice
587 ______
588 .. autoclass:: pyderasn.Choice
589    :members: __init__
590
591 PrimitiveTypes
592 ______________
593 .. autoclass:: PrimitiveTypes
594
595 Any
596 ___
597 .. autoclass:: pyderasn.Any
598    :members: __init__
599
600 Constructed types
601 -----------------
602
603 Sequence
604 ________
605 .. autoclass:: pyderasn.Sequence
606    :members: __init__
607
608 Set
609 ___
610 .. autoclass:: pyderasn.Set
611    :members: __init__
612
613 SequenceOf
614 __________
615 .. autoclass:: pyderasn.SequenceOf
616    :members: __init__
617
618 SetOf
619 _____
620 .. autoclass:: pyderasn.SetOf
621    :members: __init__
622
623 Various
624 -------
625
626 .. autofunction:: pyderasn.abs_decode_path
627 .. autofunction:: pyderasn.colonize_hex
628 .. autofunction:: pyderasn.hexenc
629 .. autofunction:: pyderasn.hexdec
630 .. autofunction:: pyderasn.tag_encode
631 .. autofunction:: pyderasn.tag_decode
632 .. autofunction:: pyderasn.tag_ctxp
633 .. autofunction:: pyderasn.tag_ctxc
634 .. autoclass:: pyderasn.DecodeError
635    :members: __init__
636 .. autoclass:: pyderasn.NotEnoughData
637 .. autoclass:: pyderasn.LenIndefForm
638 .. autoclass:: pyderasn.TagMismatch
639 .. autoclass:: pyderasn.InvalidLength
640 .. autoclass:: pyderasn.InvalidOID
641 .. autoclass:: pyderasn.ObjUnknown
642 .. autoclass:: pyderasn.ObjNotReady
643 .. autoclass:: pyderasn.InvalidValueType
644 .. autoclass:: pyderasn.BoundsError
645 """
646
647 from codecs import getdecoder
648 from codecs import getencoder
649 from collections import namedtuple
650 from collections import OrderedDict
651 from copy import copy
652 from datetime import datetime
653 from math import ceil
654 from os import environ
655 from string import ascii_letters
656 from string import digits
657
658 from six import add_metaclass
659 from six import binary_type
660 from six import byte2int
661 from six import indexbytes
662 from six import int2byte
663 from six import integer_types
664 from six import iterbytes
665 from six import iteritems
666 from six import itervalues
667 from six import PY2
668 from six import string_types
669 from six import text_type
670 from six import unichr as six_unichr
671 from six.moves import xrange as six_xrange
672
673
674 try:
675     from termcolor import colored
676 except ImportError:  # pragma: no cover
677     def colored(what, *args, **kwargs):
678         return what
679
680 __version__ = "5.5"
681
682 __all__ = (
683     "Any",
684     "BitString",
685     "BMPString",
686     "Boolean",
687     "BoundsError",
688     "Choice",
689     "DecodeError",
690     "DecodePathDefBy",
691     "Enumerated",
692     "GeneralizedTime",
693     "GeneralString",
694     "GraphicString",
695     "hexdec",
696     "hexenc",
697     "IA5String",
698     "Integer",
699     "InvalidLength",
700     "InvalidOID",
701     "InvalidValueType",
702     "ISO646String",
703     "LenIndefForm",
704     "NotEnoughData",
705     "Null",
706     "NumericString",
707     "obj_by_path",
708     "ObjectIdentifier",
709     "ObjNotReady",
710     "ObjUnknown",
711     "OctetString",
712     "PrimitiveTypes",
713     "PrintableString",
714     "Sequence",
715     "SequenceOf",
716     "Set",
717     "SetOf",
718     "T61String",
719     "tag_ctxc",
720     "tag_ctxp",
721     "tag_decode",
722     "TagClassApplication",
723     "TagClassContext",
724     "TagClassPrivate",
725     "TagClassUniversal",
726     "TagFormConstructed",
727     "TagFormPrimitive",
728     "TagMismatch",
729     "TeletexString",
730     "UniversalString",
731     "UTCTime",
732     "UTF8String",
733     "VideotexString",
734     "VisibleString",
735 )
736
737 TagClassUniversal = 0
738 TagClassApplication = 1 << 6
739 TagClassContext = 1 << 7
740 TagClassPrivate = 1 << 6 | 1 << 7
741 TagFormPrimitive = 0
742 TagFormConstructed = 1 << 5
743 TagClassReprs = {
744     TagClassContext: "",
745     TagClassApplication: "APPLICATION ",
746     TagClassPrivate: "PRIVATE ",
747     TagClassUniversal: "UNIV ",
748 }
749 EOC = b"\x00\x00"
750 EOC_LEN = len(EOC)
751 LENINDEF = b"\x80"  # length indefinite mark
752 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
753
754
755 ########################################################################
756 # Errors
757 ########################################################################
758
759 class ASN1Error(ValueError):
760     pass
761
762
763 class DecodeError(ASN1Error):
764     def __init__(self, msg="", klass=None, decode_path=(), offset=0):
765         """
766         :param str msg: reason of decode failing
767         :param klass: optional exact DecodeError inherited class (like
768                       :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
769                       :py:exc:`InvalidLength`)
770         :param decode_path: tuple of strings. It contains human
771                             readable names of the fields through which
772                             decoding process has passed
773         :param int offset: binary offset where failure happened
774         """
775         super(DecodeError, self).__init__()
776         self.msg = msg
777         self.klass = klass
778         self.decode_path = decode_path
779         self.offset = offset
780
781     def __str__(self):
782         return " ".join(
783             c for c in (
784                 "" if self.klass is None else self.klass.__name__,
785                 (
786                     ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
787                     if len(self.decode_path) > 0 else ""
788                 ),
789                 ("(at %d)" % self.offset) if self.offset > 0 else "",
790                 self.msg,
791             ) if c != ""
792         )
793
794     def __repr__(self):
795         return "%s(%s)" % (self.__class__.__name__, self)
796
797
798 class NotEnoughData(DecodeError):
799     pass
800
801
802 class LenIndefForm(DecodeError):
803     pass
804
805
806 class TagMismatch(DecodeError):
807     pass
808
809
810 class InvalidLength(DecodeError):
811     pass
812
813
814 class InvalidOID(DecodeError):
815     pass
816
817
818 class ObjUnknown(ASN1Error):
819     def __init__(self, name):
820         super(ObjUnknown, self).__init__()
821         self.name = name
822
823     def __str__(self):
824         return "object is unknown: %s" % self.name
825
826     def __repr__(self):
827         return "%s(%s)" % (self.__class__.__name__, self)
828
829
830 class ObjNotReady(ASN1Error):
831     def __init__(self, name):
832         super(ObjNotReady, self).__init__()
833         self.name = name
834
835     def __str__(self):
836         return "object is not ready: %s" % self.name
837
838     def __repr__(self):
839         return "%s(%s)" % (self.__class__.__name__, self)
840
841
842 class InvalidValueType(ASN1Error):
843     def __init__(self, expected_types):
844         super(InvalidValueType, self).__init__()
845         self.expected_types = expected_types
846
847     def __str__(self):
848         return "invalid value type, expected: %s" % ", ".join(
849             [repr(t) for t in self.expected_types]
850         )
851
852     def __repr__(self):
853         return "%s(%s)" % (self.__class__.__name__, self)
854
855
856 class BoundsError(ASN1Error):
857     def __init__(self, bound_min, value, bound_max):
858         super(BoundsError, self).__init__()
859         self.bound_min = bound_min
860         self.value = value
861         self.bound_max = bound_max
862
863     def __str__(self):
864         return "unsatisfied bounds: %s <= %s <= %s" % (
865             self.bound_min,
866             self.value,
867             self.bound_max,
868         )
869
870     def __repr__(self):
871         return "%s(%s)" % (self.__class__.__name__, self)
872
873
874 ########################################################################
875 # Basic coders
876 ########################################################################
877
878 _hexdecoder = getdecoder("hex")
879 _hexencoder = getencoder("hex")
880
881
882 def hexdec(data):
883     """Binary data to hexadecimal string convert
884     """
885     return _hexdecoder(data)[0]
886
887
888 def hexenc(data):
889     """Hexadecimal string to binary data convert
890     """
891     return _hexencoder(data)[0].decode("ascii")
892
893
894 def int_bytes_len(num, byte_len=8):
895     if num == 0:
896         return 1
897     return int(ceil(float(num.bit_length()) / byte_len))
898
899
900 def zero_ended_encode(num):
901     octets = bytearray(int_bytes_len(num, 7))
902     i = len(octets) - 1
903     octets[i] = num & 0x7F
904     num >>= 7
905     i -= 1
906     while num > 0:
907         octets[i] = 0x80 | (num & 0x7F)
908         num >>= 7
909         i -= 1
910     return bytes(octets)
911
912
913 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
914     """Encode tag to binary form
915
916     :param int num: tag's number
917     :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
918                       :py:data:`pyderasn.TagClassContext`,
919                       :py:data:`pyderasn.TagClassApplication`,
920                       :py:data:`pyderasn.TagClassPrivate`)
921     :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
922                      :py:data:`pyderasn.TagFormConstructed`)
923     """
924     if num < 31:
925         # [XX|X|.....]
926         return int2byte(klass | form | num)
927     # [XX|X|11111][1.......][1.......] ... [0.......]
928     return int2byte(klass | form | 31) + zero_ended_encode(num)
929
930
931 def tag_decode(tag):
932     """Decode tag from binary form
933
934     .. warning::
935
936        No validation is performed, assuming that it has already passed.
937
938     It returns tuple with three integers, as
939     :py:func:`pyderasn.tag_encode` accepts.
940     """
941     first_octet = byte2int(tag)
942     klass = first_octet & 0xC0
943     form = first_octet & 0x20
944     if first_octet & 0x1F < 0x1F:
945         return (klass, form, first_octet & 0x1F)
946     num = 0
947     for octet in iterbytes(tag[1:]):
948         num <<= 7
949         num |= octet & 0x7F
950     return (klass, form, num)
951
952
953 def tag_ctxp(num):
954     """Create CONTEXT PRIMITIVE tag
955     """
956     return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
957
958
959 def tag_ctxc(num):
960     """Create CONTEXT CONSTRUCTED tag
961     """
962     return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
963
964
965 def tag_strip(data):
966     """Take off tag from the data
967
968     :returns: (encoded tag, tag length, remaining data)
969     """
970     if len(data) == 0:
971         raise NotEnoughData("no data at all")
972     if byte2int(data) & 0x1F < 31:
973         return data[:1], 1, data[1:]
974     i = 0
975     while True:
976         i += 1
977         if i == len(data):
978             raise DecodeError("unfinished tag")
979         if indexbytes(data, i) & 0x80 == 0:
980             break
981     i += 1
982     return data[:i], i, data[i:]
983
984
985 def len_encode(l):
986     if l < 0x80:
987         return int2byte(l)
988     octets = bytearray(int_bytes_len(l) + 1)
989     octets[0] = 0x80 | (len(octets) - 1)
990     for i in six_xrange(len(octets) - 1, 0, -1):
991         octets[i] = l & 0xFF
992         l >>= 8
993     return bytes(octets)
994
995
996 def len_decode(data):
997     """Decode length
998
999     :returns: (decoded length, length's length, remaining data)
1000     :raises LenIndefForm: if indefinite form encoding is met
1001     """
1002     if len(data) == 0:
1003         raise NotEnoughData("no data at all")
1004     first_octet = byte2int(data)
1005     if first_octet & 0x80 == 0:
1006         return first_octet, 1, data[1:]
1007     octets_num = first_octet & 0x7F
1008     if octets_num + 1 > len(data):
1009         raise NotEnoughData("encoded length is longer than data")
1010     if octets_num == 0:
1011         raise LenIndefForm()
1012     if byte2int(data[1:]) == 0:
1013         raise DecodeError("leading zeros")
1014     l = 0
1015     for v in iterbytes(data[1:1 + octets_num]):
1016         l = (l << 8) | v
1017     if l <= 127:
1018         raise DecodeError("long form instead of short one")
1019     return l, 1 + octets_num, data[1 + octets_num:]
1020
1021
1022 ########################################################################
1023 # Base class
1024 ########################################################################
1025
1026 class AutoAddSlots(type):
1027     def __new__(cls, name, bases, _dict):
1028         _dict["__slots__"] = _dict.get("__slots__", ())
1029         return type.__new__(cls, name, bases, _dict)
1030
1031
1032 @add_metaclass(AutoAddSlots)
1033 class Obj(object):
1034     """Common ASN.1 object class
1035
1036     All ASN.1 types are inherited from it. It has metaclass that
1037     automatically adds ``__slots__`` to all inherited classes.
1038     """
1039     __slots__ = (
1040         "tag",
1041         "_value",
1042         "_expl",
1043         "default",
1044         "optional",
1045         "offset",
1046         "llen",
1047         "vlen",
1048         "expl_lenindef",
1049         "lenindef",
1050         "ber_encoded",
1051     )
1052
1053     def __init__(
1054             self,
1055             impl=None,
1056             expl=None,
1057             default=None,
1058             optional=False,
1059             _decoded=(0, 0, 0),
1060     ):
1061         self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1062         self._expl = getattr(self, "expl", None) if expl is None else expl
1063         if self.tag != self.tag_default and self._expl is not None:
1064             raise ValueError("implicit and explicit tags can not be set simultaneously")
1065         if default is not None:
1066             optional = True
1067         self.optional = optional
1068         self.offset, self.llen, self.vlen = _decoded
1069         self.default = None
1070         self.expl_lenindef = False
1071         self.lenindef = False
1072         self.ber_encoded = False
1073
1074     @property
1075     def ready(self):  # pragma: no cover
1076         """Is object ready to be encoded?
1077         """
1078         raise NotImplementedError()
1079
1080     def _assert_ready(self):
1081         if not self.ready:
1082             raise ObjNotReady(self.__class__.__name__)
1083
1084     @property
1085     def bered(self):
1086         """Is either object or any elements inside is BER encoded?
1087         """
1088         return self.expl_lenindef or self.lenindef or self.ber_encoded
1089
1090     @property
1091     def decoded(self):
1092         """Is object decoded?
1093         """
1094         return (self.llen + self.vlen) > 0
1095
1096     def copy(self):  # pragma: no cover
1097         """Make a copy of object, safe to be mutated
1098         """
1099         raise NotImplementedError()
1100
1101     @property
1102     def tlen(self):
1103         """See :ref:`decoding`
1104         """
1105         return len(self.tag)
1106
1107     @property
1108     def tlvlen(self):
1109         """See :ref:`decoding`
1110         """
1111         return self.tlen + self.llen + self.vlen
1112
1113     def __str__(self):  # pragma: no cover
1114         return self.__bytes__() if PY2 else self.__unicode__()
1115
1116     def __ne__(self, their):
1117         return not(self == their)
1118
1119     def __gt__(self, their):  # pragma: no cover
1120         return not(self < their)
1121
1122     def __le__(self, their):  # pragma: no cover
1123         return (self == their) or (self < their)
1124
1125     def __ge__(self, their):  # pragma: no cover
1126         return (self == their) or (self > their)
1127
1128     def _encode(self):  # pragma: no cover
1129         raise NotImplementedError()
1130
1131     def _decode(self, tlv, offset, decode_path, ctx, tag_only):  # pragma: no cover
1132         raise NotImplementedError()
1133
1134     def encode(self):
1135         """Encode the structure
1136
1137         :returns: DER representation
1138         """
1139         raw = self._encode()
1140         if self._expl is None:
1141             return raw
1142         return b"".join((self._expl, len_encode(len(raw)), raw))
1143
1144     def decode(
1145             self,
1146             data,
1147             offset=0,
1148             leavemm=False,
1149             decode_path=(),
1150             ctx=None,
1151             tag_only=False,
1152             _ctx_immutable=True,
1153     ):
1154         """Decode the data
1155
1156         :param data: either binary or memoryview
1157         :param int offset: initial data's offset
1158         :param bool leavemm: do we need to leave memoryview of remaining
1159                     data as is, or convert it to bytes otherwise
1160         :param ctx: optional :ref:`context <ctx>` governing decoding process
1161         :param tag_only: decode only the tag, without length and contents
1162                          (used only in Choice and Set structures, trying to
1163                          determine if tag satisfies the scheme)
1164         :param _ctx_immutable: do we need to copy ``ctx`` before using it
1165         :returns: (Obj, remaining data)
1166
1167         .. seealso:: :ref:`decoding`
1168         """
1169         if ctx is None:
1170             ctx = {}
1171         elif _ctx_immutable:
1172             ctx = copy(ctx)
1173         tlv = memoryview(data)
1174         if self._expl is None:
1175             result = self._decode(
1176                 tlv,
1177                 offset,
1178                 decode_path=decode_path,
1179                 ctx=ctx,
1180                 tag_only=tag_only,
1181             )
1182             if tag_only:
1183                 return None
1184             obj, tail = result
1185         else:
1186             try:
1187                 t, tlen, lv = tag_strip(tlv)
1188             except DecodeError as err:
1189                 raise err.__class__(
1190                     msg=err.msg,
1191                     klass=self.__class__,
1192                     decode_path=decode_path,
1193                     offset=offset,
1194                 )
1195             if t != self._expl:
1196                 raise TagMismatch(
1197                     klass=self.__class__,
1198                     decode_path=decode_path,
1199                     offset=offset,
1200                 )
1201             try:
1202                 l, llen, v = len_decode(lv)
1203             except LenIndefForm as err:
1204                 if not ctx.get("bered", False):
1205                     raise err.__class__(
1206                         msg=err.msg,
1207                         klass=self.__class__,
1208                         decode_path=decode_path,
1209                         offset=offset,
1210                     )
1211                 llen, v = 1, lv[1:]
1212                 offset += tlen + llen
1213                 result = self._decode(
1214                     v,
1215                     offset=offset,
1216                     decode_path=decode_path,
1217                     ctx=ctx,
1218                     tag_only=tag_only,
1219                 )
1220                 if tag_only:  # pragma: no cover
1221                     return None
1222                 obj, tail = result
1223                 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1224                 if eoc_expected.tobytes() != EOC:
1225                     raise DecodeError(
1226                         "no EOC",
1227                         klass=self.__class__,
1228                         decode_path=decode_path,
1229                         offset=offset,
1230                     )
1231                 obj.vlen += EOC_LEN
1232                 obj.expl_lenindef = True
1233             except DecodeError as err:
1234                 raise err.__class__(
1235                     msg=err.msg,
1236                     klass=self.__class__,
1237                     decode_path=decode_path,
1238                     offset=offset,
1239                 )
1240             else:
1241                 if l > len(v):
1242                     raise NotEnoughData(
1243                         "encoded length is longer than data",
1244                         klass=self.__class__,
1245                         decode_path=decode_path,
1246                         offset=offset,
1247                     )
1248                 result = self._decode(
1249                     v,
1250                     offset=offset + tlen + llen,
1251                     decode_path=decode_path,
1252                     ctx=ctx,
1253                     tag_only=tag_only,
1254                 )
1255                 if tag_only:  # pragma: no cover
1256                     return None
1257                 obj, tail = result
1258                 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1259                     raise DecodeError(
1260                         "explicit tag out-of-bound, longer than data",
1261                         klass=self.__class__,
1262                         decode_path=decode_path,
1263                         offset=offset,
1264                     )
1265         return obj, (tail if leavemm else tail.tobytes())
1266
1267     @property
1268     def expled(self):
1269         """See :ref:`decoding`
1270         """
1271         return self._expl is not None
1272
1273     @property
1274     def expl_tag(self):
1275         """See :ref:`decoding`
1276         """
1277         return self._expl
1278
1279     @property
1280     def expl_tlen(self):
1281         """See :ref:`decoding`
1282         """
1283         return len(self._expl)
1284
1285     @property
1286     def expl_llen(self):
1287         """See :ref:`decoding`
1288         """
1289         if self.expl_lenindef:
1290             return 1
1291         return len(len_encode(self.tlvlen))
1292
1293     @property
1294     def expl_offset(self):
1295         """See :ref:`decoding`
1296         """
1297         return self.offset - self.expl_tlen - self.expl_llen
1298
1299     @property
1300     def expl_vlen(self):
1301         """See :ref:`decoding`
1302         """
1303         return self.tlvlen
1304
1305     @property
1306     def expl_tlvlen(self):
1307         """See :ref:`decoding`
1308         """
1309         return self.expl_tlen + self.expl_llen + self.expl_vlen
1310
1311     @property
1312     def fulloffset(self):
1313         """See :ref:`decoding`
1314         """
1315         return self.expl_offset if self.expled else self.offset
1316
1317     @property
1318     def fulllen(self):
1319         """See :ref:`decoding`
1320         """
1321         return self.expl_tlvlen if self.expled else self.tlvlen
1322
1323     def pps_lenindef(self, decode_path):
1324         if self.lenindef and not (
1325             getattr(self, "defined", None) is not None and
1326             self.defined[1].lenindef
1327         ):
1328             yield _pp(
1329                 asn1_type_name="EOC",
1330                 obj_name="",
1331                 decode_path=decode_path,
1332                 offset=(
1333                     self.offset + self.tlvlen -
1334                     (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1335                 ),
1336                 tlen=1,
1337                 llen=1,
1338                 vlen=0,
1339                 ber_encoded=True,
1340                 bered=True,
1341             )
1342         if self.expl_lenindef:
1343             yield _pp(
1344                 asn1_type_name="EOC",
1345                 obj_name="EXPLICIT",
1346                 decode_path=decode_path,
1347                 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1348                 tlen=1,
1349                 llen=1,
1350                 vlen=0,
1351                 ber_encoded=True,
1352                 bered=True,
1353             )
1354
1355
1356 class DecodePathDefBy(object):
1357     """DEFINED BY representation inside decode path
1358     """
1359     __slots__ = ("defined_by",)
1360
1361     def __init__(self, defined_by):
1362         self.defined_by = defined_by
1363
1364     def __ne__(self, their):
1365         return not(self == their)
1366
1367     def __eq__(self, their):
1368         if not isinstance(their, self.__class__):
1369             return False
1370         return self.defined_by == their.defined_by
1371
1372     def __str__(self):
1373         return "DEFINED BY " + str(self.defined_by)
1374
1375     def __repr__(self):
1376         return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1377
1378
1379 ########################################################################
1380 # Pretty printing
1381 ########################################################################
1382
1383 PP = namedtuple("PP", (
1384     "obj",
1385     "asn1_type_name",
1386     "obj_name",
1387     "decode_path",
1388     "value",
1389     "blob",
1390     "optional",
1391     "default",
1392     "impl",
1393     "expl",
1394     "offset",
1395     "tlen",
1396     "llen",
1397     "vlen",
1398     "expl_offset",
1399     "expl_tlen",
1400     "expl_llen",
1401     "expl_vlen",
1402     "expl_lenindef",
1403     "lenindef",
1404     "ber_encoded",
1405     "bered",
1406 ))
1407
1408
1409 def _pp(
1410         obj=None,
1411         asn1_type_name="unknown",
1412         obj_name="unknown",
1413         decode_path=(),
1414         value=None,
1415         blob=None,
1416         optional=False,
1417         default=False,
1418         impl=None,
1419         expl=None,
1420         offset=0,
1421         tlen=0,
1422         llen=0,
1423         vlen=0,
1424         expl_offset=None,
1425         expl_tlen=None,
1426         expl_llen=None,
1427         expl_vlen=None,
1428         expl_lenindef=False,
1429         lenindef=False,
1430         ber_encoded=False,
1431         bered=False,
1432 ):
1433     return PP(
1434         obj,
1435         asn1_type_name,
1436         obj_name,
1437         decode_path,
1438         value,
1439         blob,
1440         optional,
1441         default,
1442         impl,
1443         expl,
1444         offset,
1445         tlen,
1446         llen,
1447         vlen,
1448         expl_offset,
1449         expl_tlen,
1450         expl_llen,
1451         expl_vlen,
1452         expl_lenindef,
1453         lenindef,
1454         ber_encoded,
1455         bered,
1456     )
1457
1458
1459 def _colourize(what, colour, with_colours, attrs=("bold",)):
1460     return colored(what, colour, attrs=attrs) if with_colours else what
1461
1462
1463 def colonize_hex(hexed):
1464     """Separate hexadecimal string with colons
1465     """
1466     return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1467
1468
1469 def pp_console_row(
1470         pp,
1471         oid_maps=(),
1472         with_offsets=False,
1473         with_blob=True,
1474         with_colours=False,
1475         with_decode_path=False,
1476         decode_path_len_decrease=0,
1477 ):
1478     cols = []
1479     if with_offsets:
1480         col = "%5d%s%s" % (
1481             pp.offset,
1482             (
1483                 "  " if pp.expl_offset is None else
1484                 ("-%d" % (pp.offset - pp.expl_offset))
1485             ),
1486             LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1487         )
1488         col = _colourize(col, "red", with_colours, ())
1489         col += _colourize("B", "red", with_colours) if pp.bered else " "
1490         cols.append(col)
1491         col = "[%d,%d,%4d]%s" % (
1492             pp.tlen,
1493             pp.llen,
1494             pp.vlen,
1495             LENINDEF_PP_CHAR if pp.lenindef else " "
1496         )
1497         col = _colourize(col, "green", with_colours, ())
1498         cols.append(col)
1499     decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1500     if decode_path_len > 0:
1501         cols.append(" ." * decode_path_len)
1502         ent = pp.decode_path[-1]
1503         if isinstance(ent, DecodePathDefBy):
1504             cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1505             value = str(ent.defined_by)
1506             oid_name = None
1507             if (
1508                     len(oid_maps) > 0 and
1509                     ent.defined_by.asn1_type_name ==
1510                     ObjectIdentifier.asn1_type_name
1511             ):
1512                 for oid_map in oid_maps:
1513                     oid_name = oid_map.get(value)
1514                     if oid_name is not None:
1515                         cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1516                         break
1517             if oid_name is None:
1518                 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1519         else:
1520             cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1521     if pp.expl is not None:
1522         klass, _, num = pp.expl
1523         col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1524         cols.append(_colourize(col, "blue", with_colours))
1525     if pp.impl is not None:
1526         klass, _, num = pp.impl
1527         col = "[%s%d]" % (TagClassReprs[klass], num)
1528         cols.append(_colourize(col, "blue", with_colours))
1529     if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1530         cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1531     if pp.ber_encoded:
1532         cols.append(_colourize("BER", "red", with_colours))
1533     cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1534     if pp.value is not None:
1535         value = pp.value
1536         cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1537         if (
1538                 len(oid_maps) > 0 and
1539                 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1540         ):
1541             for oid_map in oid_maps:
1542                 oid_name = oid_map.get(value)
1543                 if oid_name is not None:
1544                     cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1545                     break
1546         if pp.asn1_type_name == Integer.asn1_type_name:
1547             hex_repr = hex(int(pp.obj._value))[2:].upper()
1548             if len(hex_repr) % 2 != 0:
1549                 hex_repr = "0" + hex_repr
1550             cols.append(_colourize(
1551                 "(%s)" % colonize_hex(hex_repr),
1552                 "green",
1553                 with_colours,
1554             ))
1555     if with_blob:
1556         if isinstance(pp.blob, binary_type):
1557             cols.append(hexenc(pp.blob))
1558         elif isinstance(pp.blob, tuple):
1559             cols.append(", ".join(pp.blob))
1560     if pp.optional:
1561         cols.append(_colourize("OPTIONAL", "red", with_colours))
1562     if pp.default:
1563         cols.append(_colourize("DEFAULT", "red", with_colours))
1564     if with_decode_path:
1565         cols.append(_colourize(
1566             "[%s]" % ":".join(str(p) for p in pp.decode_path),
1567             "grey",
1568             with_colours,
1569         ))
1570     return " ".join(cols)
1571
1572
1573 def pp_console_blob(pp, decode_path_len_decrease=0):
1574     cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1575     decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1576     if decode_path_len > 0:
1577         cols.append(" ." * (decode_path_len + 1))
1578     if isinstance(pp.blob, binary_type):
1579         blob = hexenc(pp.blob).upper()
1580         for i in six_xrange(0, len(blob), 32):
1581             chunk = blob[i:i + 32]
1582             yield " ".join(cols + [colonize_hex(chunk)])
1583     elif isinstance(pp.blob, tuple):
1584         yield " ".join(cols + [", ".join(pp.blob)])
1585
1586
1587 def pprint(
1588         obj,
1589         oid_maps=(),
1590         big_blobs=False,
1591         with_colours=False,
1592         with_decode_path=False,
1593         decode_path_only=(),
1594 ):
1595     """Pretty print object
1596
1597     :param Obj obj: object you want to pretty print
1598     :param oid_maps: list of ``OID <-> humand readable string`` dictionary.
1599                      When OID from it is met, then its humand readable form
1600                      is printed
1601     :param big_blobs: if large binary objects are met (like OctetString
1602                       values), do we need to print them too, on separate
1603                       lines
1604     :param with_colours: colourize output, if ``termcolor`` library
1605                          is available
1606     :param with_decode_path: print decode path
1607     :param decode_path_only: print only that specified decode path
1608     """
1609     def _pprint_pps(pps):
1610         for pp in pps:
1611             if hasattr(pp, "_fields"):
1612                 if (
1613                     decode_path_only != () and
1614                     tuple(
1615                         str(p) for p in pp.decode_path[:len(decode_path_only)]
1616                     ) != decode_path_only
1617                 ):
1618                     continue
1619                 if big_blobs:
1620                     yield pp_console_row(
1621                         pp,
1622                         oid_maps=oid_maps,
1623                         with_offsets=True,
1624                         with_blob=False,
1625                         with_colours=with_colours,
1626                         with_decode_path=with_decode_path,
1627                         decode_path_len_decrease=len(decode_path_only),
1628                     )
1629                     for row in pp_console_blob(
1630                         pp,
1631                         decode_path_len_decrease=len(decode_path_only),
1632                     ):
1633                         yield row
1634                 else:
1635                     yield pp_console_row(
1636                         pp,
1637                         oid_maps=oid_maps,
1638                         with_offsets=True,
1639                         with_blob=True,
1640                         with_colours=with_colours,
1641                         with_decode_path=with_decode_path,
1642                         decode_path_len_decrease=len(decode_path_only),
1643                     )
1644             else:
1645                 for row in _pprint_pps(pp):
1646                     yield row
1647     return "\n".join(_pprint_pps(obj.pps()))
1648
1649
1650 ########################################################################
1651 # ASN.1 primitive types
1652 ########################################################################
1653
1654 class Boolean(Obj):
1655     """``BOOLEAN`` boolean type
1656
1657     >>> b = Boolean(True)
1658     BOOLEAN True
1659     >>> b == Boolean(True)
1660     True
1661     >>> bool(b)
1662     True
1663     """
1664     __slots__ = ()
1665     tag_default = tag_encode(1)
1666     asn1_type_name = "BOOLEAN"
1667
1668     def __init__(
1669             self,
1670             value=None,
1671             impl=None,
1672             expl=None,
1673             default=None,
1674             optional=False,
1675             _decoded=(0, 0, 0),
1676     ):
1677         """
1678         :param value: set the value. Either boolean type, or
1679                       :py:class:`pyderasn.Boolean` object
1680         :param bytes impl: override default tag with ``IMPLICIT`` one
1681         :param bytes expl: override default tag with ``EXPLICIT`` one
1682         :param default: set default value. Type same as in ``value``
1683         :param bool optional: is object ``OPTIONAL`` in sequence
1684         """
1685         super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1686         self._value = None if value is None else self._value_sanitize(value)
1687         if default is not None:
1688             default = self._value_sanitize(default)
1689             self.default = self.__class__(
1690                 value=default,
1691                 impl=self.tag,
1692                 expl=self._expl,
1693             )
1694             if value is None:
1695                 self._value = default
1696
1697     def _value_sanitize(self, value):
1698         if isinstance(value, bool):
1699             return value
1700         if issubclass(value.__class__, Boolean):
1701             return value._value
1702         raise InvalidValueType((self.__class__, bool))
1703
1704     @property
1705     def ready(self):
1706         return self._value is not None
1707
1708     def copy(self):
1709         obj = self.__class__()
1710         obj._value = self._value
1711         obj.tag = self.tag
1712         obj._expl = self._expl
1713         obj.default = self.default
1714         obj.optional = self.optional
1715         obj.offset = self.offset
1716         obj.llen = self.llen
1717         obj.vlen = self.vlen
1718         obj.expl_lenindef = self.expl_lenindef
1719         obj.lenindef = self.lenindef
1720         obj.ber_encoded = self.ber_encoded
1721         return obj
1722
1723     def __nonzero__(self):
1724         self._assert_ready()
1725         return self._value
1726
1727     def __bool__(self):
1728         self._assert_ready()
1729         return self._value
1730
1731     def __eq__(self, their):
1732         if isinstance(their, bool):
1733             return self._value == their
1734         if not issubclass(their.__class__, Boolean):
1735             return False
1736         return (
1737             self._value == their._value and
1738             self.tag == their.tag and
1739             self._expl == their._expl
1740         )
1741
1742     def __call__(
1743             self,
1744             value=None,
1745             impl=None,
1746             expl=None,
1747             default=None,
1748             optional=None,
1749     ):
1750         return self.__class__(
1751             value=value,
1752             impl=self.tag if impl is None else impl,
1753             expl=self._expl if expl is None else expl,
1754             default=self.default if default is None else default,
1755             optional=self.optional if optional is None else optional,
1756         )
1757
1758     def _encode(self):
1759         self._assert_ready()
1760         return b"".join((
1761             self.tag,
1762             len_encode(1),
1763             (b"\xFF" if self._value else b"\x00"),
1764         ))
1765
1766     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1767         try:
1768             t, _, lv = tag_strip(tlv)
1769         except DecodeError as err:
1770             raise err.__class__(
1771                 msg=err.msg,
1772                 klass=self.__class__,
1773                 decode_path=decode_path,
1774                 offset=offset,
1775             )
1776         if t != self.tag:
1777             raise TagMismatch(
1778                 klass=self.__class__,
1779                 decode_path=decode_path,
1780                 offset=offset,
1781             )
1782         if tag_only:
1783             return None
1784         try:
1785             l, _, v = len_decode(lv)
1786         except DecodeError as err:
1787             raise err.__class__(
1788                 msg=err.msg,
1789                 klass=self.__class__,
1790                 decode_path=decode_path,
1791                 offset=offset,
1792             )
1793         if l != 1:
1794             raise InvalidLength(
1795                 "Boolean's length must be equal to 1",
1796                 klass=self.__class__,
1797                 decode_path=decode_path,
1798                 offset=offset,
1799             )
1800         if l > len(v):
1801             raise NotEnoughData(
1802                 "encoded length is longer than data",
1803                 klass=self.__class__,
1804                 decode_path=decode_path,
1805                 offset=offset,
1806             )
1807         first_octet = byte2int(v)
1808         ber_encoded = False
1809         if first_octet == 0:
1810             value = False
1811         elif first_octet == 0xFF:
1812             value = True
1813         elif ctx.get("bered", False):
1814             value = True
1815             ber_encoded = True
1816         else:
1817             raise DecodeError(
1818                 "unacceptable Boolean value",
1819                 klass=self.__class__,
1820                 decode_path=decode_path,
1821                 offset=offset,
1822             )
1823         obj = self.__class__(
1824             value=value,
1825             impl=self.tag,
1826             expl=self._expl,
1827             default=self.default,
1828             optional=self.optional,
1829             _decoded=(offset, 1, 1),
1830         )
1831         obj.ber_encoded = ber_encoded
1832         return obj, v[1:]
1833
1834     def __repr__(self):
1835         return pp_console_row(next(self.pps()))
1836
1837     def pps(self, decode_path=()):
1838         yield _pp(
1839             obj=self,
1840             asn1_type_name=self.asn1_type_name,
1841             obj_name=self.__class__.__name__,
1842             decode_path=decode_path,
1843             value=str(self._value) if self.ready else None,
1844             optional=self.optional,
1845             default=self == self.default,
1846             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1847             expl=None if self._expl is None else tag_decode(self._expl),
1848             offset=self.offset,
1849             tlen=self.tlen,
1850             llen=self.llen,
1851             vlen=self.vlen,
1852             expl_offset=self.expl_offset if self.expled else None,
1853             expl_tlen=self.expl_tlen if self.expled else None,
1854             expl_llen=self.expl_llen if self.expled else None,
1855             expl_vlen=self.expl_vlen if self.expled else None,
1856             expl_lenindef=self.expl_lenindef,
1857             ber_encoded=self.ber_encoded,
1858             bered=self.bered,
1859         )
1860         for pp in self.pps_lenindef(decode_path):
1861             yield pp
1862
1863
1864 class Integer(Obj):
1865     """``INTEGER`` integer type
1866
1867     >>> b = Integer(-123)
1868     INTEGER -123
1869     >>> b == Integer(-123)
1870     True
1871     >>> int(b)
1872     -123
1873
1874     >>> Integer(2, bounds=(1, 3))
1875     INTEGER 2
1876     >>> Integer(5, bounds=(1, 3))
1877     Traceback (most recent call last):
1878     pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1879
1880     ::
1881
1882         class Version(Integer):
1883             schema = (
1884                 ("v1", 0),
1885                 ("v2", 1),
1886                 ("v3", 2),
1887             )
1888
1889     >>> v = Version("v1")
1890     Version INTEGER v1
1891     >>> int(v)
1892     0
1893     >>> v.named
1894     'v1'
1895     >>> v.specs
1896     {'v3': 2, 'v1': 0, 'v2': 1}
1897     """
1898     __slots__ = ("specs", "_bound_min", "_bound_max")
1899     tag_default = tag_encode(2)
1900     asn1_type_name = "INTEGER"
1901
1902     def __init__(
1903             self,
1904             value=None,
1905             bounds=None,
1906             impl=None,
1907             expl=None,
1908             default=None,
1909             optional=False,
1910             _specs=None,
1911             _decoded=(0, 0, 0),
1912     ):
1913         """
1914         :param value: set the value. Either integer type, named value
1915                       (if ``schema`` is specified in the class), or
1916                       :py:class:`pyderasn.Integer` object
1917         :param bounds: set ``(MIN, MAX)`` value constraint.
1918                        (-inf, +inf) by default
1919         :param bytes impl: override default tag with ``IMPLICIT`` one
1920         :param bytes expl: override default tag with ``EXPLICIT`` one
1921         :param default: set default value. Type same as in ``value``
1922         :param bool optional: is object ``OPTIONAL`` in sequence
1923         """
1924         super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1925         self._value = value
1926         specs = getattr(self, "schema", {}) if _specs is None else _specs
1927         self.specs = specs if isinstance(specs, dict) else dict(specs)
1928         self._bound_min, self._bound_max = getattr(
1929             self,
1930             "bounds",
1931             (float("-inf"), float("+inf")),
1932         ) if bounds is None else bounds
1933         if value is not None:
1934             self._value = self._value_sanitize(value)
1935         if default is not None:
1936             default = self._value_sanitize(default)
1937             self.default = self.__class__(
1938                 value=default,
1939                 impl=self.tag,
1940                 expl=self._expl,
1941                 _specs=self.specs,
1942             )
1943             if self._value is None:
1944                 self._value = default
1945
1946     def _value_sanitize(self, value):
1947         if isinstance(value, integer_types):
1948             pass
1949         elif issubclass(value.__class__, Integer):
1950             value = value._value
1951         elif isinstance(value, str):
1952             value = self.specs.get(value)
1953             if value is None:
1954                 raise ObjUnknown("integer value: %s" % value)
1955         else:
1956             raise InvalidValueType((self.__class__, int, str))
1957         if not self._bound_min <= value <= self._bound_max:
1958             raise BoundsError(self._bound_min, value, self._bound_max)
1959         return value
1960
1961     @property
1962     def ready(self):
1963         return self._value is not None
1964
1965     def copy(self):
1966         obj = self.__class__(_specs=self.specs)
1967         obj._value = self._value
1968         obj._bound_min = self._bound_min
1969         obj._bound_max = self._bound_max
1970         obj.tag = self.tag
1971         obj._expl = self._expl
1972         obj.default = self.default
1973         obj.optional = self.optional
1974         obj.offset = self.offset
1975         obj.llen = self.llen
1976         obj.vlen = self.vlen
1977         obj.expl_lenindef = self.expl_lenindef
1978         obj.lenindef = self.lenindef
1979         obj.ber_encoded = self.ber_encoded
1980         return obj
1981
1982     def __int__(self):
1983         self._assert_ready()
1984         return int(self._value)
1985
1986     def __hash__(self):
1987         self._assert_ready()
1988         return hash(
1989             self.tag +
1990             bytes(self._expl or b"") +
1991             str(self._value).encode("ascii"),
1992         )
1993
1994     def __eq__(self, their):
1995         if isinstance(their, integer_types):
1996             return self._value == their
1997         if not issubclass(their.__class__, Integer):
1998             return False
1999         return (
2000             self._value == their._value and
2001             self.tag == their.tag and
2002             self._expl == their._expl
2003         )
2004
2005     def __lt__(self, their):
2006         return self._value < their._value
2007
2008     @property
2009     def named(self):
2010         for name, value in iteritems(self.specs):
2011             if value == self._value:
2012                 return name
2013         return None
2014
2015     def __call__(
2016             self,
2017             value=None,
2018             bounds=None,
2019             impl=None,
2020             expl=None,
2021             default=None,
2022             optional=None,
2023     ):
2024         return self.__class__(
2025             value=value,
2026             bounds=(
2027                 (self._bound_min, self._bound_max)
2028                 if bounds is None else bounds
2029             ),
2030             impl=self.tag if impl is None else impl,
2031             expl=self._expl if expl is None else expl,
2032             default=self.default if default is None else default,
2033             optional=self.optional if optional is None else optional,
2034             _specs=self.specs,
2035         )
2036
2037     def _encode(self):
2038         self._assert_ready()
2039         value = self._value
2040         if PY2:
2041             if value == 0:
2042                 octets = bytearray([0])
2043             elif value < 0:
2044                 value = -value
2045                 value -= 1
2046                 octets = bytearray()
2047                 while value > 0:
2048                     octets.append((value & 0xFF) ^ 0xFF)
2049                     value >>= 8
2050                 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2051                     octets.append(0xFF)
2052             else:
2053                 octets = bytearray()
2054                 while value > 0:
2055                     octets.append(value & 0xFF)
2056                     value >>= 8
2057                 if octets[-1] & 0x80 > 0:
2058                     octets.append(0x00)
2059             octets.reverse()
2060             octets = bytes(octets)
2061         else:
2062             bytes_len = ceil(value.bit_length() / 8) or 1
2063             while True:
2064                 try:
2065                     octets = value.to_bytes(
2066                         bytes_len,
2067                         byteorder="big",
2068                         signed=True,
2069                     )
2070                 except OverflowError:
2071                     bytes_len += 1
2072                 else:
2073                     break
2074         return b"".join((self.tag, len_encode(len(octets)), octets))
2075
2076     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2077         try:
2078             t, _, lv = tag_strip(tlv)
2079         except DecodeError as err:
2080             raise err.__class__(
2081                 msg=err.msg,
2082                 klass=self.__class__,
2083                 decode_path=decode_path,
2084                 offset=offset,
2085             )
2086         if t != self.tag:
2087             raise TagMismatch(
2088                 klass=self.__class__,
2089                 decode_path=decode_path,
2090                 offset=offset,
2091             )
2092         if tag_only:
2093             return None
2094         try:
2095             l, llen, v = len_decode(lv)
2096         except DecodeError as err:
2097             raise err.__class__(
2098                 msg=err.msg,
2099                 klass=self.__class__,
2100                 decode_path=decode_path,
2101                 offset=offset,
2102             )
2103         if l > len(v):
2104             raise NotEnoughData(
2105                 "encoded length is longer than data",
2106                 klass=self.__class__,
2107                 decode_path=decode_path,
2108                 offset=offset,
2109             )
2110         if l == 0:
2111             raise NotEnoughData(
2112                 "zero length",
2113                 klass=self.__class__,
2114                 decode_path=decode_path,
2115                 offset=offset,
2116             )
2117         v, tail = v[:l], v[l:]
2118         first_octet = byte2int(v)
2119         if l > 1:
2120             second_octet = byte2int(v[1:])
2121             if (
2122                     ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2123                     ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2124             ):
2125                 raise DecodeError(
2126                     "non normalized integer",
2127                     klass=self.__class__,
2128                     decode_path=decode_path,
2129                     offset=offset,
2130                 )
2131         if PY2:
2132             value = 0
2133             if first_octet & 0x80 > 0:
2134                 octets = bytearray()
2135                 for octet in bytearray(v):
2136                     octets.append(octet ^ 0xFF)
2137                 for octet in octets:
2138                     value = (value << 8) | octet
2139                 value += 1
2140                 value = -value
2141             else:
2142                 for octet in bytearray(v):
2143                     value = (value << 8) | octet
2144         else:
2145             value = int.from_bytes(v, byteorder="big", signed=True)
2146         try:
2147             obj = self.__class__(
2148                 value=value,
2149                 bounds=(self._bound_min, self._bound_max),
2150                 impl=self.tag,
2151                 expl=self._expl,
2152                 default=self.default,
2153                 optional=self.optional,
2154                 _specs=self.specs,
2155                 _decoded=(offset, llen, l),
2156             )
2157         except BoundsError as err:
2158             raise DecodeError(
2159                 msg=str(err),
2160                 klass=self.__class__,
2161                 decode_path=decode_path,
2162                 offset=offset,
2163             )
2164         return obj, tail
2165
2166     def __repr__(self):
2167         return pp_console_row(next(self.pps()))
2168
2169     def pps(self, decode_path=()):
2170         yield _pp(
2171             obj=self,
2172             asn1_type_name=self.asn1_type_name,
2173             obj_name=self.__class__.__name__,
2174             decode_path=decode_path,
2175             value=(self.named or str(self._value)) if self.ready else None,
2176             optional=self.optional,
2177             default=self == self.default,
2178             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2179             expl=None if self._expl is None else tag_decode(self._expl),
2180             offset=self.offset,
2181             tlen=self.tlen,
2182             llen=self.llen,
2183             vlen=self.vlen,
2184             expl_offset=self.expl_offset if self.expled else None,
2185             expl_tlen=self.expl_tlen if self.expled else None,
2186             expl_llen=self.expl_llen if self.expled else None,
2187             expl_vlen=self.expl_vlen if self.expled else None,
2188             expl_lenindef=self.expl_lenindef,
2189             bered=self.bered,
2190         )
2191         for pp in self.pps_lenindef(decode_path):
2192             yield pp
2193
2194
2195 SET01 = frozenset(("0", "1"))
2196
2197
2198 class BitString(Obj):
2199     """``BIT STRING`` bit string type
2200
2201     >>> BitString(b"hello world")
2202     BIT STRING 88 bits 68656c6c6f20776f726c64
2203     >>> bytes(b)
2204     b'hello world'
2205     >>> b == b"hello world"
2206     True
2207     >>> b.bit_len
2208     88
2209
2210     >>> BitString("'0A3B5F291CD'H")
2211     BIT STRING 44 bits 0a3b5f291cd0
2212     >>> b = BitString("'010110000000'B")
2213     BIT STRING 12 bits 5800
2214     >>> b.bit_len
2215     12
2216     >>> b[0], b[1], b[2], b[3]
2217     (False, True, False, True)
2218     >>> b[1000]
2219     False
2220     >>> [v for v in b]
2221     [False, True, False, True, True, False, False, False, False, False, False, False]
2222
2223     ::
2224
2225         class KeyUsage(BitString):
2226             schema = (
2227                 ("digitalSignature", 0),
2228                 ("nonRepudiation", 1),
2229                 ("keyEncipherment", 2),
2230             )
2231
2232     >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2233     KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2234     >>> b.named
2235     ['nonRepudiation', 'keyEncipherment']
2236     >>> b.specs
2237     {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2238
2239     .. note::
2240
2241        Pay attention that BIT STRING can be encoded both in primitive
2242        and constructed forms. Decoder always checks constructed form tag
2243        additionally to specified primitive one. If BER decoding is
2244        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2245        of DER restrictions.
2246     """
2247     __slots__ = ("tag_constructed", "specs", "defined")
2248     tag_default = tag_encode(3)
2249     asn1_type_name = "BIT STRING"
2250
2251     def __init__(
2252             self,
2253             value=None,
2254             impl=None,
2255             expl=None,
2256             default=None,
2257             optional=False,
2258             _specs=None,
2259             _decoded=(0, 0, 0),
2260     ):
2261         """
2262         :param value: set the value. Either binary type, tuple of named
2263                       values (if ``schema`` is specified in the class),
2264                       string in ``'XXX...'B`` form, or
2265                       :py:class:`pyderasn.BitString` object
2266         :param bytes impl: override default tag with ``IMPLICIT`` one
2267         :param bytes expl: override default tag with ``EXPLICIT`` one
2268         :param default: set default value. Type same as in ``value``
2269         :param bool optional: is object ``OPTIONAL`` in sequence
2270         """
2271         super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2272         specs = getattr(self, "schema", {}) if _specs is None else _specs
2273         self.specs = specs if isinstance(specs, dict) else dict(specs)
2274         self._value = None if value is None else self._value_sanitize(value)
2275         if default is not None:
2276             default = self._value_sanitize(default)
2277             self.default = self.__class__(
2278                 value=default,
2279                 impl=self.tag,
2280                 expl=self._expl,
2281             )
2282             if value is None:
2283                 self._value = default
2284         self.defined = None
2285         tag_klass, _, tag_num = tag_decode(self.tag)
2286         self.tag_constructed = tag_encode(
2287             klass=tag_klass,
2288             form=TagFormConstructed,
2289             num=tag_num,
2290         )
2291
2292     def _bits2octets(self, bits):
2293         if len(self.specs) > 0:
2294             bits = bits.rstrip("0")
2295         bit_len = len(bits)
2296         bits += "0" * ((8 - (bit_len % 8)) % 8)
2297         octets = bytearray(len(bits) // 8)
2298         for i in six_xrange(len(octets)):
2299             octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2300         return bit_len, bytes(octets)
2301
2302     def _value_sanitize(self, value):
2303         if isinstance(value, (string_types, binary_type)):
2304             if (
2305                     isinstance(value, string_types) and
2306                     value.startswith("'")
2307             ):
2308                 if value.endswith("'B"):
2309                     value = value[1:-2]
2310                     if not frozenset(value) <= SET01:
2311                         raise ValueError("B's coding contains unacceptable chars")
2312                     return self._bits2octets(value)
2313                 if value.endswith("'H"):
2314                     value = value[1:-2]
2315                     return (
2316                         len(value) * 4,
2317                         hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2318                     )
2319             if isinstance(value, binary_type):
2320                 return (len(value) * 8, value)
2321             raise InvalidValueType((self.__class__, string_types, binary_type))
2322         if isinstance(value, tuple):
2323             if (
2324                     len(value) == 2 and
2325                     isinstance(value[0], integer_types) and
2326                     isinstance(value[1], binary_type)
2327             ):
2328                 return value
2329             bits = []
2330             for name in value:
2331                 bit = self.specs.get(name)
2332                 if bit is None:
2333                     raise ObjUnknown("BitString value: %s" % name)
2334                 bits.append(bit)
2335             if len(bits) == 0:
2336                 return self._bits2octets("")
2337             bits = frozenset(bits)
2338             return self._bits2octets("".join(
2339                 ("1" if bit in bits else "0")
2340                 for bit in six_xrange(max(bits) + 1)
2341             ))
2342         if issubclass(value.__class__, BitString):
2343             return value._value
2344         raise InvalidValueType((self.__class__, binary_type, string_types))
2345
2346     @property
2347     def ready(self):
2348         return self._value is not None
2349
2350     def copy(self):
2351         obj = self.__class__(_specs=self.specs)
2352         value = self._value
2353         if value is not None:
2354             value = (value[0], value[1])
2355         obj._value = value
2356         obj.tag = self.tag
2357         obj._expl = self._expl
2358         obj.default = self.default
2359         obj.optional = self.optional
2360         obj.offset = self.offset
2361         obj.llen = self.llen
2362         obj.vlen = self.vlen
2363         obj.expl_lenindef = self.expl_lenindef
2364         obj.lenindef = self.lenindef
2365         obj.ber_encoded = self.ber_encoded
2366         return obj
2367
2368     def __iter__(self):
2369         self._assert_ready()
2370         for i in six_xrange(self._value[0]):
2371             yield self[i]
2372
2373     @property
2374     def bit_len(self):
2375         self._assert_ready()
2376         return self._value[0]
2377
2378     def __bytes__(self):
2379         self._assert_ready()
2380         return self._value[1]
2381
2382     def __eq__(self, their):
2383         if isinstance(their, bytes):
2384             return self._value[1] == their
2385         if not issubclass(their.__class__, BitString):
2386             return False
2387         return (
2388             self._value == their._value and
2389             self.tag == their.tag and
2390             self._expl == their._expl
2391         )
2392
2393     @property
2394     def named(self):
2395         return [name for name, bit in iteritems(self.specs) if self[bit]]
2396
2397     def __call__(
2398             self,
2399             value=None,
2400             impl=None,
2401             expl=None,
2402             default=None,
2403             optional=None,
2404     ):
2405         return self.__class__(
2406             value=value,
2407             impl=self.tag if impl is None else impl,
2408             expl=self._expl if expl is None else expl,
2409             default=self.default if default is None else default,
2410             optional=self.optional if optional is None else optional,
2411             _specs=self.specs,
2412         )
2413
2414     def __getitem__(self, key):
2415         if isinstance(key, int):
2416             bit_len, octets = self._value
2417             if key >= bit_len:
2418                 return False
2419             return (
2420                 byte2int(memoryview(octets)[key // 8:]) >>
2421                 (7 - (key % 8))
2422             ) & 1 == 1
2423         if isinstance(key, string_types):
2424             value = self.specs.get(key)
2425             if value is None:
2426                 raise ObjUnknown("BitString value: %s" % key)
2427             return self[value]
2428         raise InvalidValueType((int, str))
2429
2430     def _encode(self):
2431         self._assert_ready()
2432         bit_len, octets = self._value
2433         return b"".join((
2434             self.tag,
2435             len_encode(len(octets) + 1),
2436             int2byte((8 - bit_len % 8) % 8),
2437             octets,
2438         ))
2439
2440     def _decode_chunk(self, lv, offset, decode_path, ctx):
2441         try:
2442             l, llen, v = len_decode(lv)
2443         except DecodeError as err:
2444             raise err.__class__(
2445                 msg=err.msg,
2446                 klass=self.__class__,
2447                 decode_path=decode_path,
2448                 offset=offset,
2449             )
2450         if l > len(v):
2451             raise NotEnoughData(
2452                 "encoded length is longer than data",
2453                 klass=self.__class__,
2454                 decode_path=decode_path,
2455                 offset=offset,
2456             )
2457         if l == 0:
2458             raise NotEnoughData(
2459                 "zero length",
2460                 klass=self.__class__,
2461                 decode_path=decode_path,
2462                 offset=offset,
2463             )
2464         pad_size = byte2int(v)
2465         if l == 1 and pad_size != 0:
2466             raise DecodeError(
2467                 "invalid empty value",
2468                 klass=self.__class__,
2469                 decode_path=decode_path,
2470                 offset=offset,
2471             )
2472         if pad_size > 7:
2473             raise DecodeError(
2474                 "too big pad",
2475                 klass=self.__class__,
2476                 decode_path=decode_path,
2477                 offset=offset,
2478             )
2479         if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2480             raise DecodeError(
2481                 "invalid pad",
2482                 klass=self.__class__,
2483                 decode_path=decode_path,
2484                 offset=offset,
2485             )
2486         v, tail = v[:l], v[l:]
2487         obj = self.__class__(
2488             value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2489             impl=self.tag,
2490             expl=self._expl,
2491             default=self.default,
2492             optional=self.optional,
2493             _specs=self.specs,
2494             _decoded=(offset, llen, l),
2495         )
2496         return obj, tail
2497
2498     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2499         try:
2500             t, tlen, lv = tag_strip(tlv)
2501         except DecodeError as err:
2502             raise err.__class__(
2503                 msg=err.msg,
2504                 klass=self.__class__,
2505                 decode_path=decode_path,
2506                 offset=offset,
2507             )
2508         if t == self.tag:
2509             if tag_only:  # pragma: no cover
2510                 return None
2511             return self._decode_chunk(lv, offset, decode_path, ctx)
2512         if t == self.tag_constructed:
2513             if not ctx.get("bered", False):
2514                 raise DecodeError(
2515                     "unallowed BER constructed encoding",
2516                     klass=self.__class__,
2517                     decode_path=decode_path,
2518                     offset=offset,
2519                 )
2520             if tag_only:  # pragma: no cover
2521                 return None
2522             lenindef = False
2523             try:
2524                 l, llen, v = len_decode(lv)
2525             except LenIndefForm:
2526                 llen, l, v = 1, 0, lv[1:]
2527                 lenindef = True
2528             except DecodeError as err:
2529                 raise err.__class__(
2530                     msg=err.msg,
2531                     klass=self.__class__,
2532                     decode_path=decode_path,
2533                     offset=offset,
2534                 )
2535             if l > len(v):
2536                 raise NotEnoughData(
2537                     "encoded length is longer than data",
2538                     klass=self.__class__,
2539                     decode_path=decode_path,
2540                     offset=offset,
2541                 )
2542             if not lenindef and l == 0:
2543                 raise NotEnoughData(
2544                     "zero length",
2545                     klass=self.__class__,
2546                     decode_path=decode_path,
2547                     offset=offset,
2548                 )
2549             chunks = []
2550             sub_offset = offset + tlen + llen
2551             vlen = 0
2552             while True:
2553                 if lenindef:
2554                     if v[:EOC_LEN].tobytes() == EOC:
2555                         break
2556                 else:
2557                     if vlen == l:
2558                         break
2559                     if vlen > l:
2560                         raise DecodeError(
2561                             "chunk out of bounds",
2562                             klass=self.__class__,
2563                             decode_path=decode_path + (str(len(chunks) - 1),),
2564                             offset=chunks[-1].offset,
2565                         )
2566                 sub_decode_path = decode_path + (str(len(chunks)),)
2567                 try:
2568                     chunk, v_tail = BitString().decode(
2569                         v,
2570                         offset=sub_offset,
2571                         decode_path=sub_decode_path,
2572                         leavemm=True,
2573                         ctx=ctx,
2574                         _ctx_immutable=False,
2575                     )
2576                 except TagMismatch:
2577                     raise DecodeError(
2578                         "expected BitString encoded chunk",
2579                         klass=self.__class__,
2580                         decode_path=sub_decode_path,
2581                         offset=sub_offset,
2582                     )
2583                 chunks.append(chunk)
2584                 sub_offset += chunk.tlvlen
2585                 vlen += chunk.tlvlen
2586                 v = v_tail
2587             if len(chunks) == 0:
2588                 raise DecodeError(
2589                     "no chunks",
2590                     klass=self.__class__,
2591                     decode_path=decode_path,
2592                     offset=offset,
2593                 )
2594             values = []
2595             bit_len = 0
2596             for chunk_i, chunk in enumerate(chunks[:-1]):
2597                 if chunk.bit_len % 8 != 0:
2598                     raise DecodeError(
2599                         "BitString chunk is not multiple of 8 bits",
2600                         klass=self.__class__,
2601                         decode_path=decode_path + (str(chunk_i),),
2602                         offset=chunk.offset,
2603                     )
2604                 values.append(bytes(chunk))
2605                 bit_len += chunk.bit_len
2606             chunk_last = chunks[-1]
2607             values.append(bytes(chunk_last))
2608             bit_len += chunk_last.bit_len
2609             obj = self.__class__(
2610                 value=(bit_len, b"".join(values)),
2611                 impl=self.tag,
2612                 expl=self._expl,
2613                 default=self.default,
2614                 optional=self.optional,
2615                 _specs=self.specs,
2616                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2617             )
2618             obj.lenindef = lenindef
2619             obj.ber_encoded = True
2620             return obj, (v[EOC_LEN:] if lenindef else v)
2621         raise TagMismatch(
2622             klass=self.__class__,
2623             decode_path=decode_path,
2624             offset=offset,
2625         )
2626
2627     def __repr__(self):
2628         return pp_console_row(next(self.pps()))
2629
2630     def pps(self, decode_path=()):
2631         value = None
2632         blob = None
2633         if self.ready:
2634             bit_len, blob = self._value
2635             value = "%d bits" % bit_len
2636             if len(self.specs) > 0:
2637                 blob = tuple(self.named)
2638         yield _pp(
2639             obj=self,
2640             asn1_type_name=self.asn1_type_name,
2641             obj_name=self.__class__.__name__,
2642             decode_path=decode_path,
2643             value=value,
2644             blob=blob,
2645             optional=self.optional,
2646             default=self == self.default,
2647             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2648             expl=None if self._expl is None else tag_decode(self._expl),
2649             offset=self.offset,
2650             tlen=self.tlen,
2651             llen=self.llen,
2652             vlen=self.vlen,
2653             expl_offset=self.expl_offset if self.expled else None,
2654             expl_tlen=self.expl_tlen if self.expled else None,
2655             expl_llen=self.expl_llen if self.expled else None,
2656             expl_vlen=self.expl_vlen if self.expled else None,
2657             expl_lenindef=self.expl_lenindef,
2658             lenindef=self.lenindef,
2659             ber_encoded=self.ber_encoded,
2660             bered=self.bered,
2661         )
2662         defined_by, defined = self.defined or (None, None)
2663         if defined_by is not None:
2664             yield defined.pps(
2665                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2666             )
2667         for pp in self.pps_lenindef(decode_path):
2668             yield pp
2669
2670
2671 class OctetString(Obj):
2672     """``OCTET STRING`` binary string type
2673
2674     >>> s = OctetString(b"hello world")
2675     OCTET STRING 11 bytes 68656c6c6f20776f726c64
2676     >>> s == OctetString(b"hello world")
2677     True
2678     >>> bytes(s)
2679     b'hello world'
2680
2681     >>> OctetString(b"hello", bounds=(4, 4))
2682     Traceback (most recent call last):
2683     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2684     >>> OctetString(b"hell", bounds=(4, 4))
2685     OCTET STRING 4 bytes 68656c6c
2686
2687     .. note::
2688
2689        Pay attention that OCTET STRING can be encoded both in primitive
2690        and constructed forms. Decoder always checks constructed form tag
2691        additionally to specified primitive one. If BER decoding is
2692        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2693        of DER restrictions.
2694     """
2695     __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2696     tag_default = tag_encode(4)
2697     asn1_type_name = "OCTET STRING"
2698
2699     def __init__(
2700             self,
2701             value=None,
2702             bounds=None,
2703             impl=None,
2704             expl=None,
2705             default=None,
2706             optional=False,
2707             _decoded=(0, 0, 0),
2708     ):
2709         """
2710         :param value: set the value. Either binary type, or
2711                       :py:class:`pyderasn.OctetString` object
2712         :param bounds: set ``(MIN, MAX)`` value size constraint.
2713                        (-inf, +inf) by default
2714         :param bytes impl: override default tag with ``IMPLICIT`` one
2715         :param bytes expl: override default tag with ``EXPLICIT`` one
2716         :param default: set default value. Type same as in ``value``
2717         :param bool optional: is object ``OPTIONAL`` in sequence
2718         """
2719         super(OctetString, self).__init__(
2720             impl,
2721             expl,
2722             default,
2723             optional,
2724             _decoded,
2725         )
2726         self._value = value
2727         self._bound_min, self._bound_max = getattr(
2728             self,
2729             "bounds",
2730             (0, float("+inf")),
2731         ) if bounds is None else bounds
2732         if value is not None:
2733             self._value = self._value_sanitize(value)
2734         if default is not None:
2735             default = self._value_sanitize(default)
2736             self.default = self.__class__(
2737                 value=default,
2738                 impl=self.tag,
2739                 expl=self._expl,
2740             )
2741             if self._value is None:
2742                 self._value = default
2743         self.defined = None
2744         tag_klass, _, tag_num = tag_decode(self.tag)
2745         self.tag_constructed = tag_encode(
2746             klass=tag_klass,
2747             form=TagFormConstructed,
2748             num=tag_num,
2749         )
2750
2751     def _value_sanitize(self, value):
2752         if isinstance(value, binary_type):
2753             pass
2754         elif issubclass(value.__class__, OctetString):
2755             value = value._value
2756         else:
2757             raise InvalidValueType((self.__class__, bytes))
2758         if not self._bound_min <= len(value) <= self._bound_max:
2759             raise BoundsError(self._bound_min, len(value), self._bound_max)
2760         return value
2761
2762     @property
2763     def ready(self):
2764         return self._value is not None
2765
2766     def copy(self):
2767         obj = self.__class__()
2768         obj._value = self._value
2769         obj._bound_min = self._bound_min
2770         obj._bound_max = self._bound_max
2771         obj.tag = self.tag
2772         obj._expl = self._expl
2773         obj.default = self.default
2774         obj.optional = self.optional
2775         obj.offset = self.offset
2776         obj.llen = self.llen
2777         obj.vlen = self.vlen
2778         obj.expl_lenindef = self.expl_lenindef
2779         obj.lenindef = self.lenindef
2780         obj.ber_encoded = self.ber_encoded
2781         return obj
2782
2783     def __bytes__(self):
2784         self._assert_ready()
2785         return self._value
2786
2787     def __eq__(self, their):
2788         if isinstance(their, binary_type):
2789             return self._value == their
2790         if not issubclass(their.__class__, OctetString):
2791             return False
2792         return (
2793             self._value == their._value and
2794             self.tag == their.tag and
2795             self._expl == their._expl
2796         )
2797
2798     def __lt__(self, their):
2799         return self._value < their._value
2800
2801     def __call__(
2802             self,
2803             value=None,
2804             bounds=None,
2805             impl=None,
2806             expl=None,
2807             default=None,
2808             optional=None,
2809     ):
2810         return self.__class__(
2811             value=value,
2812             bounds=(
2813                 (self._bound_min, self._bound_max)
2814                 if bounds is None else bounds
2815             ),
2816             impl=self.tag if impl is None else impl,
2817             expl=self._expl if expl is None else expl,
2818             default=self.default if default is None else default,
2819             optional=self.optional if optional is None else optional,
2820         )
2821
2822     def _encode(self):
2823         self._assert_ready()
2824         return b"".join((
2825             self.tag,
2826             len_encode(len(self._value)),
2827             self._value,
2828         ))
2829
2830     def _decode_chunk(self, lv, offset, decode_path, ctx):
2831         try:
2832             l, llen, v = len_decode(lv)
2833         except DecodeError as err:
2834             raise err.__class__(
2835                 msg=err.msg,
2836                 klass=self.__class__,
2837                 decode_path=decode_path,
2838                 offset=offset,
2839             )
2840         if l > len(v):
2841             raise NotEnoughData(
2842                 "encoded length is longer than data",
2843                 klass=self.__class__,
2844                 decode_path=decode_path,
2845                 offset=offset,
2846             )
2847         v, tail = v[:l], v[l:]
2848         try:
2849             obj = self.__class__(
2850                 value=v.tobytes(),
2851                 bounds=(self._bound_min, self._bound_max),
2852                 impl=self.tag,
2853                 expl=self._expl,
2854                 default=self.default,
2855                 optional=self.optional,
2856                 _decoded=(offset, llen, l),
2857             )
2858         except DecodeError as err:
2859             raise DecodeError(
2860                 msg=err.msg,
2861                 klass=self.__class__,
2862                 decode_path=decode_path,
2863                 offset=offset,
2864             )
2865         except BoundsError as err:
2866             raise DecodeError(
2867                 msg=str(err),
2868                 klass=self.__class__,
2869                 decode_path=decode_path,
2870                 offset=offset,
2871             )
2872         return obj, tail
2873
2874     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2875         try:
2876             t, tlen, lv = tag_strip(tlv)
2877         except DecodeError as err:
2878             raise err.__class__(
2879                 msg=err.msg,
2880                 klass=self.__class__,
2881                 decode_path=decode_path,
2882                 offset=offset,
2883             )
2884         if t == self.tag:
2885             if tag_only:
2886                 return None
2887             return self._decode_chunk(lv, offset, decode_path, ctx)
2888         if t == self.tag_constructed:
2889             if not ctx.get("bered", False):
2890                 raise DecodeError(
2891                     "unallowed BER constructed encoding",
2892                     klass=self.__class__,
2893                     decode_path=decode_path,
2894                     offset=offset,
2895                 )
2896             if tag_only:
2897                 return None
2898             lenindef = False
2899             try:
2900                 l, llen, v = len_decode(lv)
2901             except LenIndefForm:
2902                 llen, l, v = 1, 0, lv[1:]
2903                 lenindef = True
2904             except DecodeError as err:
2905                 raise err.__class__(
2906                     msg=err.msg,
2907                     klass=self.__class__,
2908                     decode_path=decode_path,
2909                     offset=offset,
2910                 )
2911             if l > len(v):
2912                 raise NotEnoughData(
2913                     "encoded length is longer than data",
2914                     klass=self.__class__,
2915                     decode_path=decode_path,
2916                     offset=offset,
2917                 )
2918             chunks = []
2919             sub_offset = offset + tlen + llen
2920             vlen = 0
2921             while True:
2922                 if lenindef:
2923                     if v[:EOC_LEN].tobytes() == EOC:
2924                         break
2925                 else:
2926                     if vlen == l:
2927                         break
2928                     if vlen > l:
2929                         raise DecodeError(
2930                             "chunk out of bounds",
2931                             klass=self.__class__,
2932                             decode_path=decode_path + (str(len(chunks) - 1),),
2933                             offset=chunks[-1].offset,
2934                         )
2935                 sub_decode_path = decode_path + (str(len(chunks)),)
2936                 try:
2937                     chunk, v_tail = OctetString().decode(
2938                         v,
2939                         offset=sub_offset,
2940                         decode_path=sub_decode_path,
2941                         leavemm=True,
2942                         ctx=ctx,
2943                         _ctx_immutable=False,
2944                     )
2945                 except TagMismatch:
2946                     raise DecodeError(
2947                         "expected OctetString encoded chunk",
2948                         klass=self.__class__,
2949                         decode_path=sub_decode_path,
2950                         offset=sub_offset,
2951                     )
2952                 chunks.append(chunk)
2953                 sub_offset += chunk.tlvlen
2954                 vlen += chunk.tlvlen
2955                 v = v_tail
2956             try:
2957                 obj = self.__class__(
2958                     value=b"".join(bytes(chunk) for chunk in chunks),
2959                     bounds=(self._bound_min, self._bound_max),
2960                     impl=self.tag,
2961                     expl=self._expl,
2962                     default=self.default,
2963                     optional=self.optional,
2964                     _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2965                 )
2966             except DecodeError as err:
2967                 raise DecodeError(
2968                     msg=err.msg,
2969                     klass=self.__class__,
2970                     decode_path=decode_path,
2971                     offset=offset,
2972                 )
2973             except BoundsError as err:
2974                 raise DecodeError(
2975                     msg=str(err),
2976                     klass=self.__class__,
2977                     decode_path=decode_path,
2978                     offset=offset,
2979                 )
2980             obj.lenindef = lenindef
2981             obj.ber_encoded = True
2982             return obj, (v[EOC_LEN:] if lenindef else v)
2983         raise TagMismatch(
2984             klass=self.__class__,
2985             decode_path=decode_path,
2986             offset=offset,
2987         )
2988
2989     def __repr__(self):
2990         return pp_console_row(next(self.pps()))
2991
2992     def pps(self, decode_path=()):
2993         yield _pp(
2994             obj=self,
2995             asn1_type_name=self.asn1_type_name,
2996             obj_name=self.__class__.__name__,
2997             decode_path=decode_path,
2998             value=("%d bytes" % len(self._value)) if self.ready else None,
2999             blob=self._value if self.ready else None,
3000             optional=self.optional,
3001             default=self == self.default,
3002             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3003             expl=None if self._expl is None else tag_decode(self._expl),
3004             offset=self.offset,
3005             tlen=self.tlen,
3006             llen=self.llen,
3007             vlen=self.vlen,
3008             expl_offset=self.expl_offset if self.expled else None,
3009             expl_tlen=self.expl_tlen if self.expled else None,
3010             expl_llen=self.expl_llen if self.expled else None,
3011             expl_vlen=self.expl_vlen if self.expled else None,
3012             expl_lenindef=self.expl_lenindef,
3013             lenindef=self.lenindef,
3014             ber_encoded=self.ber_encoded,
3015             bered=self.bered,
3016         )
3017         defined_by, defined = self.defined or (None, None)
3018         if defined_by is not None:
3019             yield defined.pps(
3020                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3021             )
3022         for pp in self.pps_lenindef(decode_path):
3023             yield pp
3024
3025
3026 class Null(Obj):
3027     """``NULL`` null object
3028
3029     >>> n = Null()
3030     NULL
3031     >>> n.ready
3032     True
3033     """
3034     __slots__ = ()
3035     tag_default = tag_encode(5)
3036     asn1_type_name = "NULL"
3037
3038     def __init__(
3039             self,
3040             value=None,  # unused, but Sequence passes it
3041             impl=None,
3042             expl=None,
3043             optional=False,
3044             _decoded=(0, 0, 0),
3045     ):
3046         """
3047         :param bytes impl: override default tag with ``IMPLICIT`` one
3048         :param bytes expl: override default tag with ``EXPLICIT`` one
3049         :param bool optional: is object ``OPTIONAL`` in sequence
3050         """
3051         super(Null, self).__init__(impl, expl, None, optional, _decoded)
3052         self.default = None
3053
3054     @property
3055     def ready(self):
3056         return True
3057
3058     def copy(self):
3059         obj = self.__class__()
3060         obj.tag = self.tag
3061         obj._expl = self._expl
3062         obj.default = self.default
3063         obj.optional = self.optional
3064         obj.offset = self.offset
3065         obj.llen = self.llen
3066         obj.vlen = self.vlen
3067         obj.expl_lenindef = self.expl_lenindef
3068         obj.lenindef = self.lenindef
3069         obj.ber_encoded = self.ber_encoded
3070         return obj
3071
3072     def __eq__(self, their):
3073         if not issubclass(their.__class__, Null):
3074             return False
3075         return (
3076             self.tag == their.tag and
3077             self._expl == their._expl
3078         )
3079
3080     def __call__(
3081             self,
3082             value=None,
3083             impl=None,
3084             expl=None,
3085             optional=None,
3086     ):
3087         return self.__class__(
3088             impl=self.tag if impl is None else impl,
3089             expl=self._expl if expl is None else expl,
3090             optional=self.optional if optional is None else optional,
3091         )
3092
3093     def _encode(self):
3094         return self.tag + len_encode(0)
3095
3096     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3097         try:
3098             t, _, lv = tag_strip(tlv)
3099         except DecodeError as err:
3100             raise err.__class__(
3101                 msg=err.msg,
3102                 klass=self.__class__,
3103                 decode_path=decode_path,
3104                 offset=offset,
3105             )
3106         if t != self.tag:
3107             raise TagMismatch(
3108                 klass=self.__class__,
3109                 decode_path=decode_path,
3110                 offset=offset,
3111             )
3112         if tag_only:  # pragma: no cover
3113             return None
3114         try:
3115             l, _, v = len_decode(lv)
3116         except DecodeError as err:
3117             raise err.__class__(
3118                 msg=err.msg,
3119                 klass=self.__class__,
3120                 decode_path=decode_path,
3121                 offset=offset,
3122             )
3123         if l != 0:
3124             raise InvalidLength(
3125                 "Null must have zero length",
3126                 klass=self.__class__,
3127                 decode_path=decode_path,
3128                 offset=offset,
3129             )
3130         obj = self.__class__(
3131             impl=self.tag,
3132             expl=self._expl,
3133             optional=self.optional,
3134             _decoded=(offset, 1, 0),
3135         )
3136         return obj, v
3137
3138     def __repr__(self):
3139         return pp_console_row(next(self.pps()))
3140
3141     def pps(self, decode_path=()):
3142         yield _pp(
3143             obj=self,
3144             asn1_type_name=self.asn1_type_name,
3145             obj_name=self.__class__.__name__,
3146             decode_path=decode_path,
3147             optional=self.optional,
3148             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3149             expl=None if self._expl is None else tag_decode(self._expl),
3150             offset=self.offset,
3151             tlen=self.tlen,
3152             llen=self.llen,
3153             vlen=self.vlen,
3154             expl_offset=self.expl_offset if self.expled else None,
3155             expl_tlen=self.expl_tlen if self.expled else None,
3156             expl_llen=self.expl_llen if self.expled else None,
3157             expl_vlen=self.expl_vlen if self.expled else None,
3158             expl_lenindef=self.expl_lenindef,
3159             bered=self.bered,
3160         )
3161         for pp in self.pps_lenindef(decode_path):
3162             yield pp
3163
3164
3165 class ObjectIdentifier(Obj):
3166     """``OBJECT IDENTIFIER`` OID type
3167
3168     >>> oid = ObjectIdentifier((1, 2, 3))
3169     OBJECT IDENTIFIER 1.2.3
3170     >>> oid == ObjectIdentifier("1.2.3")
3171     True
3172     >>> tuple(oid)
3173     (1, 2, 3)
3174     >>> str(oid)
3175     '1.2.3'
3176     >>> oid + (4, 5) + ObjectIdentifier("1.7")
3177     OBJECT IDENTIFIER 1.2.3.4.5.1.7
3178
3179     >>> str(ObjectIdentifier((3, 1)))
3180     Traceback (most recent call last):
3181     pyderasn.InvalidOID: unacceptable first arc value
3182     """
3183     __slots__ = ("defines",)
3184     tag_default = tag_encode(6)
3185     asn1_type_name = "OBJECT IDENTIFIER"
3186
3187     def __init__(
3188             self,
3189             value=None,
3190             defines=(),
3191             impl=None,
3192             expl=None,
3193             default=None,
3194             optional=False,
3195             _decoded=(0, 0, 0),
3196     ):
3197         """
3198         :param value: set the value. Either tuples of integers,
3199                       string of "."-concatenated integers, or
3200                       :py:class:`pyderasn.ObjectIdentifier` object
3201         :param defines: sequence of tuples. Each tuple has two elements.
3202                         First one is relative to current one decode
3203                         path, aiming to the field defined by that OID.
3204                         Read about relative path in
3205                         :py:func:`pyderasn.abs_decode_path`. Second
3206                         tuple element is ``{OID: pyderasn.Obj()}``
3207                         dictionary, mapping between current OID value
3208                         and structure applied to defined field.
3209                         :ref:`Read about DEFINED BY <definedby>`
3210         :param bytes impl: override default tag with ``IMPLICIT`` one
3211         :param bytes expl: override default tag with ``EXPLICIT`` one
3212         :param default: set default value. Type same as in ``value``
3213         :param bool optional: is object ``OPTIONAL`` in sequence
3214         """
3215         super(ObjectIdentifier, self).__init__(
3216             impl,
3217             expl,
3218             default,
3219             optional,
3220             _decoded,
3221         )
3222         self._value = value
3223         if value is not None:
3224             self._value = self._value_sanitize(value)
3225         if default is not None:
3226             default = self._value_sanitize(default)
3227             self.default = self.__class__(
3228                 value=default,
3229                 impl=self.tag,
3230                 expl=self._expl,
3231             )
3232             if self._value is None:
3233                 self._value = default
3234         self.defines = defines
3235
3236     def __add__(self, their):
3237         if isinstance(their, self.__class__):
3238             return self.__class__(self._value + their._value)
3239         if isinstance(their, tuple):
3240             return self.__class__(self._value + their)
3241         raise InvalidValueType((self.__class__, tuple))
3242
3243     def _value_sanitize(self, value):
3244         if issubclass(value.__class__, ObjectIdentifier):
3245             return value._value
3246         if isinstance(value, string_types):
3247             try:
3248                 value = tuple(int(arc) for arc in value.split("."))
3249             except ValueError:
3250                 raise InvalidOID("unacceptable arcs values")
3251         if isinstance(value, tuple):
3252             if len(value) < 2:
3253                 raise InvalidOID("less than 2 arcs")
3254             first_arc = value[0]
3255             if first_arc in (0, 1):
3256                 if not (0 <= value[1] <= 39):
3257                     raise InvalidOID("second arc is too wide")
3258             elif first_arc == 2:
3259                 pass
3260             else:
3261                 raise InvalidOID("unacceptable first arc value")
3262             return value
3263         raise InvalidValueType((self.__class__, str, tuple))
3264
3265     @property
3266     def ready(self):
3267         return self._value is not None
3268
3269     def copy(self):
3270         obj = self.__class__()
3271         obj._value = self._value
3272         obj.defines = self.defines
3273         obj.tag = self.tag
3274         obj._expl = self._expl
3275         obj.default = self.default
3276         obj.optional = self.optional
3277         obj.offset = self.offset
3278         obj.llen = self.llen
3279         obj.vlen = self.vlen
3280         obj.expl_lenindef = self.expl_lenindef
3281         obj.lenindef = self.lenindef
3282         obj.ber_encoded = self.ber_encoded
3283         return obj
3284
3285     def __iter__(self):
3286         self._assert_ready()
3287         return iter(self._value)
3288
3289     def __str__(self):
3290         return ".".join(str(arc) for arc in self._value or ())
3291
3292     def __hash__(self):
3293         self._assert_ready()
3294         return hash(
3295             self.tag +
3296             bytes(self._expl or b"") +
3297             str(self._value).encode("ascii"),
3298         )
3299
3300     def __eq__(self, their):
3301         if isinstance(their, tuple):
3302             return self._value == their
3303         if not issubclass(their.__class__, ObjectIdentifier):
3304             return False
3305         return (
3306             self.tag == their.tag and
3307             self._expl == their._expl and
3308             self._value == their._value
3309         )
3310
3311     def __lt__(self, their):
3312         return self._value < their._value
3313
3314     def __call__(
3315             self,
3316             value=None,
3317             defines=None,
3318             impl=None,
3319             expl=None,
3320             default=None,
3321             optional=None,
3322     ):
3323         return self.__class__(
3324             value=value,
3325             defines=self.defines if defines is None else defines,
3326             impl=self.tag if impl is None else impl,
3327             expl=self._expl if expl is None else expl,
3328             default=self.default if default is None else default,
3329             optional=self.optional if optional is None else optional,
3330         )
3331
3332     def _encode(self):
3333         self._assert_ready()
3334         value = self._value
3335         first_value = value[1]
3336         first_arc = value[0]
3337         if first_arc == 0:
3338             pass
3339         elif first_arc == 1:
3340             first_value += 40
3341         elif first_arc == 2:
3342             first_value += 80
3343         else:  # pragma: no cover
3344             raise RuntimeError("invalid arc is stored")
3345         octets = [zero_ended_encode(first_value)]
3346         for arc in value[2:]:
3347             octets.append(zero_ended_encode(arc))
3348         v = b"".join(octets)
3349         return b"".join((self.tag, len_encode(len(v)), v))
3350
3351     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3352         try:
3353             t, _, lv = tag_strip(tlv)
3354         except DecodeError as err:
3355             raise err.__class__(
3356                 msg=err.msg,
3357                 klass=self.__class__,
3358                 decode_path=decode_path,
3359                 offset=offset,
3360             )
3361         if t != self.tag:
3362             raise TagMismatch(
3363                 klass=self.__class__,
3364                 decode_path=decode_path,
3365                 offset=offset,
3366             )
3367         if tag_only:  # pragma: no cover
3368             return None
3369         try:
3370             l, llen, v = len_decode(lv)
3371         except DecodeError as err:
3372             raise err.__class__(
3373                 msg=err.msg,
3374                 klass=self.__class__,
3375                 decode_path=decode_path,
3376                 offset=offset,
3377             )
3378         if l > len(v):
3379             raise NotEnoughData(
3380                 "encoded length is longer than data",
3381                 klass=self.__class__,
3382                 decode_path=decode_path,
3383                 offset=offset,
3384             )
3385         if l == 0:
3386             raise NotEnoughData(
3387                 "zero length",
3388                 klass=self.__class__,
3389                 decode_path=decode_path,
3390                 offset=offset,
3391             )
3392         v, tail = v[:l], v[l:]
3393         arcs = []
3394         ber_encoded = False
3395         while len(v) > 0:
3396             i = 0
3397             arc = 0
3398             while True:
3399                 octet = indexbytes(v, i)
3400                 if i == 0 and octet == 0x80:
3401                     if ctx.get("bered", False):
3402                         ber_encoded = True
3403                     else:
3404                         raise DecodeError("non normalized arc encoding")
3405                 arc = (arc << 7) | (octet & 0x7F)
3406                 if octet & 0x80 == 0:
3407                     arcs.append(arc)
3408                     v = v[i + 1:]
3409                     break
3410                 i += 1
3411                 if i == len(v):
3412                     raise DecodeError(
3413                         "unfinished OID",
3414                         klass=self.__class__,
3415                         decode_path=decode_path,
3416                         offset=offset,
3417                     )
3418         first_arc = 0
3419         second_arc = arcs[0]
3420         if 0 <= second_arc <= 39:
3421             first_arc = 0
3422         elif 40 <= second_arc <= 79:
3423             first_arc = 1
3424             second_arc -= 40
3425         else:
3426             first_arc = 2
3427             second_arc -= 80
3428         obj = self.__class__(
3429             value=tuple([first_arc, second_arc] + arcs[1:]),
3430             impl=self.tag,
3431             expl=self._expl,
3432             default=self.default,
3433             optional=self.optional,
3434             _decoded=(offset, llen, l),
3435         )
3436         if ber_encoded:
3437             obj.ber_encoded = True
3438         return obj, tail
3439
3440     def __repr__(self):
3441         return pp_console_row(next(self.pps()))
3442
3443     def pps(self, decode_path=()):
3444         yield _pp(
3445             obj=self,
3446             asn1_type_name=self.asn1_type_name,
3447             obj_name=self.__class__.__name__,
3448             decode_path=decode_path,
3449             value=str(self) if self.ready else None,
3450             optional=self.optional,
3451             default=self == self.default,
3452             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3453             expl=None if self._expl is None else tag_decode(self._expl),
3454             offset=self.offset,
3455             tlen=self.tlen,
3456             llen=self.llen,
3457             vlen=self.vlen,
3458             expl_offset=self.expl_offset if self.expled else None,
3459             expl_tlen=self.expl_tlen if self.expled else None,
3460             expl_llen=self.expl_llen if self.expled else None,
3461             expl_vlen=self.expl_vlen if self.expled else None,
3462             expl_lenindef=self.expl_lenindef,
3463             ber_encoded=self.ber_encoded,
3464             bered=self.bered,
3465         )
3466         for pp in self.pps_lenindef(decode_path):
3467             yield pp
3468
3469
3470 class Enumerated(Integer):
3471     """``ENUMERATED`` integer type
3472
3473     This type is identical to :py:class:`pyderasn.Integer`, but requires
3474     schema to be specified and does not accept values missing from it.
3475     """
3476     __slots__ = ()
3477     tag_default = tag_encode(10)
3478     asn1_type_name = "ENUMERATED"
3479
3480     def __init__(
3481             self,
3482             value=None,
3483             impl=None,
3484             expl=None,
3485             default=None,
3486             optional=False,
3487             _specs=None,
3488             _decoded=(0, 0, 0),
3489             bounds=None,  # dummy argument, workability for Integer.decode
3490     ):
3491         super(Enumerated, self).__init__(
3492             value=value,
3493             impl=impl,
3494             expl=expl,
3495             default=default,
3496             optional=optional,
3497             _specs=_specs,
3498             _decoded=_decoded,
3499         )
3500         if len(self.specs) == 0:
3501             raise ValueError("schema must be specified")
3502
3503     def _value_sanitize(self, value):
3504         if isinstance(value, self.__class__):
3505             value = value._value
3506         elif isinstance(value, integer_types):
3507             for _value in itervalues(self.specs):
3508                 if _value == value:
3509                     break
3510             else:
3511                 raise DecodeError(
3512                     "unknown integer value: %s" % value,
3513                     klass=self.__class__,
3514                 )
3515         elif isinstance(value, string_types):
3516             value = self.specs.get(value)
3517             if value is None:
3518                 raise ObjUnknown("integer value: %s" % value)
3519         else:
3520             raise InvalidValueType((self.__class__, int, str))
3521         return value
3522
3523     def copy(self):
3524         obj = self.__class__(_specs=self.specs)
3525         obj._value = self._value
3526         obj._bound_min = self._bound_min
3527         obj._bound_max = self._bound_max
3528         obj.tag = self.tag
3529         obj._expl = self._expl
3530         obj.default = self.default
3531         obj.optional = self.optional
3532         obj.offset = self.offset
3533         obj.llen = self.llen
3534         obj.vlen = self.vlen
3535         obj.expl_lenindef = self.expl_lenindef
3536         obj.lenindef = self.lenindef
3537         obj.ber_encoded = self.ber_encoded
3538         return obj
3539
3540     def __call__(
3541             self,
3542             value=None,
3543             impl=None,
3544             expl=None,
3545             default=None,
3546             optional=None,
3547             _specs=None,
3548     ):
3549         return self.__class__(
3550             value=value,
3551             impl=self.tag if impl is None else impl,
3552             expl=self._expl if expl is None else expl,
3553             default=self.default if default is None else default,
3554             optional=self.optional if optional is None else optional,
3555             _specs=self.specs,
3556         )
3557
3558
3559 class CommonString(OctetString):
3560     """Common class for all strings
3561
3562     Everything resembles :py:class:`pyderasn.OctetString`, except
3563     ability to deal with unicode text strings.
3564
3565     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
3566     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3567     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
3568     True
3569     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
3570     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
3571     >>> str(s)
3572     'привет Ð¼Ð¸Ñ€'
3573     >>> hexenc(bytes(s))
3574     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3575
3576     >>> PrintableString("привет Ð¼Ð¸Ñ€")
3577     Traceback (most recent call last):
3578     pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3579
3580     >>> BMPString("ада", bounds=(2, 2))
3581     Traceback (most recent call last):
3582     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3583     >>> s = BMPString("ад", bounds=(2, 2))
3584     >>> s.encoding
3585     'utf-16-be'
3586     >>> hexenc(bytes(s))
3587     '04300434'
3588
3589     .. list-table::
3590        :header-rows: 1
3591
3592        * - Class
3593          - Text Encoding
3594        * - :py:class:`pyderasn.UTF8String`
3595          - utf-8
3596        * - :py:class:`pyderasn.NumericString`
3597          - ascii
3598        * - :py:class:`pyderasn.PrintableString`
3599          - ascii
3600        * - :py:class:`pyderasn.TeletexString`
3601          - ascii
3602        * - :py:class:`pyderasn.T61String`
3603          - ascii
3604        * - :py:class:`pyderasn.VideotexString`
3605          - iso-8859-1
3606        * - :py:class:`pyderasn.IA5String`
3607          - ascii
3608        * - :py:class:`pyderasn.GraphicString`
3609          - iso-8859-1
3610        * - :py:class:`pyderasn.VisibleString`
3611          - ascii
3612        * - :py:class:`pyderasn.ISO646String`
3613          - ascii
3614        * - :py:class:`pyderasn.GeneralString`
3615          - iso-8859-1
3616        * - :py:class:`pyderasn.UniversalString`
3617          - utf-32-be
3618        * - :py:class:`pyderasn.BMPString`
3619          - utf-16-be
3620     """
3621     __slots__ = ("encoding",)
3622
3623     def _value_sanitize(self, value):
3624         value_raw = None
3625         value_decoded = None
3626         if isinstance(value, self.__class__):
3627             value_raw = value._value
3628         elif isinstance(value, text_type):
3629             value_decoded = value
3630         elif isinstance(value, binary_type):
3631             value_raw = value
3632         else:
3633             raise InvalidValueType((self.__class__, text_type, binary_type))
3634         try:
3635             value_raw = (
3636                 value_decoded.encode(self.encoding)
3637                 if value_raw is None else value_raw
3638             )
3639             value_decoded = (
3640                 value_raw.decode(self.encoding)
3641                 if value_decoded is None else value_decoded
3642             )
3643         except (UnicodeEncodeError, UnicodeDecodeError) as err:
3644             raise DecodeError(str(err))
3645         if not self._bound_min <= len(value_decoded) <= self._bound_max:
3646             raise BoundsError(
3647                 self._bound_min,
3648                 len(value_decoded),
3649                 self._bound_max,
3650             )
3651         return value_raw
3652
3653     def __eq__(self, their):
3654         if isinstance(their, binary_type):
3655             return self._value == their
3656         if isinstance(their, text_type):
3657             return self._value == their.encode(self.encoding)
3658         if not isinstance(their, self.__class__):
3659             return False
3660         return (
3661             self._value == their._value and
3662             self.tag == their.tag and
3663             self._expl == their._expl
3664         )
3665
3666     def __unicode__(self):
3667         if self.ready:
3668             return self._value.decode(self.encoding)
3669         return text_type(self._value)
3670
3671     def __repr__(self):
3672         return pp_console_row(next(self.pps(no_unicode=PY2)))
3673
3674     def pps(self, decode_path=(), no_unicode=False):
3675         value = None
3676         if self.ready:
3677             value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3678         yield _pp(
3679             obj=self,
3680             asn1_type_name=self.asn1_type_name,
3681             obj_name=self.__class__.__name__,
3682             decode_path=decode_path,
3683             value=value,
3684             optional=self.optional,
3685             default=self == self.default,
3686             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3687             expl=None if self._expl is None else tag_decode(self._expl),
3688             offset=self.offset,
3689             tlen=self.tlen,
3690             llen=self.llen,
3691             vlen=self.vlen,
3692             expl_offset=self.expl_offset if self.expled else None,
3693             expl_tlen=self.expl_tlen if self.expled else None,
3694             expl_llen=self.expl_llen if self.expled else None,
3695             expl_vlen=self.expl_vlen if self.expled else None,
3696             expl_lenindef=self.expl_lenindef,
3697             ber_encoded=self.ber_encoded,
3698             bered=self.bered,
3699         )
3700         for pp in self.pps_lenindef(decode_path):
3701             yield pp
3702
3703
3704 class UTF8String(CommonString):
3705     __slots__ = ()
3706     tag_default = tag_encode(12)
3707     encoding = "utf-8"
3708     asn1_type_name = "UTF8String"
3709
3710
3711 class AllowableCharsMixin(object):
3712     @property
3713     def allowable_chars(self):
3714         if PY2:
3715             return self._allowable_chars
3716         return frozenset(six_unichr(c) for c in self._allowable_chars)
3717
3718
3719 class NumericString(AllowableCharsMixin, CommonString):
3720     """Numeric string
3721
3722     Its value is properly sanitized: only ASCII digits with spaces can
3723     be stored.
3724
3725     >>> NumericString().allowable_chars
3726     frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
3727     """
3728     __slots__ = ()
3729     tag_default = tag_encode(18)
3730     encoding = "ascii"
3731     asn1_type_name = "NumericString"
3732     _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3733
3734     def _value_sanitize(self, value):
3735         value = super(NumericString, self)._value_sanitize(value)
3736         if not frozenset(value) <= self._allowable_chars:
3737             raise DecodeError("non-numeric value")
3738         return value
3739
3740
3741 class PrintableString(AllowableCharsMixin, CommonString):
3742     """Printable string
3743
3744     Its value is properly sanitized: see X.680 41.4 table 10.
3745
3746     >>> PrintableString().allowable_chars
3747     frozenset([' ', "'", ..., 'z'])
3748     """
3749     __slots__ = ()
3750     tag_default = tag_encode(19)
3751     encoding = "ascii"
3752     asn1_type_name = "PrintableString"
3753     _allowable_chars = frozenset(
3754         (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3755     )
3756
3757     def _value_sanitize(self, value):
3758         value = super(PrintableString, self)._value_sanitize(value)
3759         if not frozenset(value) <= self._allowable_chars:
3760             raise DecodeError("non-printable value")
3761         return value
3762
3763
3764 class TeletexString(CommonString):
3765     __slots__ = ()
3766     tag_default = tag_encode(20)
3767     encoding = "ascii"
3768     asn1_type_name = "TeletexString"
3769
3770
3771 class T61String(TeletexString):
3772     __slots__ = ()
3773     asn1_type_name = "T61String"
3774
3775
3776 class VideotexString(CommonString):
3777     __slots__ = ()
3778     tag_default = tag_encode(21)
3779     encoding = "iso-8859-1"
3780     asn1_type_name = "VideotexString"
3781
3782
3783 class IA5String(CommonString):
3784     __slots__ = ()
3785     tag_default = tag_encode(22)
3786     encoding = "ascii"
3787     asn1_type_name = "IA5"
3788
3789
3790 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3791 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3792 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3793
3794
3795 class UTCTime(CommonString):
3796     """``UTCTime`` datetime type
3797
3798     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3799     UTCTime UTCTime 2017-09-30T22:07:50
3800     >>> str(t)
3801     '170930220750Z'
3802     >>> bytes(t)
3803     b'170930220750Z'
3804     >>> t.todatetime()
3805     datetime.datetime(2017, 9, 30, 22, 7, 50)
3806     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3807     datetime.datetime(1957, 9, 30, 22, 7, 50)
3808
3809     .. warning::
3810
3811        BER encoding is unsupported.
3812     """
3813     __slots__ = ()
3814     tag_default = tag_encode(23)
3815     encoding = "ascii"
3816     asn1_type_name = "UTCTime"
3817
3818     def __init__(
3819             self,
3820             value=None,
3821             impl=None,
3822             expl=None,
3823             default=None,
3824             optional=False,
3825             _decoded=(0, 0, 0),
3826             bounds=None,  # dummy argument, workability for OctetString.decode
3827     ):
3828         """
3829         :param value: set the value. Either datetime type, or
3830                       :py:class:`pyderasn.UTCTime` object
3831         :param bytes impl: override default tag with ``IMPLICIT`` one
3832         :param bytes expl: override default tag with ``EXPLICIT`` one
3833         :param default: set default value. Type same as in ``value``
3834         :param bool optional: is object ``OPTIONAL`` in sequence
3835         """
3836         super(UTCTime, self).__init__(
3837             impl=impl,
3838             expl=expl,
3839             default=default,
3840             optional=optional,
3841             _decoded=_decoded,
3842         )
3843         self._value = value
3844         if value is not None:
3845             self._value = self._value_sanitize(value)
3846         if default is not None:
3847             default = self._value_sanitize(default)
3848             self.default = self.__class__(
3849                 value=default,
3850                 impl=self.tag,
3851                 expl=self._expl,
3852             )
3853             if self._value is None:
3854                 self._value = default
3855
3856     def _strptime(self, value):
3857         # datetime.strptime's format: %y%m%d%H%M%SZ
3858         if len(value) != LEN_YYMMDDHHMMSSZ:
3859             raise ValueError("invalid UTCTime length")
3860         if value[-1] != "Z":
3861             raise ValueError("non UTC timezone")
3862         return datetime(
3863             2000 + int(value[:2]),  # %y
3864             int(value[2:4]),  # %m
3865             int(value[4:6]),  # %d
3866             int(value[6:8]),  # %H
3867             int(value[8:10]),  # %M
3868             int(value[10:12]),  # %S
3869         )
3870
3871     def _value_sanitize(self, value):
3872         if isinstance(value, binary_type):
3873             try:
3874                 value_decoded = value.decode("ascii")
3875             except (UnicodeEncodeError, UnicodeDecodeError) as err:
3876                 raise DecodeError("invalid UTCTime encoding: %r" % err)
3877             try:
3878                 self._strptime(value_decoded)
3879             except (TypeError, ValueError) as err:
3880                 raise DecodeError("invalid UTCTime format: %r" % err)
3881             return value
3882         if isinstance(value, self.__class__):
3883             return value._value
3884         if isinstance(value, datetime):
3885             return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
3886         raise InvalidValueType((self.__class__, datetime))
3887
3888     def __eq__(self, their):
3889         if isinstance(their, binary_type):
3890             return self._value == their
3891         if isinstance(their, datetime):
3892             return self.todatetime() == their
3893         if not isinstance(their, self.__class__):
3894             return False
3895         return (
3896             self._value == their._value and
3897             self.tag == their.tag and
3898             self._expl == their._expl
3899         )
3900
3901     def todatetime(self):
3902         """Convert to datetime
3903
3904         :returns: datetime
3905
3906         Pay attention that UTCTime can not hold full year, so all years
3907         having < 50 years are treated as 20xx, 19xx otherwise, according
3908         to X.509 recomendation.
3909         """
3910         value = self._strptime(self._value.decode("ascii"))
3911         year = value.year % 100
3912         return datetime(
3913             year=(2000 + year) if year < 50 else (1900 + year),
3914             month=value.month,
3915             day=value.day,
3916             hour=value.hour,
3917             minute=value.minute,
3918             second=value.second,
3919         )
3920
3921     def __repr__(self):
3922         return pp_console_row(next(self.pps()))
3923
3924     def pps(self, decode_path=()):
3925         yield _pp(
3926             obj=self,
3927             asn1_type_name=self.asn1_type_name,
3928             obj_name=self.__class__.__name__,
3929             decode_path=decode_path,
3930             value=self.todatetime().isoformat() if self.ready else None,
3931             optional=self.optional,
3932             default=self == self.default,
3933             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3934             expl=None if self._expl is None else tag_decode(self._expl),
3935             offset=self.offset,
3936             tlen=self.tlen,
3937             llen=self.llen,
3938             vlen=self.vlen,
3939             expl_offset=self.expl_offset if self.expled else None,
3940             expl_tlen=self.expl_tlen if self.expled else None,
3941             expl_llen=self.expl_llen if self.expled else None,
3942             expl_vlen=self.expl_vlen if self.expled else None,
3943             expl_lenindef=self.expl_lenindef,
3944             ber_encoded=self.ber_encoded,
3945             bered=self.bered,
3946         )
3947         for pp in self.pps_lenindef(decode_path):
3948             yield pp
3949
3950
3951 class GeneralizedTime(UTCTime):
3952     """``GeneralizedTime`` datetime type
3953
3954     This type is similar to :py:class:`pyderasn.UTCTime`.
3955
3956     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3957     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3958     >>> str(t)
3959     '20170930220750.000123Z'
3960     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3961     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3962
3963     .. warning::
3964
3965        BER encoding is unsupported.
3966
3967     .. warning::
3968
3969        Only microsecond fractions are supported.
3970        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
3971        higher precision values.
3972     """
3973     __slots__ = ()
3974     tag_default = tag_encode(24)
3975     asn1_type_name = "GeneralizedTime"
3976
3977     def _strptime(self, value):
3978         l = len(value)
3979         if l == LEN_YYYYMMDDHHMMSSZ:
3980             # datetime.strptime's format: %y%m%d%H%M%SZ
3981             if value[-1] != "Z":
3982                 raise ValueError("non UTC timezone")
3983             return datetime(
3984                 int(value[:4]),  # %Y
3985                 int(value[4:6]),  # %m
3986                 int(value[6:8]),  # %d
3987                 int(value[8:10]),  # %H
3988                 int(value[10:12]),  # %M
3989                 int(value[12:14]),  # %S
3990             )
3991         if l >= LEN_YYYYMMDDHHMMSSDMZ:
3992             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
3993             if value[-1] != "Z":
3994                 raise ValueError("non UTC timezone")
3995             if value[14] != ".":
3996                 raise ValueError("no fractions separator")
3997             us = value[15:-1]
3998             if us[-1] == "0":
3999                 raise ValueError("trailing zero")
4000             us_len = len(us)
4001             if us_len > 6:
4002                 raise ValueError("only microsecond fractions are supported")
4003             us = int(us + ("0" * (6 - us_len)))
4004             decoded = datetime(
4005                 int(value[:4]),  # %Y
4006                 int(value[4:6]),  # %m
4007                 int(value[6:8]),  # %d
4008                 int(value[8:10]),  # %H
4009                 int(value[10:12]),  # %M
4010                 int(value[12:14]),  # %S
4011                 us,  # %f
4012             )
4013             return decoded
4014         raise ValueError("invalid GeneralizedTime length")
4015
4016     def _value_sanitize(self, value):
4017         if isinstance(value, binary_type):
4018             try:
4019                 value_decoded = value.decode("ascii")
4020             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4021                 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
4022             try:
4023                 self._strptime(value_decoded)
4024             except (TypeError, ValueError) as err:
4025                 raise DecodeError(
4026                     "invalid GeneralizedTime format: %r" % err,
4027                     klass=self.__class__,
4028                 )
4029             return value
4030         if isinstance(value, self.__class__):
4031             return value._value
4032         if isinstance(value, datetime):
4033             encoded = value.strftime("%Y%m%d%H%M%S")
4034             if value.microsecond > 0:
4035                 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4036             return (encoded + "Z").encode("ascii")
4037         raise InvalidValueType((self.__class__, datetime))
4038
4039     def todatetime(self):
4040         return self._strptime(self._value.decode("ascii"))
4041
4042
4043 class GraphicString(CommonString):
4044     __slots__ = ()
4045     tag_default = tag_encode(25)
4046     encoding = "iso-8859-1"
4047     asn1_type_name = "GraphicString"
4048
4049
4050 class VisibleString(CommonString):
4051     __slots__ = ()
4052     tag_default = tag_encode(26)
4053     encoding = "ascii"
4054     asn1_type_name = "VisibleString"
4055
4056
4057 class ISO646String(VisibleString):
4058     __slots__ = ()
4059     asn1_type_name = "ISO646String"
4060
4061
4062 class GeneralString(CommonString):
4063     __slots__ = ()
4064     tag_default = tag_encode(27)
4065     encoding = "iso-8859-1"
4066     asn1_type_name = "GeneralString"
4067
4068
4069 class UniversalString(CommonString):
4070     __slots__ = ()
4071     tag_default = tag_encode(28)
4072     encoding = "utf-32-be"
4073     asn1_type_name = "UniversalString"
4074
4075
4076 class BMPString(CommonString):
4077     __slots__ = ()
4078     tag_default = tag_encode(30)
4079     encoding = "utf-16-be"
4080     asn1_type_name = "BMPString"
4081
4082
4083 class Choice(Obj):
4084     """``CHOICE`` special type
4085
4086     ::
4087
4088         class GeneralName(Choice):
4089             schema = (
4090                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4091                 ("dNSName", IA5String(impl=tag_ctxp(2))),
4092             )
4093
4094     >>> gn = GeneralName()
4095     GeneralName CHOICE
4096     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4097     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4098     >>> gn["dNSName"] = IA5String("bar.baz")
4099     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4100     >>> gn["rfc822Name"]
4101     None
4102     >>> gn["dNSName"]
4103     [2] IA5String IA5 bar.baz
4104     >>> gn.choice
4105     'dNSName'
4106     >>> gn.value == gn["dNSName"]
4107     True
4108     >>> gn.specs
4109     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4110
4111     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4112     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4113     """
4114     __slots__ = ("specs",)
4115     tag_default = None
4116     asn1_type_name = "CHOICE"
4117
4118     def __init__(
4119             self,
4120             value=None,
4121             schema=None,
4122             impl=None,
4123             expl=None,
4124             default=None,
4125             optional=False,
4126             _decoded=(0, 0, 0),
4127     ):
4128         """
4129         :param value: set the value. Either ``(choice, value)`` tuple, or
4130                       :py:class:`pyderasn.Choice` object
4131         :param bytes impl: can not be set, do **not** use it
4132         :param bytes expl: override default tag with ``EXPLICIT`` one
4133         :param default: set default value. Type same as in ``value``
4134         :param bool optional: is object ``OPTIONAL`` in sequence
4135         """
4136         if impl is not None:
4137             raise ValueError("no implicit tag allowed for CHOICE")
4138         super(Choice, self).__init__(None, expl, default, optional, _decoded)
4139         if schema is None:
4140             schema = getattr(self, "schema", ())
4141         if len(schema) == 0:
4142             raise ValueError("schema must be specified")
4143         self.specs = (
4144             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4145         )
4146         self._value = None
4147         if value is not None:
4148             self._value = self._value_sanitize(value)
4149         if default is not None:
4150             default_value = self._value_sanitize(default)
4151             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4152             default_obj.specs = self.specs
4153             default_obj._value = default_value
4154             self.default = default_obj
4155             if value is None:
4156                 self._value = default_obj.copy()._value
4157
4158     def _value_sanitize(self, value):
4159         if isinstance(value, tuple) and len(value) == 2:
4160             choice, obj = value
4161             spec = self.specs.get(choice)
4162             if spec is None:
4163                 raise ObjUnknown(choice)
4164             if not isinstance(obj, spec.__class__):
4165                 raise InvalidValueType((spec,))
4166             return (choice, spec(obj))
4167         if isinstance(value, self.__class__):
4168             return value._value
4169         raise InvalidValueType((self.__class__, tuple))
4170
4171     @property
4172     def ready(self):
4173         return self._value is not None and self._value[1].ready
4174
4175     @property
4176     def bered(self):
4177         return self.expl_lenindef or (
4178             (self._value is not None) and
4179             self._value[1].bered
4180         )
4181
4182     def copy(self):
4183         obj = self.__class__(schema=self.specs)
4184         obj._expl = self._expl
4185         obj.default = self.default
4186         obj.optional = self.optional
4187         obj.offset = self.offset
4188         obj.llen = self.llen
4189         obj.vlen = self.vlen
4190         obj.expl_lenindef = self.expl_lenindef
4191         obj.lenindef = self.lenindef
4192         obj.ber_encoded = self.ber_encoded
4193         value = self._value
4194         if value is not None:
4195             obj._value = (value[0], value[1].copy())
4196         return obj
4197
4198     def __eq__(self, their):
4199         if isinstance(their, tuple) and len(their) == 2:
4200             return self._value == their
4201         if not isinstance(their, self.__class__):
4202             return False
4203         return (
4204             self.specs == their.specs and
4205             self._value == their._value
4206         )
4207
4208     def __call__(
4209             self,
4210             value=None,
4211             expl=None,
4212             default=None,
4213             optional=None,
4214     ):
4215         return self.__class__(
4216             value=value,
4217             schema=self.specs,
4218             expl=self._expl if expl is None else expl,
4219             default=self.default if default is None else default,
4220             optional=self.optional if optional is None else optional,
4221         )
4222
4223     @property
4224     def choice(self):
4225         self._assert_ready()
4226         return self._value[0]
4227
4228     @property
4229     def value(self):
4230         self._assert_ready()
4231         return self._value[1]
4232
4233     def __getitem__(self, key):
4234         if key not in self.specs:
4235             raise ObjUnknown(key)
4236         if self._value is None:
4237             return None
4238         choice, value = self._value
4239         if choice != key:
4240             return None
4241         return value
4242
4243     def __setitem__(self, key, value):
4244         spec = self.specs.get(key)
4245         if spec is None:
4246             raise ObjUnknown(key)
4247         if not isinstance(value, spec.__class__):
4248             raise InvalidValueType((spec.__class__,))
4249         self._value = (key, spec(value))
4250
4251     @property
4252     def tlen(self):
4253         return 0
4254
4255     @property
4256     def decoded(self):
4257         return self._value[1].decoded if self.ready else False
4258
4259     def _encode(self):
4260         self._assert_ready()
4261         return self._value[1].encode()
4262
4263     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4264         for choice, spec in iteritems(self.specs):
4265             sub_decode_path = decode_path + (choice,)
4266             try:
4267                 spec.decode(
4268                     tlv,
4269                     offset=offset,
4270                     leavemm=True,
4271                     decode_path=sub_decode_path,
4272                     ctx=ctx,
4273                     tag_only=True,
4274                     _ctx_immutable=False,
4275                 )
4276             except TagMismatch:
4277                 continue
4278             break
4279         else:
4280             raise TagMismatch(
4281                 klass=self.__class__,
4282                 decode_path=decode_path,
4283                 offset=offset,
4284             )
4285         if tag_only:  # pragma: no cover
4286             return None
4287         value, tail = spec.decode(
4288             tlv,
4289             offset=offset,
4290             leavemm=True,
4291             decode_path=sub_decode_path,
4292             ctx=ctx,
4293             _ctx_immutable=False,
4294         )
4295         obj = self.__class__(
4296             schema=self.specs,
4297             expl=self._expl,
4298             default=self.default,
4299             optional=self.optional,
4300             _decoded=(offset, 0, value.fulllen),
4301         )
4302         obj._value = (choice, value)
4303         return obj, tail
4304
4305     def __repr__(self):
4306         value = pp_console_row(next(self.pps()))
4307         if self.ready:
4308             value = "%s[%r]" % (value, self.value)
4309         return value
4310
4311     def pps(self, decode_path=()):
4312         yield _pp(
4313             obj=self,
4314             asn1_type_name=self.asn1_type_name,
4315             obj_name=self.__class__.__name__,
4316             decode_path=decode_path,
4317             value=self.choice if self.ready else None,
4318             optional=self.optional,
4319             default=self == self.default,
4320             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4321             expl=None if self._expl is None else tag_decode(self._expl),
4322             offset=self.offset,
4323             tlen=self.tlen,
4324             llen=self.llen,
4325             vlen=self.vlen,
4326             expl_lenindef=self.expl_lenindef,
4327             bered=self.bered,
4328         )
4329         if self.ready:
4330             yield self.value.pps(decode_path=decode_path + (self.choice,))
4331         for pp in self.pps_lenindef(decode_path):
4332             yield pp
4333
4334
4335 class PrimitiveTypes(Choice):
4336     """Predefined ``CHOICE`` for all generic primitive types
4337
4338     It could be useful for general decoding of some unspecified values:
4339
4340     >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4341     OCTET STRING 3 bytes 666f6f
4342     >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4343     INTEGER 1193046
4344     """
4345     __slots__ = ()
4346     schema = tuple((klass.__name__, klass()) for klass in (
4347         Boolean,
4348         Integer,
4349         BitString,
4350         OctetString,
4351         Null,
4352         ObjectIdentifier,
4353         UTF8String,
4354         NumericString,
4355         PrintableString,
4356         TeletexString,
4357         VideotexString,
4358         IA5String,
4359         UTCTime,
4360         GeneralizedTime,
4361         GraphicString,
4362         VisibleString,
4363         ISO646String,
4364         GeneralString,
4365         UniversalString,
4366         BMPString,
4367     ))
4368
4369
4370 class Any(Obj):
4371     """``ANY`` special type
4372
4373     >>> Any(Integer(-123))
4374     ANY 020185
4375     >>> a = Any(OctetString(b"hello world").encode())
4376     ANY 040b68656c6c6f20776f726c64
4377     >>> hexenc(bytes(a))
4378     b'0x040x0bhello world'
4379     """
4380     __slots__ = ("defined",)
4381     tag_default = tag_encode(0)
4382     asn1_type_name = "ANY"
4383
4384     def __init__(
4385             self,
4386             value=None,
4387             expl=None,
4388             optional=False,
4389             _decoded=(0, 0, 0),
4390     ):
4391         """
4392         :param value: set the value. Either any kind of pyderasn's
4393                       **ready** object, or bytes. Pay attention that
4394                       **no** validation is performed is raw binary value
4395                       is valid TLV
4396         :param bytes expl: override default tag with ``EXPLICIT`` one
4397         :param bool optional: is object ``OPTIONAL`` in sequence
4398         """
4399         super(Any, self).__init__(None, expl, None, optional, _decoded)
4400         self._value = None if value is None else self._value_sanitize(value)
4401         self.defined = None
4402
4403     def _value_sanitize(self, value):
4404         if isinstance(value, binary_type):
4405             return value
4406         if isinstance(value, self.__class__):
4407             return value._value
4408         if isinstance(value, Obj):
4409             return value.encode()
4410         raise InvalidValueType((self.__class__, Obj, binary_type))
4411
4412     @property
4413     def ready(self):
4414         return self._value is not None
4415
4416     @property
4417     def bered(self):
4418         if self.expl_lenindef or self.lenindef:
4419             return True
4420         if self.defined is None:
4421             return False
4422         return self.defined[1].bered
4423
4424     def copy(self):
4425         obj = self.__class__()
4426         obj._value = self._value
4427         obj.tag = self.tag
4428         obj._expl = self._expl
4429         obj.optional = self.optional
4430         obj.offset = self.offset
4431         obj.llen = self.llen
4432         obj.vlen = self.vlen
4433         obj.expl_lenindef = self.expl_lenindef
4434         obj.lenindef = self.lenindef
4435         obj.ber_encoded = self.ber_encoded
4436         return obj
4437
4438     def __eq__(self, their):
4439         if isinstance(their, binary_type):
4440             return self._value == their
4441         if issubclass(their.__class__, Any):
4442             return self._value == their._value
4443         return False
4444
4445     def __call__(
4446             self,
4447             value=None,
4448             expl=None,
4449             optional=None,
4450     ):
4451         return self.__class__(
4452             value=value,
4453             expl=self._expl if expl is None else expl,
4454             optional=self.optional if optional is None else optional,
4455         )
4456
4457     def __bytes__(self):
4458         self._assert_ready()
4459         return self._value
4460
4461     @property
4462     def tlen(self):
4463         return 0
4464
4465     def _encode(self):
4466         self._assert_ready()
4467         return self._value
4468
4469     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4470         try:
4471             t, tlen, lv = tag_strip(tlv)
4472         except DecodeError as err:
4473             raise err.__class__(
4474                 msg=err.msg,
4475                 klass=self.__class__,
4476                 decode_path=decode_path,
4477                 offset=offset,
4478             )
4479         try:
4480             l, llen, v = len_decode(lv)
4481         except LenIndefForm as err:
4482             if not ctx.get("bered", False):
4483                 raise err.__class__(
4484                     msg=err.msg,
4485                     klass=self.__class__,
4486                     decode_path=decode_path,
4487                     offset=offset,
4488                 )
4489             llen, vlen, v = 1, 0, lv[1:]
4490             sub_offset = offset + tlen + llen
4491             chunk_i = 0
4492             while v[:EOC_LEN].tobytes() != EOC:
4493                 chunk, v = Any().decode(
4494                     v,
4495                     offset=sub_offset,
4496                     decode_path=decode_path + (str(chunk_i),),
4497                     leavemm=True,
4498                     ctx=ctx,
4499                     _ctx_immutable=False,
4500                 )
4501                 vlen += chunk.tlvlen
4502                 sub_offset += chunk.tlvlen
4503                 chunk_i += 1
4504             tlvlen = tlen + llen + vlen + EOC_LEN
4505             obj = self.__class__(
4506                 value=tlv[:tlvlen].tobytes(),
4507                 expl=self._expl,
4508                 optional=self.optional,
4509                 _decoded=(offset, 0, tlvlen),
4510             )
4511             obj.lenindef = True
4512             obj.tag = t
4513             return obj, v[EOC_LEN:]
4514         except DecodeError as err:
4515             raise err.__class__(
4516                 msg=err.msg,
4517                 klass=self.__class__,
4518                 decode_path=decode_path,
4519                 offset=offset,
4520             )
4521         if l > len(v):
4522             raise NotEnoughData(
4523                 "encoded length is longer than data",
4524                 klass=self.__class__,
4525                 decode_path=decode_path,
4526                 offset=offset,
4527             )
4528         tlvlen = tlen + llen + l
4529         v, tail = tlv[:tlvlen], v[l:]
4530         obj = self.__class__(
4531             value=v.tobytes(),
4532             expl=self._expl,
4533             optional=self.optional,
4534             _decoded=(offset, 0, tlvlen),
4535         )
4536         obj.tag = t
4537         return obj, tail
4538
4539     def __repr__(self):
4540         return pp_console_row(next(self.pps()))
4541
4542     def pps(self, decode_path=()):
4543         yield _pp(
4544             obj=self,
4545             asn1_type_name=self.asn1_type_name,
4546             obj_name=self.__class__.__name__,
4547             decode_path=decode_path,
4548             blob=self._value if self.ready else None,
4549             optional=self.optional,
4550             default=self == self.default,
4551             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4552             expl=None if self._expl is None else tag_decode(self._expl),
4553             offset=self.offset,
4554             tlen=self.tlen,
4555             llen=self.llen,
4556             vlen=self.vlen,
4557             expl_offset=self.expl_offset if self.expled else None,
4558             expl_tlen=self.expl_tlen if self.expled else None,
4559             expl_llen=self.expl_llen if self.expled else None,
4560             expl_vlen=self.expl_vlen if self.expled else None,
4561             expl_lenindef=self.expl_lenindef,
4562             lenindef=self.lenindef,
4563             bered=self.bered,
4564         )
4565         defined_by, defined = self.defined or (None, None)
4566         if defined_by is not None:
4567             yield defined.pps(
4568                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4569             )
4570         for pp in self.pps_lenindef(decode_path):
4571             yield pp
4572
4573
4574 ########################################################################
4575 # ASN.1 constructed types
4576 ########################################################################
4577
4578 def get_def_by_path(defines_by_path, sub_decode_path):
4579     """Get define by decode path
4580     """
4581     for path, define in defines_by_path:
4582         if len(path) != len(sub_decode_path):
4583             continue
4584         for p1, p2 in zip(path, sub_decode_path):
4585             if (p1 != any) and (p1 != p2):
4586                 break
4587         else:
4588             return define
4589
4590
4591 def abs_decode_path(decode_path, rel_path):
4592     """Create an absolute decode path from current and relative ones
4593
4594     :param decode_path: current decode path, starting point. Tuple of strings
4595     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4596                      If first tuple's element is "/", then treat it as
4597                      an absolute path, ignoring ``decode_path`` as
4598                      starting point. Also this tuple can contain ".."
4599                      elements, stripping the leading element from
4600                      ``decode_path``
4601
4602     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4603     ("foo", "bar", "baz", "whatever")
4604     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4605     ("foo", "whatever")
4606     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4607     ("baz", "whatever")
4608     """
4609     if rel_path[0] == "/":
4610         return rel_path[1:]
4611     if rel_path[0] == "..":
4612         return abs_decode_path(decode_path[:-1], rel_path[1:])
4613     return decode_path + rel_path
4614
4615
4616 class Sequence(Obj):
4617     """``SEQUENCE`` structure type
4618
4619     You have to make specification of sequence::
4620
4621         class Extension(Sequence):
4622             schema = (
4623                 ("extnID", ObjectIdentifier()),
4624                 ("critical", Boolean(default=False)),
4625                 ("extnValue", OctetString()),
4626             )
4627
4628     Then, you can work with it as with dictionary.
4629
4630     >>> ext = Extension()
4631     >>> Extension().specs
4632     OrderedDict([
4633         ('extnID', OBJECT IDENTIFIER),
4634         ('critical', BOOLEAN False OPTIONAL DEFAULT),
4635         ('extnValue', OCTET STRING),
4636     ])
4637     >>> ext["extnID"] = "1.2.3"
4638     Traceback (most recent call last):
4639     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4640     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4641
4642     You can determine if sequence is ready to be encoded:
4643
4644     >>> ext.ready
4645     False
4646     >>> ext.encode()
4647     Traceback (most recent call last):
4648     pyderasn.ObjNotReady: object is not ready: extnValue
4649     >>> ext["extnValue"] = OctetString(b"foobar")
4650     >>> ext.ready
4651     True
4652
4653     Value you want to assign, must have the same **type** as in
4654     corresponding specification, but it can have different tags,
4655     optional/default attributes -- they will be taken from specification
4656     automatically::
4657
4658         class TBSCertificate(Sequence):
4659             schema = (
4660                 ("version", Version(expl=tag_ctxc(0), default="v1")),
4661             [...]
4662
4663     >>> tbs = TBSCertificate()
4664     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4665
4666     Assign ``None`` to remove value from sequence.
4667
4668     You can set values in Sequence during its initialization:
4669
4670     >>> AlgorithmIdentifier((
4671         ("algorithm", ObjectIdentifier("1.2.3")),
4672         ("parameters", Any(Null()))
4673     ))
4674     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4675
4676     You can determine if value exists/set in the sequence and take its value:
4677
4678     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4679     (True, True, False)
4680     >>> ext["extnID"]
4681     OBJECT IDENTIFIER 1.2.3
4682
4683     But pay attention that if value has default, then it won't be (not
4684     in) in the sequence (because ``DEFAULT`` must not be encoded in
4685     DER), but you can read its value:
4686
4687     >>> "critical" in ext, ext["critical"]
4688     (False, BOOLEAN False)
4689     >>> ext["critical"] = Boolean(True)
4690     >>> "critical" in ext, ext["critical"]
4691     (True, BOOLEAN True)
4692
4693     All defaulted values are always optional.
4694
4695     .. _allow_default_values_ctx:
4696
4697     DER prohibits default value encoding and will raise an error if
4698     default value is unexpectedly met during decode.
4699     If :ref:`bered <bered_ctx>` context option is set, then no error
4700     will be raised, but ``bered`` attribute set. You can disable strict
4701     defaulted values existence validation by setting
4702     ``"allow_default_values": True`` :ref:`context <ctx>` option.
4703
4704     Two sequences are equal if they have equal specification (schema),
4705     implicit/explicit tagging and the same values.
4706     """
4707     __slots__ = ("specs",)
4708     tag_default = tag_encode(form=TagFormConstructed, num=16)
4709     asn1_type_name = "SEQUENCE"
4710
4711     def __init__(
4712             self,
4713             value=None,
4714             schema=None,
4715             impl=None,
4716             expl=None,
4717             default=None,
4718             optional=False,
4719             _decoded=(0, 0, 0),
4720     ):
4721         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4722         if schema is None:
4723             schema = getattr(self, "schema", ())
4724         self.specs = (
4725             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4726         )
4727         self._value = {}
4728         if value is not None:
4729             if issubclass(value.__class__, Sequence):
4730                 self._value = value._value
4731             elif hasattr(value, "__iter__"):
4732                 for seq_key, seq_value in value:
4733                     self[seq_key] = seq_value
4734             else:
4735                 raise InvalidValueType((Sequence,))
4736         if default is not None:
4737             if not issubclass(default.__class__, Sequence):
4738                 raise InvalidValueType((Sequence,))
4739             default_value = default._value
4740             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4741             default_obj.specs = self.specs
4742             default_obj._value = default_value
4743             self.default = default_obj
4744             if value is None:
4745                 self._value = default_obj.copy()._value
4746
4747     @property
4748     def ready(self):
4749         for name, spec in iteritems(self.specs):
4750             value = self._value.get(name)
4751             if value is None:
4752                 if spec.optional:
4753                     continue
4754                 return False
4755             if not value.ready:
4756                 return False
4757         return True
4758
4759     @property
4760     def bered(self):
4761         if self.expl_lenindef or self.lenindef or self.ber_encoded:
4762             return True
4763         return any(value.bered for value in itervalues(self._value))
4764
4765     def copy(self):
4766         obj = self.__class__(schema=self.specs)
4767         obj.tag = self.tag
4768         obj._expl = self._expl
4769         obj.default = self.default
4770         obj.optional = self.optional
4771         obj.offset = self.offset
4772         obj.llen = self.llen
4773         obj.vlen = self.vlen
4774         obj.expl_lenindef = self.expl_lenindef
4775         obj.lenindef = self.lenindef
4776         obj.ber_encoded = self.ber_encoded
4777         obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4778         return obj
4779
4780     def __eq__(self, their):
4781         if not isinstance(their, self.__class__):
4782             return False
4783         return (
4784             self.specs == their.specs and
4785             self.tag == their.tag and
4786             self._expl == their._expl and
4787             self._value == their._value
4788         )
4789
4790     def __call__(
4791             self,
4792             value=None,
4793             impl=None,
4794             expl=None,
4795             default=None,
4796             optional=None,
4797     ):
4798         return self.__class__(
4799             value=value,
4800             schema=self.specs,
4801             impl=self.tag if impl is None else impl,
4802             expl=self._expl if expl is None else expl,
4803             default=self.default if default is None else default,
4804             optional=self.optional if optional is None else optional,
4805         )
4806
4807     def __contains__(self, key):
4808         return key in self._value
4809
4810     def __setitem__(self, key, value):
4811         spec = self.specs.get(key)
4812         if spec is None:
4813             raise ObjUnknown(key)
4814         if value is None:
4815             self._value.pop(key, None)
4816             return
4817         if not isinstance(value, spec.__class__):
4818             raise InvalidValueType((spec.__class__,))
4819         value = spec(value=value)
4820         if spec.default is not None and value == spec.default:
4821             self._value.pop(key, None)
4822             return
4823         self._value[key] = value
4824
4825     def __getitem__(self, key):
4826         value = self._value.get(key)
4827         if value is not None:
4828             return value
4829         spec = self.specs.get(key)
4830         if spec is None:
4831             raise ObjUnknown(key)
4832         if spec.default is not None:
4833             return spec.default
4834         return None
4835
4836     def _encoded_values(self):
4837         raws = []
4838         for name, spec in iteritems(self.specs):
4839             value = self._value.get(name)
4840             if value is None:
4841                 if spec.optional:
4842                     continue
4843                 raise ObjNotReady(name)
4844             raws.append(value.encode())
4845         return raws
4846
4847     def _encode(self):
4848         v = b"".join(self._encoded_values())
4849         return b"".join((self.tag, len_encode(len(v)), v))
4850
4851     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4852         try:
4853             t, tlen, lv = tag_strip(tlv)
4854         except DecodeError as err:
4855             raise err.__class__(
4856                 msg=err.msg,
4857                 klass=self.__class__,
4858                 decode_path=decode_path,
4859                 offset=offset,
4860             )
4861         if t != self.tag:
4862             raise TagMismatch(
4863                 klass=self.__class__,
4864                 decode_path=decode_path,
4865                 offset=offset,
4866             )
4867         if tag_only:  # pragma: no cover
4868             return None
4869         lenindef = False
4870         ctx_bered = ctx.get("bered", False)
4871         try:
4872             l, llen, v = len_decode(lv)
4873         except LenIndefForm as err:
4874             if not ctx_bered:
4875                 raise err.__class__(
4876                     msg=err.msg,
4877                     klass=self.__class__,
4878                     decode_path=decode_path,
4879                     offset=offset,
4880                 )
4881             l, llen, v = 0, 1, lv[1:]
4882             lenindef = True
4883         except DecodeError as err:
4884             raise err.__class__(
4885                 msg=err.msg,
4886                 klass=self.__class__,
4887                 decode_path=decode_path,
4888                 offset=offset,
4889             )
4890         if l > len(v):
4891             raise NotEnoughData(
4892                 "encoded length is longer than data",
4893                 klass=self.__class__,
4894                 decode_path=decode_path,
4895                 offset=offset,
4896             )
4897         if not lenindef:
4898             v, tail = v[:l], v[l:]
4899         vlen = 0
4900         sub_offset = offset + tlen + llen
4901         values = {}
4902         ber_encoded = False
4903         ctx_allow_default_values = ctx.get("allow_default_values", False)
4904         for name, spec in iteritems(self.specs):
4905             if spec.optional and (
4906                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4907                     len(v) == 0
4908             ):
4909                 continue
4910             sub_decode_path = decode_path + (name,)
4911             try:
4912                 value, v_tail = spec.decode(
4913                     v,
4914                     sub_offset,
4915                     leavemm=True,
4916                     decode_path=sub_decode_path,
4917                     ctx=ctx,
4918                     _ctx_immutable=False,
4919                 )
4920             except TagMismatch as err:
4921                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
4922                     continue
4923                 raise
4924
4925             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4926             if defined is not None:
4927                 defined_by, defined_spec = defined
4928                 if issubclass(value.__class__, SequenceOf):
4929                     for i, _value in enumerate(value):
4930                         sub_sub_decode_path = sub_decode_path + (
4931                             str(i),
4932                             DecodePathDefBy(defined_by),
4933                         )
4934                         defined_value, defined_tail = defined_spec.decode(
4935                             memoryview(bytes(_value)),
4936                             sub_offset + (
4937                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4938                                 if value.expled else (value.tlen + value.llen)
4939                             ),
4940                             leavemm=True,
4941                             decode_path=sub_sub_decode_path,
4942                             ctx=ctx,
4943                             _ctx_immutable=False,
4944                         )
4945                         if len(defined_tail) > 0:
4946                             raise DecodeError(
4947                                 "remaining data",
4948                                 klass=self.__class__,
4949                                 decode_path=sub_sub_decode_path,
4950                                 offset=offset,
4951                             )
4952                         _value.defined = (defined_by, defined_value)
4953                 else:
4954                     defined_value, defined_tail = defined_spec.decode(
4955                         memoryview(bytes(value)),
4956                         sub_offset + (
4957                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4958                             if value.expled else (value.tlen + value.llen)
4959                         ),
4960                         leavemm=True,
4961                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4962                         ctx=ctx,
4963                         _ctx_immutable=False,
4964                     )
4965                     if len(defined_tail) > 0:
4966                         raise DecodeError(
4967                             "remaining data",
4968                             klass=self.__class__,
4969                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4970                             offset=offset,
4971                         )
4972                     value.defined = (defined_by, defined_value)
4973
4974             value_len = value.fulllen
4975             vlen += value_len
4976             sub_offset += value_len
4977             v = v_tail
4978             if spec.default is not None and value == spec.default:
4979                 if ctx_bered or ctx_allow_default_values:
4980                     ber_encoded = True
4981                 else:
4982                     raise DecodeError(
4983                         "DEFAULT value met",
4984                         klass=self.__class__,
4985                         decode_path=sub_decode_path,
4986                         offset=sub_offset,
4987                     )
4988             values[name] = value
4989
4990             spec_defines = getattr(spec, "defines", ())
4991             if len(spec_defines) == 0:
4992                 defines_by_path = ctx.get("defines_by_path", ())
4993                 if len(defines_by_path) > 0:
4994                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4995             if spec_defines is not None and len(spec_defines) > 0:
4996                 for rel_path, schema in spec_defines:
4997                     defined = schema.get(value, None)
4998                     if defined is not None:
4999                         ctx.setdefault("_defines", []).append((
5000                             abs_decode_path(sub_decode_path[:-1], rel_path),
5001                             (value, defined),
5002                         ))
5003         if lenindef:
5004             if v[:EOC_LEN].tobytes() != EOC:
5005                 raise DecodeError(
5006                     "no EOC",
5007                     klass=self.__class__,
5008                     decode_path=decode_path,
5009                     offset=offset,
5010                 )
5011             tail = v[EOC_LEN:]
5012             vlen += EOC_LEN
5013         elif len(v) > 0:
5014             raise DecodeError(
5015                 "remaining data",
5016                 klass=self.__class__,
5017                 decode_path=decode_path,
5018                 offset=offset,
5019             )
5020         obj = self.__class__(
5021             schema=self.specs,
5022             impl=self.tag,
5023             expl=self._expl,
5024             default=self.default,
5025             optional=self.optional,
5026             _decoded=(offset, llen, vlen),
5027         )
5028         obj._value = values
5029         obj.lenindef = lenindef
5030         obj.ber_encoded = ber_encoded
5031         return obj, tail
5032
5033     def __repr__(self):
5034         value = pp_console_row(next(self.pps()))
5035         cols = []
5036         for name in self.specs:
5037             _value = self._value.get(name)
5038             if _value is None:
5039                 continue
5040             cols.append("%s: %s" % (name, repr(_value)))
5041         return "%s[%s]" % (value, "; ".join(cols))
5042
5043     def pps(self, decode_path=()):
5044         yield _pp(
5045             obj=self,
5046             asn1_type_name=self.asn1_type_name,
5047             obj_name=self.__class__.__name__,
5048             decode_path=decode_path,
5049             optional=self.optional,
5050             default=self == self.default,
5051             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5052             expl=None if self._expl is None else tag_decode(self._expl),
5053             offset=self.offset,
5054             tlen=self.tlen,
5055             llen=self.llen,
5056             vlen=self.vlen,
5057             expl_offset=self.expl_offset if self.expled else None,
5058             expl_tlen=self.expl_tlen if self.expled else None,
5059             expl_llen=self.expl_llen if self.expled else None,
5060             expl_vlen=self.expl_vlen if self.expled else None,
5061             expl_lenindef=self.expl_lenindef,
5062             lenindef=self.lenindef,
5063             ber_encoded=self.ber_encoded,
5064             bered=self.bered,
5065         )
5066         for name in self.specs:
5067             value = self._value.get(name)
5068             if value is None:
5069                 continue
5070             yield value.pps(decode_path=decode_path + (name,))
5071         for pp in self.pps_lenindef(decode_path):
5072             yield pp
5073
5074
5075 class Set(Sequence):
5076     """``SET`` structure type
5077
5078     Its usage is identical to :py:class:`pyderasn.Sequence`.
5079
5080     .. _allow_unordered_set_ctx:
5081
5082     DER prohibits unordered values encoding and will raise an error
5083     during decode. If If :ref:`bered <bered_ctx>` context option is set,
5084     then no error will occure. Also you can disable strict values
5085     ordering check by setting ``"allow_unordered_set": True``
5086     :ref:`context <ctx>` option.
5087     """
5088     __slots__ = ()
5089     tag_default = tag_encode(form=TagFormConstructed, num=17)
5090     asn1_type_name = "SET"
5091
5092     def _encode(self):
5093         raws = self._encoded_values()
5094         raws.sort()
5095         v = b"".join(raws)
5096         return b"".join((self.tag, len_encode(len(v)), v))
5097
5098     def _specs_items(self):
5099         return iteritems(self.specs)
5100
5101     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5102         try:
5103             t, tlen, lv = tag_strip(tlv)
5104         except DecodeError as err:
5105             raise err.__class__(
5106                 msg=err.msg,
5107                 klass=self.__class__,
5108                 decode_path=decode_path,
5109                 offset=offset,
5110             )
5111         if t != self.tag:
5112             raise TagMismatch(
5113                 klass=self.__class__,
5114                 decode_path=decode_path,
5115                 offset=offset,
5116             )
5117         if tag_only:
5118             return None
5119         lenindef = False
5120         ctx_bered = ctx.get("bered", False)
5121         try:
5122             l, llen, v = len_decode(lv)
5123         except LenIndefForm as err:
5124             if not ctx_bered:
5125                 raise err.__class__(
5126                     msg=err.msg,
5127                     klass=self.__class__,
5128                     decode_path=decode_path,
5129                     offset=offset,
5130                 )
5131             l, llen, v = 0, 1, lv[1:]
5132             lenindef = True
5133         except DecodeError as err:
5134             raise err.__class__(
5135                 msg=err.msg,
5136                 klass=self.__class__,
5137                 decode_path=decode_path,
5138                 offset=offset,
5139             )
5140         if l > len(v):
5141             raise NotEnoughData(
5142                 "encoded length is longer than data",
5143                 klass=self.__class__,
5144                 offset=offset,
5145             )
5146         if not lenindef:
5147             v, tail = v[:l], v[l:]
5148         vlen = 0
5149         sub_offset = offset + tlen + llen
5150         values = {}
5151         ber_encoded = False
5152         ctx_allow_default_values = ctx.get("allow_default_values", False)
5153         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5154         value_prev = memoryview(v[:0])
5155
5156         while len(v) > 0:
5157             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5158                 break
5159             for name, spec in self._specs_items():
5160                 sub_decode_path = decode_path + (name,)
5161                 try:
5162                     spec.decode(
5163                         v,
5164                         sub_offset,
5165                         leavemm=True,
5166                         decode_path=sub_decode_path,
5167                         ctx=ctx,
5168                         tag_only=True,
5169                         _ctx_immutable=False,
5170                     )
5171                 except TagMismatch:
5172                     continue
5173                 break
5174             else:
5175                 raise TagMismatch(
5176                     klass=self.__class__,
5177                     decode_path=decode_path,
5178                     offset=offset,
5179                 )
5180             value, v_tail = spec.decode(
5181                 v,
5182                 sub_offset,
5183                 leavemm=True,
5184                 decode_path=sub_decode_path,
5185                 ctx=ctx,
5186                 _ctx_immutable=False,
5187             )
5188             value_len = value.fulllen
5189             if value_prev.tobytes() > v[:value_len].tobytes():
5190                 if ctx_bered or ctx_allow_unordered_set:
5191                     ber_encoded = True
5192                 else:
5193                     raise DecodeError(
5194                         "unordered " + self.asn1_type_name,
5195                         klass=self.__class__,
5196                         decode_path=sub_decode_path,
5197                         offset=sub_offset,
5198                     )
5199             if spec.default is None or value != spec.default:
5200                 pass
5201             elif ctx_bered or ctx_allow_default_values:
5202                 ber_encoded = True
5203             else:
5204                 raise DecodeError(
5205                     "DEFAULT value met",
5206                     klass=self.__class__,
5207                     decode_path=sub_decode_path,
5208                     offset=sub_offset,
5209                 )
5210             values[name] = value
5211             value_prev = v[:value_len]
5212             sub_offset += value_len
5213             vlen += value_len
5214             v = v_tail
5215         obj = self.__class__(
5216             schema=self.specs,
5217             impl=self.tag,
5218             expl=self._expl,
5219             default=self.default,
5220             optional=self.optional,
5221             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5222         )
5223         if lenindef:
5224             if v[:EOC_LEN].tobytes() != EOC:
5225                 raise DecodeError(
5226                     "no EOC",
5227                     klass=self.__class__,
5228                     decode_path=decode_path,
5229                     offset=offset,
5230                 )
5231             tail = v[EOC_LEN:]
5232             obj.lenindef = True
5233         obj._value = values
5234         if not obj.ready:
5235             raise DecodeError(
5236                 "not all values are ready",
5237                 klass=self.__class__,
5238                 decode_path=decode_path,
5239                 offset=offset,
5240             )
5241         obj.ber_encoded = ber_encoded
5242         return obj, tail
5243
5244
5245 class SequenceOf(Obj):
5246     """``SEQUENCE OF`` sequence type
5247
5248     For that kind of type you must specify the object it will carry on
5249     (bounds are for example here, not required)::
5250
5251         class Ints(SequenceOf):
5252             schema = Integer()
5253             bounds = (0, 2)
5254
5255     >>> ints = Ints()
5256     >>> ints.append(Integer(123))
5257     >>> ints.append(Integer(234))
5258     >>> ints
5259     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5260     >>> [int(i) for i in ints]
5261     [123, 234]
5262     >>> ints.append(Integer(345))
5263     Traceback (most recent call last):
5264     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5265     >>> ints[1]
5266     INTEGER 234
5267     >>> ints[1] = Integer(345)
5268     >>> ints
5269     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5270
5271     Also you can initialize sequence with preinitialized values:
5272
5273     >>> ints = Ints([Integer(123), Integer(234)])
5274     """
5275     __slots__ = ("spec", "_bound_min", "_bound_max")
5276     tag_default = tag_encode(form=TagFormConstructed, num=16)
5277     asn1_type_name = "SEQUENCE OF"
5278
5279     def __init__(
5280             self,
5281             value=None,
5282             schema=None,
5283             bounds=None,
5284             impl=None,
5285             expl=None,
5286             default=None,
5287             optional=False,
5288             _decoded=(0, 0, 0),
5289     ):
5290         super(SequenceOf, self).__init__(
5291             impl,
5292             expl,
5293             default,
5294             optional,
5295             _decoded,
5296         )
5297         if schema is None:
5298             schema = getattr(self, "schema", None)
5299         if schema is None:
5300             raise ValueError("schema must be specified")
5301         self.spec = schema
5302         self._bound_min, self._bound_max = getattr(
5303             self,
5304             "bounds",
5305             (0, float("+inf")),
5306         ) if bounds is None else bounds
5307         self._value = []
5308         if value is not None:
5309             self._value = self._value_sanitize(value)
5310         if default is not None:
5311             default_value = self._value_sanitize(default)
5312             default_obj = self.__class__(
5313                 schema=schema,
5314                 impl=self.tag,
5315                 expl=self._expl,
5316             )
5317             default_obj._value = default_value
5318             self.default = default_obj
5319             if value is None:
5320                 self._value = default_obj.copy()._value
5321
5322     def _value_sanitize(self, value):
5323         if issubclass(value.__class__, SequenceOf):
5324             value = value._value
5325         elif hasattr(value, "__iter__"):
5326             value = list(value)
5327         else:
5328             raise InvalidValueType((self.__class__, iter))
5329         if not self._bound_min <= len(value) <= self._bound_max:
5330             raise BoundsError(self._bound_min, len(value), self._bound_max)
5331         for v in value:
5332             if not isinstance(v, self.spec.__class__):
5333                 raise InvalidValueType((self.spec.__class__,))
5334         return value
5335
5336     @property
5337     def ready(self):
5338         return all(v.ready for v in self._value)
5339
5340     @property
5341     def bered(self):
5342         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5343             return True
5344         return any(v.bered for v in self._value)
5345
5346     def copy(self):
5347         obj = self.__class__(schema=self.spec)
5348         obj._bound_min = self._bound_min
5349         obj._bound_max = self._bound_max
5350         obj.tag = self.tag
5351         obj._expl = self._expl
5352         obj.default = self.default
5353         obj.optional = self.optional
5354         obj.offset = self.offset
5355         obj.llen = self.llen
5356         obj.vlen = self.vlen
5357         obj.expl_lenindef = self.expl_lenindef
5358         obj.lenindef = self.lenindef
5359         obj.ber_encoded = self.ber_encoded
5360         obj._value = [v.copy() for v in self._value]
5361         return obj
5362
5363     def __eq__(self, their):
5364         if isinstance(their, self.__class__):
5365             return (
5366                 self.spec == their.spec and
5367                 self.tag == their.tag and
5368                 self._expl == their._expl and
5369                 self._value == their._value
5370             )
5371         if hasattr(their, "__iter__"):
5372             return self._value == list(their)
5373         return False
5374
5375     def __call__(
5376             self,
5377             value=None,
5378             bounds=None,
5379             impl=None,
5380             expl=None,
5381             default=None,
5382             optional=None,
5383     ):
5384         return self.__class__(
5385             value=value,
5386             schema=self.spec,
5387             bounds=(
5388                 (self._bound_min, self._bound_max)
5389                 if bounds is None else bounds
5390             ),
5391             impl=self.tag if impl is None else impl,
5392             expl=self._expl if expl is None else expl,
5393             default=self.default if default is None else default,
5394             optional=self.optional if optional is None else optional,
5395         )
5396
5397     def __contains__(self, key):
5398         return key in self._value
5399
5400     def append(self, value):
5401         if not isinstance(value, self.spec.__class__):
5402             raise InvalidValueType((self.spec.__class__,))
5403         if len(self._value) + 1 > self._bound_max:
5404             raise BoundsError(
5405                 self._bound_min,
5406                 len(self._value) + 1,
5407                 self._bound_max,
5408             )
5409         self._value.append(value)
5410
5411     def __iter__(self):
5412         self._assert_ready()
5413         return iter(self._value)
5414
5415     def __len__(self):
5416         self._assert_ready()
5417         return len(self._value)
5418
5419     def __setitem__(self, key, value):
5420         if not isinstance(value, self.spec.__class__):
5421             raise InvalidValueType((self.spec.__class__,))
5422         self._value[key] = self.spec(value=value)
5423
5424     def __getitem__(self, key):
5425         return self._value[key]
5426
5427     def _encoded_values(self):
5428         return [v.encode() for v in self._value]
5429
5430     def _encode(self):
5431         v = b"".join(self._encoded_values())
5432         return b"".join((self.tag, len_encode(len(v)), v))
5433
5434     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5435         try:
5436             t, tlen, lv = tag_strip(tlv)
5437         except DecodeError as err:
5438             raise err.__class__(
5439                 msg=err.msg,
5440                 klass=self.__class__,
5441                 decode_path=decode_path,
5442                 offset=offset,
5443             )
5444         if t != self.tag:
5445             raise TagMismatch(
5446                 klass=self.__class__,
5447                 decode_path=decode_path,
5448                 offset=offset,
5449             )
5450         if tag_only:
5451             return None
5452         lenindef = False
5453         ctx_bered = ctx.get("bered", False)
5454         try:
5455             l, llen, v = len_decode(lv)
5456         except LenIndefForm as err:
5457             if not ctx_bered:
5458                 raise err.__class__(
5459                     msg=err.msg,
5460                     klass=self.__class__,
5461                     decode_path=decode_path,
5462                     offset=offset,
5463                 )
5464             l, llen, v = 0, 1, lv[1:]
5465             lenindef = True
5466         except DecodeError as err:
5467             raise err.__class__(
5468                 msg=err.msg,
5469                 klass=self.__class__,
5470                 decode_path=decode_path,
5471                 offset=offset,
5472             )
5473         if l > len(v):
5474             raise NotEnoughData(
5475                 "encoded length is longer than data",
5476                 klass=self.__class__,
5477                 decode_path=decode_path,
5478                 offset=offset,
5479             )
5480         if not lenindef:
5481             v, tail = v[:l], v[l:]
5482         vlen = 0
5483         sub_offset = offset + tlen + llen
5484         _value = []
5485         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5486         value_prev = memoryview(v[:0])
5487         ber_encoded = False
5488         spec = self.spec
5489         while len(v) > 0:
5490             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5491                 break
5492             sub_decode_path = decode_path + (str(len(_value)),)
5493             value, v_tail = spec.decode(
5494                 v,
5495                 sub_offset,
5496                 leavemm=True,
5497                 decode_path=sub_decode_path,
5498                 ctx=ctx,
5499                 _ctx_immutable=False,
5500             )
5501             value_len = value.fulllen
5502             if ordering_check:
5503                 if value_prev.tobytes() > v[:value_len].tobytes():
5504                     if ctx_bered or ctx_allow_unordered_set:
5505                         ber_encoded = True
5506                     else:
5507                         raise DecodeError(
5508                             "unordered " + self.asn1_type_name,
5509                             klass=self.__class__,
5510                             decode_path=sub_decode_path,
5511                             offset=sub_offset,
5512                         )
5513                 value_prev = v[:value_len]
5514             _value.append(value)
5515             sub_offset += value_len
5516             vlen += value_len
5517             v = v_tail
5518         try:
5519             obj = self.__class__(
5520                 value=_value,
5521                 schema=spec,
5522                 bounds=(self._bound_min, self._bound_max),
5523                 impl=self.tag,
5524                 expl=self._expl,
5525                 default=self.default,
5526                 optional=self.optional,
5527                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5528             )
5529         except BoundsError as err:
5530             raise DecodeError(
5531                 msg=str(err),
5532                 klass=self.__class__,
5533                 decode_path=decode_path,
5534                 offset=offset,
5535             )
5536         if lenindef:
5537             if v[:EOC_LEN].tobytes() != EOC:
5538                 raise DecodeError(
5539                     "no EOC",
5540                     klass=self.__class__,
5541                     decode_path=decode_path,
5542                     offset=offset,
5543                 )
5544             obj.lenindef = True
5545             tail = v[EOC_LEN:]
5546         obj.ber_encoded = ber_encoded
5547         return obj, tail
5548
5549     def __repr__(self):
5550         return "%s[%s]" % (
5551             pp_console_row(next(self.pps())),
5552             ", ".join(repr(v) for v in self._value),
5553         )
5554
5555     def pps(self, decode_path=()):
5556         yield _pp(
5557             obj=self,
5558             asn1_type_name=self.asn1_type_name,
5559             obj_name=self.__class__.__name__,
5560             decode_path=decode_path,
5561             optional=self.optional,
5562             default=self == self.default,
5563             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5564             expl=None if self._expl is None else tag_decode(self._expl),
5565             offset=self.offset,
5566             tlen=self.tlen,
5567             llen=self.llen,
5568             vlen=self.vlen,
5569             expl_offset=self.expl_offset if self.expled else None,
5570             expl_tlen=self.expl_tlen if self.expled else None,
5571             expl_llen=self.expl_llen if self.expled else None,
5572             expl_vlen=self.expl_vlen if self.expled else None,
5573             expl_lenindef=self.expl_lenindef,
5574             lenindef=self.lenindef,
5575             ber_encoded=self.ber_encoded,
5576             bered=self.bered,
5577         )
5578         for i, value in enumerate(self._value):
5579             yield value.pps(decode_path=decode_path + (str(i),))
5580         for pp in self.pps_lenindef(decode_path):
5581             yield pp
5582
5583
5584 class SetOf(SequenceOf):
5585     """``SET OF`` sequence type
5586
5587     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5588     """
5589     __slots__ = ()
5590     tag_default = tag_encode(form=TagFormConstructed, num=17)
5591     asn1_type_name = "SET OF"
5592
5593     def _encode(self):
5594         raws = self._encoded_values()
5595         raws.sort()
5596         v = b"".join(raws)
5597         return b"".join((self.tag, len_encode(len(v)), v))
5598
5599     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5600         return super(SetOf, self)._decode(
5601             tlv,
5602             offset,
5603             decode_path,
5604             ctx,
5605             tag_only,
5606             ordering_check=True,
5607         )
5608
5609
5610 def obj_by_path(pypath):  # pragma: no cover
5611     """Import object specified as string Python path
5612
5613     Modules must be separated from classes/functions with ``:``.
5614
5615     >>> obj_by_path("foo.bar:Baz")
5616     <class 'foo.bar.Baz'>
5617     >>> obj_by_path("foo.bar:Baz.boo")
5618     <classmethod 'foo.bar.Baz.boo'>
5619     """
5620     mod, objs = pypath.rsplit(":", 1)
5621     from importlib import import_module
5622     obj = import_module(mod)
5623     for obj_name in objs.split("."):
5624         obj = getattr(obj, obj_name)
5625     return obj
5626
5627
5628 def generic_decoder():  # pragma: no cover
5629     # All of this below is a big hack with self references
5630     choice = PrimitiveTypes()
5631     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5632     choice.specs["SetOf"] = SetOf(schema=choice)
5633     for i in six_xrange(31):
5634         choice.specs["SequenceOf%d" % i] = SequenceOf(
5635             schema=choice,
5636             expl=tag_ctxc(i),
5637         )
5638     choice.specs["Any"] = Any()
5639
5640     # Class name equals to type name, to omit it from output
5641     class SEQUENCEOF(SequenceOf):
5642         __slots__ = ()
5643         schema = choice
5644
5645     def pprint_any(
5646             obj,
5647             oid_maps=(),
5648             with_colours=False,
5649             with_decode_path=False,
5650             decode_path_only=(),
5651     ):
5652         def _pprint_pps(pps):
5653             for pp in pps:
5654                 if hasattr(pp, "_fields"):
5655                     if (
5656                         decode_path_only != () and
5657                         pp.decode_path[:len(decode_path_only)] != decode_path_only
5658                     ):
5659                         continue
5660                     if pp.asn1_type_name == Choice.asn1_type_name:
5661                         continue
5662                     pp_kwargs = pp._asdict()
5663                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5664                     pp = _pp(**pp_kwargs)
5665                     yield pp_console_row(
5666                         pp,
5667                         oid_maps=oid_maps,
5668                         with_offsets=True,
5669                         with_blob=False,
5670                         with_colours=with_colours,
5671                         with_decode_path=with_decode_path,
5672                         decode_path_len_decrease=len(decode_path_only),
5673                     )
5674                     for row in pp_console_blob(
5675                         pp,
5676                         decode_path_len_decrease=len(decode_path_only),
5677                     ):
5678                         yield row
5679                 else:
5680                     for row in _pprint_pps(pp):
5681                         yield row
5682         return "\n".join(_pprint_pps(obj.pps()))
5683     return SEQUENCEOF(), pprint_any
5684
5685
5686 def main():  # pragma: no cover
5687     import argparse
5688     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5689     parser.add_argument(
5690         "--skip",
5691         type=int,
5692         default=0,
5693         help="Skip that number of bytes from the beginning",
5694     )
5695     parser.add_argument(
5696         "--oids",
5697         help="Python paths to dictionary with OIDs, comma separated",
5698     )
5699     parser.add_argument(
5700         "--schema",
5701         help="Python path to schema definition to use",
5702     )
5703     parser.add_argument(
5704         "--defines-by-path",
5705         help="Python path to decoder's defines_by_path",
5706     )
5707     parser.add_argument(
5708         "--nobered",
5709         action="store_true",
5710         help="Disallow BER encoding",
5711     )
5712     parser.add_argument(
5713         "--print-decode-path",
5714         action="store_true",
5715         help="Print decode paths",
5716     )
5717     parser.add_argument(
5718         "--decode-path-only",
5719         help="Print only specified decode path",
5720     )
5721     parser.add_argument(
5722         "--allow-expl-oob",
5723         action="store_true",
5724         help="Allow explicit tag out-of-bound",
5725     )
5726     parser.add_argument(
5727         "DERFile",
5728         type=argparse.FileType("rb"),
5729         help="Path to DER file you want to decode",
5730     )
5731     args = parser.parse_args()
5732     args.DERFile.seek(args.skip)
5733     der = memoryview(args.DERFile.read())
5734     args.DERFile.close()
5735     oid_maps = (
5736         [obj_by_path(_path) for _path in (args.oids or "").split(",")]
5737         if args.oids else ()
5738     )
5739     if args.schema:
5740         schema = obj_by_path(args.schema)
5741         from functools import partial
5742         pprinter = partial(pprint, big_blobs=True)
5743     else:
5744         schema, pprinter = generic_decoder()
5745     ctx = {
5746         "bered": not args.nobered,
5747         "allow_expl_oob": args.allow_expl_oob,
5748     }
5749     if args.defines_by_path is not None:
5750         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5751     obj, tail = schema().decode(der, ctx=ctx)
5752     print(pprinter(
5753         obj,
5754         oid_maps=oid_maps,
5755         with_colours=environ.get("NO_COLOR") is None,
5756         with_decode_path=args.print_decode_path,
5757         decode_path_only=(
5758             () if args.decode_path_only is None else
5759             tuple(args.decode_path_only.split(":"))
5760         ),
5761     ))
5762     if tail != b"":
5763         print("\nTrailing data: %s" % hexenc(tail))
5764
5765
5766 if __name__ == "__main__":
5767     main()