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