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