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