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