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