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