]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
PrintableString'es .allow_asterisk and .allow_ampersand properties
[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     >>> obj = PrintableString("foo*bar", allow_asterisk=True)
3995     PrintableString PrintableString foo*bar
3996     >>> obj.allow_asterisk, obj.allow_ampersand
3997     (True, False)
3998     """
3999     __slots__ = ()
4000     tag_default = tag_encode(19)
4001     encoding = "ascii"
4002     asn1_type_name = "PrintableString"
4003     _allowable_chars = frozenset(
4004         (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4005     )
4006     _asterisk = frozenset("*".encode("ascii"))
4007     _ampersand = frozenset("&".encode("ascii"))
4008
4009     def __init__(
4010             self,
4011             value=None,
4012             bounds=None,
4013             impl=None,
4014             expl=None,
4015             default=None,
4016             optional=False,
4017             _decoded=(0, 0, 0),
4018             allow_asterisk=False,
4019             allow_ampersand=False,
4020     ):
4021         """
4022         :param allow_asterisk: allow asterisk character
4023         :param allow_ampersand: allow ampersand character
4024         """
4025         if allow_asterisk:
4026             self._allowable_chars |= self._asterisk
4027         if allow_ampersand:
4028             self._allowable_chars |= self._ampersand
4029         super(PrintableString, self).__init__(
4030             value, bounds, impl, expl, default, optional, _decoded,
4031         )
4032
4033     @property
4034     def allow_asterisk(self):
4035         """Is asterisk character allowed?
4036         """
4037         return self._asterisk <= self._allowable_chars
4038
4039     @property
4040     def allow_ampersand(self):
4041         """Is ampersand character allowed?
4042         """
4043         return self._ampersand <= self._allowable_chars
4044
4045     def _value_sanitize(self, value):
4046         value = super(PrintableString, self)._value_sanitize(value)
4047         if not frozenset(value) <= self._allowable_chars:
4048             raise DecodeError("non-printable value")
4049         return value
4050
4051     def __getstate__(self):
4052         return PrintableStringState(
4053             *super(PrintableString, self).__getstate__(),
4054             **{"allowable_chars": self._allowable_chars}
4055         )
4056
4057     def __setstate__(self, state):
4058         super(PrintableString, self).__setstate__(state)
4059         self._allowable_chars = state.allowable_chars
4060
4061     def __call__(
4062             self,
4063             value=None,
4064             bounds=None,
4065             impl=None,
4066             expl=None,
4067             default=None,
4068             optional=None,
4069     ):
4070         return self.__class__(
4071             value=value,
4072             bounds=(
4073                 (self._bound_min, self._bound_max)
4074                 if bounds is None else bounds
4075             ),
4076             impl=self.tag if impl is None else impl,
4077             expl=self._expl if expl is None else expl,
4078             default=self.default if default is None else default,
4079             optional=self.optional if optional is None else optional,
4080             allow_asterisk=self.allow_asterisk,
4081             allow_ampersand=self.allow_ampersand,
4082         )
4083
4084
4085 class TeletexString(CommonString):
4086     __slots__ = ()
4087     tag_default = tag_encode(20)
4088     encoding = "ascii"
4089     asn1_type_name = "TeletexString"
4090
4091
4092 class T61String(TeletexString):
4093     __slots__ = ()
4094     asn1_type_name = "T61String"
4095
4096
4097 class VideotexString(CommonString):
4098     __slots__ = ()
4099     tag_default = tag_encode(21)
4100     encoding = "iso-8859-1"
4101     asn1_type_name = "VideotexString"
4102
4103
4104 class IA5String(CommonString):
4105     __slots__ = ()
4106     tag_default = tag_encode(22)
4107     encoding = "ascii"
4108     asn1_type_name = "IA5"
4109
4110
4111 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4112 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4113 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4114
4115
4116 class UTCTime(CommonString):
4117     """``UTCTime`` datetime type
4118
4119     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4120     UTCTime UTCTime 2017-09-30T22:07:50
4121     >>> str(t)
4122     '170930220750Z'
4123     >>> bytes(t)
4124     b'170930220750Z'
4125     >>> t.todatetime()
4126     datetime.datetime(2017, 9, 30, 22, 7, 50)
4127     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
4128     datetime.datetime(1957, 9, 30, 22, 7, 50)
4129
4130     .. warning::
4131
4132        BER encoding is unsupported.
4133     """
4134     __slots__ = ()
4135     tag_default = tag_encode(23)
4136     encoding = "ascii"
4137     asn1_type_name = "UTCTime"
4138
4139     def __init__(
4140             self,
4141             value=None,
4142             impl=None,
4143             expl=None,
4144             default=None,
4145             optional=False,
4146             _decoded=(0, 0, 0),
4147             bounds=None,  # dummy argument, workability for OctetString.decode
4148     ):
4149         """
4150         :param value: set the value. Either datetime type, or
4151                       :py:class:`pyderasn.UTCTime` object
4152         :param bytes impl: override default tag with ``IMPLICIT`` one
4153         :param bytes expl: override default tag with ``EXPLICIT`` one
4154         :param default: set default value. Type same as in ``value``
4155         :param bool optional: is object ``OPTIONAL`` in sequence
4156         """
4157         super(UTCTime, self).__init__(
4158             None, None, impl, expl, default, optional, _decoded,
4159         )
4160         self._value = value
4161         if value is not None:
4162             self._value = self._value_sanitize(value)
4163         if default is not None:
4164             default = self._value_sanitize(default)
4165             self.default = self.__class__(
4166                 value=default,
4167                 impl=self.tag,
4168                 expl=self._expl,
4169             )
4170             if self._value is None:
4171                 self._value = default
4172
4173     def _strptime(self, value):
4174         # datetime.strptime's format: %y%m%d%H%M%SZ
4175         if len(value) != LEN_YYMMDDHHMMSSZ:
4176             raise ValueError("invalid UTCTime length")
4177         if value[-1] != "Z":
4178             raise ValueError("non UTC timezone")
4179         return datetime(
4180             2000 + int(value[:2]),  # %y
4181             int(value[2:4]),  # %m
4182             int(value[4:6]),  # %d
4183             int(value[6:8]),  # %H
4184             int(value[8:10]),  # %M
4185             int(value[10:12]),  # %S
4186         )
4187
4188     def _value_sanitize(self, value):
4189         if isinstance(value, binary_type):
4190             try:
4191                 value_decoded = value.decode("ascii")
4192             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4193                 raise DecodeError("invalid UTCTime encoding: %r" % err)
4194             try:
4195                 self._strptime(value_decoded)
4196             except (TypeError, ValueError) as err:
4197                 raise DecodeError("invalid UTCTime format: %r" % err)
4198             return value
4199         if isinstance(value, self.__class__):
4200             return value._value
4201         if isinstance(value, datetime):
4202             return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
4203         raise InvalidValueType((self.__class__, datetime))
4204
4205     def __eq__(self, their):
4206         if isinstance(their, binary_type):
4207             return self._value == their
4208         if isinstance(their, datetime):
4209             return self.todatetime() == their
4210         if not isinstance(their, self.__class__):
4211             return False
4212         return (
4213             self._value == their._value and
4214             self.tag == their.tag and
4215             self._expl == their._expl
4216         )
4217
4218     def todatetime(self):
4219         """Convert to datetime
4220
4221         :returns: datetime
4222
4223         Pay attention that UTCTime can not hold full year, so all years
4224         having < 50 years are treated as 20xx, 19xx otherwise, according
4225         to X.509 recomendation.
4226         """
4227         value = self._strptime(self._value.decode("ascii"))
4228         year = value.year % 100
4229         return datetime(
4230             year=(2000 + year) if year < 50 else (1900 + year),
4231             month=value.month,
4232             day=value.day,
4233             hour=value.hour,
4234             minute=value.minute,
4235             second=value.second,
4236         )
4237
4238     def __repr__(self):
4239         return pp_console_row(next(self.pps()))
4240
4241     def pps(self, decode_path=()):
4242         yield _pp(
4243             obj=self,
4244             asn1_type_name=self.asn1_type_name,
4245             obj_name=self.__class__.__name__,
4246             decode_path=decode_path,
4247             value=self.todatetime().isoformat() if self.ready else None,
4248             optional=self.optional,
4249             default=self == self.default,
4250             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4251             expl=None if self._expl is None else tag_decode(self._expl),
4252             offset=self.offset,
4253             tlen=self.tlen,
4254             llen=self.llen,
4255             vlen=self.vlen,
4256             expl_offset=self.expl_offset if self.expled else None,
4257             expl_tlen=self.expl_tlen if self.expled else None,
4258             expl_llen=self.expl_llen if self.expled else None,
4259             expl_vlen=self.expl_vlen if self.expled else None,
4260             expl_lenindef=self.expl_lenindef,
4261             ber_encoded=self.ber_encoded,
4262             bered=self.bered,
4263         )
4264         for pp in self.pps_lenindef(decode_path):
4265             yield pp
4266
4267
4268 class GeneralizedTime(UTCTime):
4269     """``GeneralizedTime`` datetime type
4270
4271     This type is similar to :py:class:`pyderasn.UTCTime`.
4272
4273     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4274     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
4275     >>> str(t)
4276     '20170930220750.000123Z'
4277     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
4278     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
4279
4280     .. warning::
4281
4282        BER encoding is unsupported.
4283
4284     .. warning::
4285
4286        Only microsecond fractions are supported.
4287        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
4288        higher precision values.
4289     """
4290     __slots__ = ()
4291     tag_default = tag_encode(24)
4292     asn1_type_name = "GeneralizedTime"
4293
4294     def _strptime(self, value):
4295         l = len(value)
4296         if l == LEN_YYYYMMDDHHMMSSZ:
4297             # datetime.strptime's format: %y%m%d%H%M%SZ
4298             if value[-1] != "Z":
4299                 raise ValueError("non UTC timezone")
4300             return datetime(
4301                 int(value[:4]),  # %Y
4302                 int(value[4:6]),  # %m
4303                 int(value[6:8]),  # %d
4304                 int(value[8:10]),  # %H
4305                 int(value[10:12]),  # %M
4306                 int(value[12:14]),  # %S
4307             )
4308         if l >= LEN_YYYYMMDDHHMMSSDMZ:
4309             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4310             if value[-1] != "Z":
4311                 raise ValueError("non UTC timezone")
4312             if value[14] != ".":
4313                 raise ValueError("no fractions separator")
4314             us = value[15:-1]
4315             if us[-1] == "0":
4316                 raise ValueError("trailing zero")
4317             us_len = len(us)
4318             if us_len > 6:
4319                 raise ValueError("only microsecond fractions are supported")
4320             us = int(us + ("0" * (6 - us_len)))
4321             decoded = datetime(
4322                 int(value[:4]),  # %Y
4323                 int(value[4:6]),  # %m
4324                 int(value[6:8]),  # %d
4325                 int(value[8:10]),  # %H
4326                 int(value[10:12]),  # %M
4327                 int(value[12:14]),  # %S
4328                 us,  # %f
4329             )
4330             return decoded
4331         raise ValueError("invalid GeneralizedTime length")
4332
4333     def _value_sanitize(self, value):
4334         if isinstance(value, binary_type):
4335             try:
4336                 value_decoded = value.decode("ascii")
4337             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4338                 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
4339             try:
4340                 self._strptime(value_decoded)
4341             except (TypeError, ValueError) as err:
4342                 raise DecodeError(
4343                     "invalid GeneralizedTime format: %r" % err,
4344                     klass=self.__class__,
4345                 )
4346             return value
4347         if isinstance(value, self.__class__):
4348             return value._value
4349         if isinstance(value, datetime):
4350             encoded = value.strftime("%Y%m%d%H%M%S")
4351             if value.microsecond > 0:
4352                 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4353             return (encoded + "Z").encode("ascii")
4354         raise InvalidValueType((self.__class__, datetime))
4355
4356     def todatetime(self):
4357         return self._strptime(self._value.decode("ascii"))
4358
4359
4360 class GraphicString(CommonString):
4361     __slots__ = ()
4362     tag_default = tag_encode(25)
4363     encoding = "iso-8859-1"
4364     asn1_type_name = "GraphicString"
4365
4366
4367 class VisibleString(CommonString):
4368     __slots__ = ()
4369     tag_default = tag_encode(26)
4370     encoding = "ascii"
4371     asn1_type_name = "VisibleString"
4372
4373
4374 class ISO646String(VisibleString):
4375     __slots__ = ()
4376     asn1_type_name = "ISO646String"
4377
4378
4379 class GeneralString(CommonString):
4380     __slots__ = ()
4381     tag_default = tag_encode(27)
4382     encoding = "iso-8859-1"
4383     asn1_type_name = "GeneralString"
4384
4385
4386 class UniversalString(CommonString):
4387     __slots__ = ()
4388     tag_default = tag_encode(28)
4389     encoding = "utf-32-be"
4390     asn1_type_name = "UniversalString"
4391
4392
4393 class BMPString(CommonString):
4394     __slots__ = ()
4395     tag_default = tag_encode(30)
4396     encoding = "utf-16-be"
4397     asn1_type_name = "BMPString"
4398
4399
4400 ChoiceState = namedtuple("ChoiceState", (
4401     "version",
4402     "specs",
4403     "value",
4404     "tag",
4405     "expl",
4406     "default",
4407     "optional",
4408     "offset",
4409     "llen",
4410     "vlen",
4411     "expl_lenindef",
4412     "lenindef",
4413     "ber_encoded",
4414 ))
4415
4416
4417 class Choice(Obj):
4418     """``CHOICE`` special type
4419
4420     ::
4421
4422         class GeneralName(Choice):
4423             schema = (
4424                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4425                 ("dNSName", IA5String(impl=tag_ctxp(2))),
4426             )
4427
4428     >>> gn = GeneralName()
4429     GeneralName CHOICE
4430     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4431     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4432     >>> gn["dNSName"] = IA5String("bar.baz")
4433     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4434     >>> gn["rfc822Name"]
4435     None
4436     >>> gn["dNSName"]
4437     [2] IA5String IA5 bar.baz
4438     >>> gn.choice
4439     'dNSName'
4440     >>> gn.value == gn["dNSName"]
4441     True
4442     >>> gn.specs
4443     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4444
4445     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4446     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4447     """
4448     __slots__ = ("specs",)
4449     tag_default = None
4450     asn1_type_name = "CHOICE"
4451
4452     def __init__(
4453             self,
4454             value=None,
4455             schema=None,
4456             impl=None,
4457             expl=None,
4458             default=None,
4459             optional=False,
4460             _decoded=(0, 0, 0),
4461     ):
4462         """
4463         :param value: set the value. Either ``(choice, value)`` tuple, or
4464                       :py:class:`pyderasn.Choice` object
4465         :param bytes impl: can not be set, do **not** use it
4466         :param bytes expl: override default tag with ``EXPLICIT`` one
4467         :param default: set default value. Type same as in ``value``
4468         :param bool optional: is object ``OPTIONAL`` in sequence
4469         """
4470         if impl is not None:
4471             raise ValueError("no implicit tag allowed for CHOICE")
4472         super(Choice, self).__init__(None, expl, default, optional, _decoded)
4473         if schema is None:
4474             schema = getattr(self, "schema", ())
4475         if len(schema) == 0:
4476             raise ValueError("schema must be specified")
4477         self.specs = (
4478             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4479         )
4480         self._value = None
4481         if value is not None:
4482             self._value = self._value_sanitize(value)
4483         if default is not None:
4484             default_value = self._value_sanitize(default)
4485             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4486             default_obj.specs = self.specs
4487             default_obj._value = default_value
4488             self.default = default_obj
4489             if value is None:
4490                 self._value = copy(default_obj._value)
4491
4492     def _value_sanitize(self, value):
4493         if isinstance(value, tuple) and len(value) == 2:
4494             choice, obj = value
4495             spec = self.specs.get(choice)
4496             if spec is None:
4497                 raise ObjUnknown(choice)
4498             if not isinstance(obj, spec.__class__):
4499                 raise InvalidValueType((spec,))
4500             return (choice, spec(obj))
4501         if isinstance(value, self.__class__):
4502             return value._value
4503         raise InvalidValueType((self.__class__, tuple))
4504
4505     @property
4506     def ready(self):
4507         return self._value is not None and self._value[1].ready
4508
4509     @property
4510     def bered(self):
4511         return self.expl_lenindef or (
4512             (self._value is not None) and
4513             self._value[1].bered
4514         )
4515
4516     def __getstate__(self):
4517         return ChoiceState(
4518             __version__,
4519             self.specs,
4520             copy(self._value),
4521             self.tag,
4522             self._expl,
4523             self.default,
4524             self.optional,
4525             self.offset,
4526             self.llen,
4527             self.vlen,
4528             self.expl_lenindef,
4529             self.lenindef,
4530             self.ber_encoded,
4531         )
4532
4533     def __setstate__(self, state):
4534         super(Choice, self).__setstate__(state)
4535         self.specs = state.specs
4536         self._value = state.value
4537         self._expl = state.expl
4538         self.default = state.default
4539         self.optional = state.optional
4540         self.offset = state.offset
4541         self.llen = state.llen
4542         self.vlen = state.vlen
4543         self.expl_lenindef = state.expl_lenindef
4544         self.lenindef = state.lenindef
4545         self.ber_encoded = state.ber_encoded
4546
4547     def __eq__(self, their):
4548         if isinstance(their, tuple) and len(their) == 2:
4549             return self._value == their
4550         if not isinstance(their, self.__class__):
4551             return False
4552         return (
4553             self.specs == their.specs and
4554             self._value == their._value
4555         )
4556
4557     def __call__(
4558             self,
4559             value=None,
4560             expl=None,
4561             default=None,
4562             optional=None,
4563     ):
4564         return self.__class__(
4565             value=value,
4566             schema=self.specs,
4567             expl=self._expl if expl is None else expl,
4568             default=self.default if default is None else default,
4569             optional=self.optional if optional is None else optional,
4570         )
4571
4572     @property
4573     def choice(self):
4574         self._assert_ready()
4575         return self._value[0]
4576
4577     @property
4578     def value(self):
4579         self._assert_ready()
4580         return self._value[1]
4581
4582     def __getitem__(self, key):
4583         if key not in self.specs:
4584             raise ObjUnknown(key)
4585         if self._value is None:
4586             return None
4587         choice, value = self._value
4588         if choice != key:
4589             return None
4590         return value
4591
4592     def __setitem__(self, key, value):
4593         spec = self.specs.get(key)
4594         if spec is None:
4595             raise ObjUnknown(key)
4596         if not isinstance(value, spec.__class__):
4597             raise InvalidValueType((spec.__class__,))
4598         self._value = (key, spec(value))
4599
4600     @property
4601     def tlen(self):
4602         return 0
4603
4604     @property
4605     def decoded(self):
4606         return self._value[1].decoded if self.ready else False
4607
4608     def _encode(self):
4609         self._assert_ready()
4610         return self._value[1].encode()
4611
4612     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4613         for choice, spec in iteritems(self.specs):
4614             sub_decode_path = decode_path + (choice,)
4615             try:
4616                 spec.decode(
4617                     tlv,
4618                     offset=offset,
4619                     leavemm=True,
4620                     decode_path=sub_decode_path,
4621                     ctx=ctx,
4622                     tag_only=True,
4623                     _ctx_immutable=False,
4624                 )
4625             except TagMismatch:
4626                 continue
4627             break
4628         else:
4629             raise TagMismatch(
4630                 klass=self.__class__,
4631                 decode_path=decode_path,
4632                 offset=offset,
4633             )
4634         if tag_only:  # pragma: no cover
4635             return None
4636         value, tail = spec.decode(
4637             tlv,
4638             offset=offset,
4639             leavemm=True,
4640             decode_path=sub_decode_path,
4641             ctx=ctx,
4642             _ctx_immutable=False,
4643         )
4644         obj = self.__class__(
4645             schema=self.specs,
4646             expl=self._expl,
4647             default=self.default,
4648             optional=self.optional,
4649             _decoded=(offset, 0, value.fulllen),
4650         )
4651         obj._value = (choice, value)
4652         return obj, tail
4653
4654     def __repr__(self):
4655         value = pp_console_row(next(self.pps()))
4656         if self.ready:
4657             value = "%s[%r]" % (value, self.value)
4658         return value
4659
4660     def pps(self, decode_path=()):
4661         yield _pp(
4662             obj=self,
4663             asn1_type_name=self.asn1_type_name,
4664             obj_name=self.__class__.__name__,
4665             decode_path=decode_path,
4666             value=self.choice if self.ready else None,
4667             optional=self.optional,
4668             default=self == self.default,
4669             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4670             expl=None if self._expl is None else tag_decode(self._expl),
4671             offset=self.offset,
4672             tlen=self.tlen,
4673             llen=self.llen,
4674             vlen=self.vlen,
4675             expl_lenindef=self.expl_lenindef,
4676             bered=self.bered,
4677         )
4678         if self.ready:
4679             yield self.value.pps(decode_path=decode_path + (self.choice,))
4680         for pp in self.pps_lenindef(decode_path):
4681             yield pp
4682
4683
4684 class PrimitiveTypes(Choice):
4685     """Predefined ``CHOICE`` for all generic primitive types
4686
4687     It could be useful for general decoding of some unspecified values:
4688
4689     >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
4690     OCTET STRING 3 bytes 666f6f
4691     >>> PrimitiveTypes().decod(hexdec("0203123456")).value
4692     INTEGER 1193046
4693     """
4694     __slots__ = ()
4695     schema = tuple((klass.__name__, klass()) for klass in (
4696         Boolean,
4697         Integer,
4698         BitString,
4699         OctetString,
4700         Null,
4701         ObjectIdentifier,
4702         UTF8String,
4703         NumericString,
4704         PrintableString,
4705         TeletexString,
4706         VideotexString,
4707         IA5String,
4708         UTCTime,
4709         GeneralizedTime,
4710         GraphicString,
4711         VisibleString,
4712         ISO646String,
4713         GeneralString,
4714         UniversalString,
4715         BMPString,
4716     ))
4717
4718
4719 AnyState = namedtuple("AnyState", (
4720     "version",
4721     "value",
4722     "tag",
4723     "expl",
4724     "optional",
4725     "offset",
4726     "llen",
4727     "vlen",
4728     "expl_lenindef",
4729     "lenindef",
4730     "ber_encoded",
4731     "defined",
4732 ))
4733
4734
4735 class Any(Obj):
4736     """``ANY`` special type
4737
4738     >>> Any(Integer(-123))
4739     ANY 020185
4740     >>> a = Any(OctetString(b"hello world").encode())
4741     ANY 040b68656c6c6f20776f726c64
4742     >>> hexenc(bytes(a))
4743     b'0x040x0bhello world'
4744     """
4745     __slots__ = ("defined",)
4746     tag_default = tag_encode(0)
4747     asn1_type_name = "ANY"
4748
4749     def __init__(
4750             self,
4751             value=None,
4752             expl=None,
4753             optional=False,
4754             _decoded=(0, 0, 0),
4755     ):
4756         """
4757         :param value: set the value. Either any kind of pyderasn's
4758                       **ready** object, or bytes. Pay attention that
4759                       **no** validation is performed is raw binary value
4760                       is valid TLV
4761         :param bytes expl: override default tag with ``EXPLICIT`` one
4762         :param bool optional: is object ``OPTIONAL`` in sequence
4763         """
4764         super(Any, self).__init__(None, expl, None, optional, _decoded)
4765         self._value = None if value is None else self._value_sanitize(value)
4766         self.defined = None
4767
4768     def _value_sanitize(self, value):
4769         if isinstance(value, binary_type):
4770             return value
4771         if isinstance(value, self.__class__):
4772             return value._value
4773         if isinstance(value, Obj):
4774             return value.encode()
4775         raise InvalidValueType((self.__class__, Obj, binary_type))
4776
4777     @property
4778     def ready(self):
4779         return self._value is not None
4780
4781     @property
4782     def bered(self):
4783         if self.expl_lenindef or self.lenindef:
4784             return True
4785         if self.defined is None:
4786             return False
4787         return self.defined[1].bered
4788
4789     def __getstate__(self):
4790         return AnyState(
4791             __version__,
4792             self._value,
4793             self.tag,
4794             self._expl,
4795             self.optional,
4796             self.offset,
4797             self.llen,
4798             self.vlen,
4799             self.expl_lenindef,
4800             self.lenindef,
4801             self.ber_encoded,
4802             self.defined,
4803         )
4804
4805     def __setstate__(self, state):
4806         super(Any, self).__setstate__(state)
4807         self._value = state.value
4808         self.tag = state.tag
4809         self._expl = state.expl
4810         self.optional = state.optional
4811         self.offset = state.offset
4812         self.llen = state.llen
4813         self.vlen = state.vlen
4814         self.expl_lenindef = state.expl_lenindef
4815         self.lenindef = state.lenindef
4816         self.ber_encoded = state.ber_encoded
4817         self.defined = state.defined
4818
4819     def __eq__(self, their):
4820         if isinstance(their, binary_type):
4821             return self._value == their
4822         if issubclass(their.__class__, Any):
4823             return self._value == their._value
4824         return False
4825
4826     def __call__(
4827             self,
4828             value=None,
4829             expl=None,
4830             optional=None,
4831     ):
4832         return self.__class__(
4833             value=value,
4834             expl=self._expl if expl is None else expl,
4835             optional=self.optional if optional is None else optional,
4836         )
4837
4838     def __bytes__(self):
4839         self._assert_ready()
4840         return self._value
4841
4842     @property
4843     def tlen(self):
4844         return 0
4845
4846     def _encode(self):
4847         self._assert_ready()
4848         return self._value
4849
4850     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4851         try:
4852             t, tlen, lv = tag_strip(tlv)
4853         except DecodeError as err:
4854             raise err.__class__(
4855                 msg=err.msg,
4856                 klass=self.__class__,
4857                 decode_path=decode_path,
4858                 offset=offset,
4859             )
4860         try:
4861             l, llen, v = len_decode(lv)
4862         except LenIndefForm as err:
4863             if not ctx.get("bered", False):
4864                 raise err.__class__(
4865                     msg=err.msg,
4866                     klass=self.__class__,
4867                     decode_path=decode_path,
4868                     offset=offset,
4869                 )
4870             llen, vlen, v = 1, 0, lv[1:]
4871             sub_offset = offset + tlen + llen
4872             chunk_i = 0
4873             while v[:EOC_LEN].tobytes() != EOC:
4874                 chunk, v = Any().decode(
4875                     v,
4876                     offset=sub_offset,
4877                     decode_path=decode_path + (str(chunk_i),),
4878                     leavemm=True,
4879                     ctx=ctx,
4880                     _ctx_immutable=False,
4881                 )
4882                 vlen += chunk.tlvlen
4883                 sub_offset += chunk.tlvlen
4884                 chunk_i += 1
4885             tlvlen = tlen + llen + vlen + EOC_LEN
4886             obj = self.__class__(
4887                 value=tlv[:tlvlen].tobytes(),
4888                 expl=self._expl,
4889                 optional=self.optional,
4890                 _decoded=(offset, 0, tlvlen),
4891             )
4892             obj.lenindef = True
4893             obj.tag = t.tobytes()
4894             return obj, v[EOC_LEN:]
4895         except DecodeError as err:
4896             raise err.__class__(
4897                 msg=err.msg,
4898                 klass=self.__class__,
4899                 decode_path=decode_path,
4900                 offset=offset,
4901             )
4902         if l > len(v):
4903             raise NotEnoughData(
4904                 "encoded length is longer than data",
4905                 klass=self.__class__,
4906                 decode_path=decode_path,
4907                 offset=offset,
4908             )
4909         tlvlen = tlen + llen + l
4910         v, tail = tlv[:tlvlen], v[l:]
4911         obj = self.__class__(
4912             value=v.tobytes(),
4913             expl=self._expl,
4914             optional=self.optional,
4915             _decoded=(offset, 0, tlvlen),
4916         )
4917         obj.tag = t.tobytes()
4918         return obj, tail
4919
4920     def __repr__(self):
4921         return pp_console_row(next(self.pps()))
4922
4923     def pps(self, decode_path=()):
4924         yield _pp(
4925             obj=self,
4926             asn1_type_name=self.asn1_type_name,
4927             obj_name=self.__class__.__name__,
4928             decode_path=decode_path,
4929             blob=self._value if self.ready else None,
4930             optional=self.optional,
4931             default=self == self.default,
4932             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4933             expl=None if self._expl is None else tag_decode(self._expl),
4934             offset=self.offset,
4935             tlen=self.tlen,
4936             llen=self.llen,
4937             vlen=self.vlen,
4938             expl_offset=self.expl_offset if self.expled else None,
4939             expl_tlen=self.expl_tlen if self.expled else None,
4940             expl_llen=self.expl_llen if self.expled else None,
4941             expl_vlen=self.expl_vlen if self.expled else None,
4942             expl_lenindef=self.expl_lenindef,
4943             lenindef=self.lenindef,
4944             bered=self.bered,
4945         )
4946         defined_by, defined = self.defined or (None, None)
4947         if defined_by is not None:
4948             yield defined.pps(
4949                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4950             )
4951         for pp in self.pps_lenindef(decode_path):
4952             yield pp
4953
4954
4955 ########################################################################
4956 # ASN.1 constructed types
4957 ########################################################################
4958
4959 def get_def_by_path(defines_by_path, sub_decode_path):
4960     """Get define by decode path
4961     """
4962     for path, define in defines_by_path:
4963         if len(path) != len(sub_decode_path):
4964             continue
4965         for p1, p2 in zip(path, sub_decode_path):
4966             if (p1 != any) and (p1 != p2):
4967                 break
4968         else:
4969             return define
4970
4971
4972 def abs_decode_path(decode_path, rel_path):
4973     """Create an absolute decode path from current and relative ones
4974
4975     :param decode_path: current decode path, starting point. Tuple of strings
4976     :param rel_path: relative path to ``decode_path``. Tuple of strings.
4977                      If first tuple's element is "/", then treat it as
4978                      an absolute path, ignoring ``decode_path`` as
4979                      starting point. Also this tuple can contain ".."
4980                      elements, stripping the leading element from
4981                      ``decode_path``
4982
4983     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4984     ("foo", "bar", "baz", "whatever")
4985     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4986     ("foo", "whatever")
4987     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4988     ("baz", "whatever")
4989     """
4990     if rel_path[0] == "/":
4991         return rel_path[1:]
4992     if rel_path[0] == "..":
4993         return abs_decode_path(decode_path[:-1], rel_path[1:])
4994     return decode_path + rel_path
4995
4996
4997 SequenceState = namedtuple("SequenceState", (
4998     "version",
4999     "specs",
5000     "value",
5001     "tag",
5002     "expl",
5003     "default",
5004     "optional",
5005     "offset",
5006     "llen",
5007     "vlen",
5008     "expl_lenindef",
5009     "lenindef",
5010     "ber_encoded",
5011 ))
5012
5013
5014 class Sequence(Obj):
5015     """``SEQUENCE`` structure type
5016
5017     You have to make specification of sequence::
5018
5019         class Extension(Sequence):
5020             schema = (
5021                 ("extnID", ObjectIdentifier()),
5022                 ("critical", Boolean(default=False)),
5023                 ("extnValue", OctetString()),
5024             )
5025
5026     Then, you can work with it as with dictionary.
5027
5028     >>> ext = Extension()
5029     >>> Extension().specs
5030     OrderedDict([
5031         ('extnID', OBJECT IDENTIFIER),
5032         ('critical', BOOLEAN False OPTIONAL DEFAULT),
5033         ('extnValue', OCTET STRING),
5034     ])
5035     >>> ext["extnID"] = "1.2.3"
5036     Traceback (most recent call last):
5037     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5038     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5039
5040     You can determine if sequence is ready to be encoded:
5041
5042     >>> ext.ready
5043     False
5044     >>> ext.encode()
5045     Traceback (most recent call last):
5046     pyderasn.ObjNotReady: object is not ready: extnValue
5047     >>> ext["extnValue"] = OctetString(b"foobar")
5048     >>> ext.ready
5049     True
5050
5051     Value you want to assign, must have the same **type** as in
5052     corresponding specification, but it can have different tags,
5053     optional/default attributes -- they will be taken from specification
5054     automatically::
5055
5056         class TBSCertificate(Sequence):
5057             schema = (
5058                 ("version", Version(expl=tag_ctxc(0), default="v1")),
5059             [...]
5060
5061     >>> tbs = TBSCertificate()
5062     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5063
5064     Assign ``None`` to remove value from sequence.
5065
5066     You can set values in Sequence during its initialization:
5067
5068     >>> AlgorithmIdentifier((
5069         ("algorithm", ObjectIdentifier("1.2.3")),
5070         ("parameters", Any(Null()))
5071     ))
5072     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5073
5074     You can determine if value exists/set in the sequence and take its value:
5075
5076     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5077     (True, True, False)
5078     >>> ext["extnID"]
5079     OBJECT IDENTIFIER 1.2.3
5080
5081     But pay attention that if value has default, then it won't be (not
5082     in) in the sequence (because ``DEFAULT`` must not be encoded in
5083     DER), but you can read its value:
5084
5085     >>> "critical" in ext, ext["critical"]
5086     (False, BOOLEAN False)
5087     >>> ext["critical"] = Boolean(True)
5088     >>> "critical" in ext, ext["critical"]
5089     (True, BOOLEAN True)
5090
5091     All defaulted values are always optional.
5092
5093     .. _allow_default_values_ctx:
5094
5095     DER prohibits default value encoding and will raise an error if
5096     default value is unexpectedly met during decode.
5097     If :ref:`bered <bered_ctx>` context option is set, then no error
5098     will be raised, but ``bered`` attribute set. You can disable strict
5099     defaulted values existence validation by setting
5100     ``"allow_default_values": True`` :ref:`context <ctx>` option.
5101
5102     Two sequences are equal if they have equal specification (schema),
5103     implicit/explicit tagging and the same values.
5104     """
5105     __slots__ = ("specs",)
5106     tag_default = tag_encode(form=TagFormConstructed, num=16)
5107     asn1_type_name = "SEQUENCE"
5108
5109     def __init__(
5110             self,
5111             value=None,
5112             schema=None,
5113             impl=None,
5114             expl=None,
5115             default=None,
5116             optional=False,
5117             _decoded=(0, 0, 0),
5118     ):
5119         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5120         if schema is None:
5121             schema = getattr(self, "schema", ())
5122         self.specs = (
5123             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
5124         )
5125         self._value = {}
5126         if value is not None:
5127             if issubclass(value.__class__, Sequence):
5128                 self._value = value._value
5129             elif hasattr(value, "__iter__"):
5130                 for seq_key, seq_value in value:
5131                     self[seq_key] = seq_value
5132             else:
5133                 raise InvalidValueType((Sequence,))
5134         if default is not None:
5135             if not issubclass(default.__class__, Sequence):
5136                 raise InvalidValueType((Sequence,))
5137             default_value = default._value
5138             default_obj = self.__class__(impl=self.tag, expl=self._expl)
5139             default_obj.specs = self.specs
5140             default_obj._value = default_value
5141             self.default = default_obj
5142             if value is None:
5143                 self._value = copy(default_obj._value)
5144
5145     @property
5146     def ready(self):
5147         for name, spec in iteritems(self.specs):
5148             value = self._value.get(name)
5149             if value is None:
5150                 if spec.optional:
5151                     continue
5152                 return False
5153             if not value.ready:
5154                 return False
5155         return True
5156
5157     @property
5158     def bered(self):
5159         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5160             return True
5161         return any(value.bered for value in itervalues(self._value))
5162
5163     def __getstate__(self):
5164         return SequenceState(
5165             __version__,
5166             self.specs,
5167             {k: copy(v) for k, v in iteritems(self._value)},
5168             self.tag,
5169             self._expl,
5170             self.default,
5171             self.optional,
5172             self.offset,
5173             self.llen,
5174             self.vlen,
5175             self.expl_lenindef,
5176             self.lenindef,
5177             self.ber_encoded,
5178         )
5179
5180     def __setstate__(self, state):
5181         super(Sequence, self).__setstate__(state)
5182         self.specs = state.specs
5183         self._value = state.value
5184         self.tag = state.tag
5185         self._expl = state.expl
5186         self.default = state.default
5187         self.optional = state.optional
5188         self.offset = state.offset
5189         self.llen = state.llen
5190         self.vlen = state.vlen
5191         self.expl_lenindef = state.expl_lenindef
5192         self.lenindef = state.lenindef
5193         self.ber_encoded = state.ber_encoded
5194
5195     def __eq__(self, their):
5196         if not isinstance(their, self.__class__):
5197             return False
5198         return (
5199             self.specs == their.specs and
5200             self.tag == their.tag and
5201             self._expl == their._expl and
5202             self._value == their._value
5203         )
5204
5205     def __call__(
5206             self,
5207             value=None,
5208             impl=None,
5209             expl=None,
5210             default=None,
5211             optional=None,
5212     ):
5213         return self.__class__(
5214             value=value,
5215             schema=self.specs,
5216             impl=self.tag if impl is None else impl,
5217             expl=self._expl if expl is None else expl,
5218             default=self.default if default is None else default,
5219             optional=self.optional if optional is None else optional,
5220         )
5221
5222     def __contains__(self, key):
5223         return key in self._value
5224
5225     def __setitem__(self, key, value):
5226         spec = self.specs.get(key)
5227         if spec is None:
5228             raise ObjUnknown(key)
5229         if value is None:
5230             self._value.pop(key, None)
5231             return
5232         if not isinstance(value, spec.__class__):
5233             raise InvalidValueType((spec.__class__,))
5234         value = spec(value=value)
5235         if spec.default is not None and value == spec.default:
5236             self._value.pop(key, None)
5237             return
5238         self._value[key] = value
5239
5240     def __getitem__(self, key):
5241         value = self._value.get(key)
5242         if value is not None:
5243             return value
5244         spec = self.specs.get(key)
5245         if spec is None:
5246             raise ObjUnknown(key)
5247         if spec.default is not None:
5248             return spec.default
5249         return None
5250
5251     def _encoded_values(self):
5252         raws = []
5253         for name, spec in iteritems(self.specs):
5254             value = self._value.get(name)
5255             if value is None:
5256                 if spec.optional:
5257                     continue
5258                 raise ObjNotReady(name)
5259             raws.append(value.encode())
5260         return raws
5261
5262     def _encode(self):
5263         v = b"".join(self._encoded_values())
5264         return b"".join((self.tag, len_encode(len(v)), v))
5265
5266     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5267         try:
5268             t, tlen, lv = tag_strip(tlv)
5269         except DecodeError as err:
5270             raise err.__class__(
5271                 msg=err.msg,
5272                 klass=self.__class__,
5273                 decode_path=decode_path,
5274                 offset=offset,
5275             )
5276         if t != self.tag:
5277             raise TagMismatch(
5278                 klass=self.__class__,
5279                 decode_path=decode_path,
5280                 offset=offset,
5281             )
5282         if tag_only:  # pragma: no cover
5283             return None
5284         lenindef = False
5285         ctx_bered = ctx.get("bered", False)
5286         try:
5287             l, llen, v = len_decode(lv)
5288         except LenIndefForm as err:
5289             if not ctx_bered:
5290                 raise err.__class__(
5291                     msg=err.msg,
5292                     klass=self.__class__,
5293                     decode_path=decode_path,
5294                     offset=offset,
5295                 )
5296             l, llen, v = 0, 1, lv[1:]
5297             lenindef = True
5298         except DecodeError as err:
5299             raise err.__class__(
5300                 msg=err.msg,
5301                 klass=self.__class__,
5302                 decode_path=decode_path,
5303                 offset=offset,
5304             )
5305         if l > len(v):
5306             raise NotEnoughData(
5307                 "encoded length is longer than data",
5308                 klass=self.__class__,
5309                 decode_path=decode_path,
5310                 offset=offset,
5311             )
5312         if not lenindef:
5313             v, tail = v[:l], v[l:]
5314         vlen = 0
5315         sub_offset = offset + tlen + llen
5316         values = {}
5317         ber_encoded = False
5318         ctx_allow_default_values = ctx.get("allow_default_values", False)
5319         for name, spec in iteritems(self.specs):
5320             if spec.optional and (
5321                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5322                     len(v) == 0
5323             ):
5324                 continue
5325             sub_decode_path = decode_path + (name,)
5326             try:
5327                 value, v_tail = spec.decode(
5328                     v,
5329                     sub_offset,
5330                     leavemm=True,
5331                     decode_path=sub_decode_path,
5332                     ctx=ctx,
5333                     _ctx_immutable=False,
5334                 )
5335             except TagMismatch as err:
5336                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5337                     continue
5338                 raise
5339
5340             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5341             if defined is not None:
5342                 defined_by, defined_spec = defined
5343                 if issubclass(value.__class__, SequenceOf):
5344                     for i, _value in enumerate(value):
5345                         sub_sub_decode_path = sub_decode_path + (
5346                             str(i),
5347                             DecodePathDefBy(defined_by),
5348                         )
5349                         defined_value, defined_tail = defined_spec.decode(
5350                             memoryview(bytes(_value)),
5351                             sub_offset + (
5352                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5353                                 if value.expled else (value.tlen + value.llen)
5354                             ),
5355                             leavemm=True,
5356                             decode_path=sub_sub_decode_path,
5357                             ctx=ctx,
5358                             _ctx_immutable=False,
5359                         )
5360                         if len(defined_tail) > 0:
5361                             raise DecodeError(
5362                                 "remaining data",
5363                                 klass=self.__class__,
5364                                 decode_path=sub_sub_decode_path,
5365                                 offset=offset,
5366                             )
5367                         _value.defined = (defined_by, defined_value)
5368                 else:
5369                     defined_value, defined_tail = defined_spec.decode(
5370                         memoryview(bytes(value)),
5371                         sub_offset + (
5372                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5373                             if value.expled else (value.tlen + value.llen)
5374                         ),
5375                         leavemm=True,
5376                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5377                         ctx=ctx,
5378                         _ctx_immutable=False,
5379                     )
5380                     if len(defined_tail) > 0:
5381                         raise DecodeError(
5382                             "remaining data",
5383                             klass=self.__class__,
5384                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5385                             offset=offset,
5386                         )
5387                     value.defined = (defined_by, defined_value)
5388
5389             value_len = value.fulllen
5390             vlen += value_len
5391             sub_offset += value_len
5392             v = v_tail
5393             if spec.default is not None and value == spec.default:
5394                 if ctx_bered or ctx_allow_default_values:
5395                     ber_encoded = True
5396                 else:
5397                     raise DecodeError(
5398                         "DEFAULT value met",
5399                         klass=self.__class__,
5400                         decode_path=sub_decode_path,
5401                         offset=sub_offset,
5402                     )
5403             values[name] = value
5404
5405             spec_defines = getattr(spec, "defines", ())
5406             if len(spec_defines) == 0:
5407                 defines_by_path = ctx.get("defines_by_path", ())
5408                 if len(defines_by_path) > 0:
5409                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5410             if spec_defines is not None and len(spec_defines) > 0:
5411                 for rel_path, schema in spec_defines:
5412                     defined = schema.get(value, None)
5413                     if defined is not None:
5414                         ctx.setdefault("_defines", []).append((
5415                             abs_decode_path(sub_decode_path[:-1], rel_path),
5416                             (value, defined),
5417                         ))
5418         if lenindef:
5419             if v[:EOC_LEN].tobytes() != EOC:
5420                 raise DecodeError(
5421                     "no EOC",
5422                     klass=self.__class__,
5423                     decode_path=decode_path,
5424                     offset=offset,
5425                 )
5426             tail = v[EOC_LEN:]
5427             vlen += EOC_LEN
5428         elif len(v) > 0:
5429             raise DecodeError(
5430                 "remaining data",
5431                 klass=self.__class__,
5432                 decode_path=decode_path,
5433                 offset=offset,
5434             )
5435         obj = self.__class__(
5436             schema=self.specs,
5437             impl=self.tag,
5438             expl=self._expl,
5439             default=self.default,
5440             optional=self.optional,
5441             _decoded=(offset, llen, vlen),
5442         )
5443         obj._value = values
5444         obj.lenindef = lenindef
5445         obj.ber_encoded = ber_encoded
5446         return obj, tail
5447
5448     def __repr__(self):
5449         value = pp_console_row(next(self.pps()))
5450         cols = []
5451         for name in self.specs:
5452             _value = self._value.get(name)
5453             if _value is None:
5454                 continue
5455             cols.append("%s: %s" % (name, repr(_value)))
5456         return "%s[%s]" % (value, "; ".join(cols))
5457
5458     def pps(self, decode_path=()):
5459         yield _pp(
5460             obj=self,
5461             asn1_type_name=self.asn1_type_name,
5462             obj_name=self.__class__.__name__,
5463             decode_path=decode_path,
5464             optional=self.optional,
5465             default=self == self.default,
5466             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5467             expl=None if self._expl is None else tag_decode(self._expl),
5468             offset=self.offset,
5469             tlen=self.tlen,
5470             llen=self.llen,
5471             vlen=self.vlen,
5472             expl_offset=self.expl_offset if self.expled else None,
5473             expl_tlen=self.expl_tlen if self.expled else None,
5474             expl_llen=self.expl_llen if self.expled else None,
5475             expl_vlen=self.expl_vlen if self.expled else None,
5476             expl_lenindef=self.expl_lenindef,
5477             lenindef=self.lenindef,
5478             ber_encoded=self.ber_encoded,
5479             bered=self.bered,
5480         )
5481         for name in self.specs:
5482             value = self._value.get(name)
5483             if value is None:
5484                 continue
5485             yield value.pps(decode_path=decode_path + (name,))
5486         for pp in self.pps_lenindef(decode_path):
5487             yield pp
5488
5489
5490 class Set(Sequence):
5491     """``SET`` structure type
5492
5493     Its usage is identical to :py:class:`pyderasn.Sequence`.
5494
5495     .. _allow_unordered_set_ctx:
5496
5497     DER prohibits unordered values encoding and will raise an error
5498     during decode. If If :ref:`bered <bered_ctx>` context option is set,
5499     then no error will occure. Also you can disable strict values
5500     ordering check by setting ``"allow_unordered_set": True``
5501     :ref:`context <ctx>` option.
5502     """
5503     __slots__ = ()
5504     tag_default = tag_encode(form=TagFormConstructed, num=17)
5505     asn1_type_name = "SET"
5506
5507     def _encode(self):
5508         raws = self._encoded_values()
5509         raws.sort()
5510         v = b"".join(raws)
5511         return b"".join((self.tag, len_encode(len(v)), v))
5512
5513     def _specs_items(self):
5514         return iteritems(self.specs)
5515
5516     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5517         try:
5518             t, tlen, lv = tag_strip(tlv)
5519         except DecodeError as err:
5520             raise err.__class__(
5521                 msg=err.msg,
5522                 klass=self.__class__,
5523                 decode_path=decode_path,
5524                 offset=offset,
5525             )
5526         if t != self.tag:
5527             raise TagMismatch(
5528                 klass=self.__class__,
5529                 decode_path=decode_path,
5530                 offset=offset,
5531             )
5532         if tag_only:
5533             return None
5534         lenindef = False
5535         ctx_bered = ctx.get("bered", False)
5536         try:
5537             l, llen, v = len_decode(lv)
5538         except LenIndefForm as err:
5539             if not ctx_bered:
5540                 raise err.__class__(
5541                     msg=err.msg,
5542                     klass=self.__class__,
5543                     decode_path=decode_path,
5544                     offset=offset,
5545                 )
5546             l, llen, v = 0, 1, lv[1:]
5547             lenindef = True
5548         except DecodeError as err:
5549             raise err.__class__(
5550                 msg=err.msg,
5551                 klass=self.__class__,
5552                 decode_path=decode_path,
5553                 offset=offset,
5554             )
5555         if l > len(v):
5556             raise NotEnoughData(
5557                 "encoded length is longer than data",
5558                 klass=self.__class__,
5559                 offset=offset,
5560             )
5561         if not lenindef:
5562             v, tail = v[:l], v[l:]
5563         vlen = 0
5564         sub_offset = offset + tlen + llen
5565         values = {}
5566         ber_encoded = False
5567         ctx_allow_default_values = ctx.get("allow_default_values", False)
5568         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5569         value_prev = memoryview(v[:0])
5570
5571         while len(v) > 0:
5572             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5573                 break
5574             for name, spec in self._specs_items():
5575                 sub_decode_path = decode_path + (name,)
5576                 try:
5577                     spec.decode(
5578                         v,
5579                         sub_offset,
5580                         leavemm=True,
5581                         decode_path=sub_decode_path,
5582                         ctx=ctx,
5583                         tag_only=True,
5584                         _ctx_immutable=False,
5585                     )
5586                 except TagMismatch:
5587                     continue
5588                 break
5589             else:
5590                 raise TagMismatch(
5591                     klass=self.__class__,
5592                     decode_path=decode_path,
5593                     offset=offset,
5594                 )
5595             value, v_tail = spec.decode(
5596                 v,
5597                 sub_offset,
5598                 leavemm=True,
5599                 decode_path=sub_decode_path,
5600                 ctx=ctx,
5601                 _ctx_immutable=False,
5602             )
5603             value_len = value.fulllen
5604             if value_prev.tobytes() > v[:value_len].tobytes():
5605                 if ctx_bered or ctx_allow_unordered_set:
5606                     ber_encoded = True
5607                 else:
5608                     raise DecodeError(
5609                         "unordered " + self.asn1_type_name,
5610                         klass=self.__class__,
5611                         decode_path=sub_decode_path,
5612                         offset=sub_offset,
5613                     )
5614             if spec.default is None or value != spec.default:
5615                 pass
5616             elif ctx_bered or ctx_allow_default_values:
5617                 ber_encoded = True
5618             else:
5619                 raise DecodeError(
5620                     "DEFAULT value met",
5621                     klass=self.__class__,
5622                     decode_path=sub_decode_path,
5623                     offset=sub_offset,
5624                 )
5625             values[name] = value
5626             value_prev = v[:value_len]
5627             sub_offset += value_len
5628             vlen += value_len
5629             v = v_tail
5630         obj = self.__class__(
5631             schema=self.specs,
5632             impl=self.tag,
5633             expl=self._expl,
5634             default=self.default,
5635             optional=self.optional,
5636             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5637         )
5638         if lenindef:
5639             if v[:EOC_LEN].tobytes() != EOC:
5640                 raise DecodeError(
5641                     "no EOC",
5642                     klass=self.__class__,
5643                     decode_path=decode_path,
5644                     offset=offset,
5645                 )
5646             tail = v[EOC_LEN:]
5647             obj.lenindef = True
5648         obj._value = values
5649         if not obj.ready:
5650             raise DecodeError(
5651                 "not all values are ready",
5652                 klass=self.__class__,
5653                 decode_path=decode_path,
5654                 offset=offset,
5655             )
5656         obj.ber_encoded = ber_encoded
5657         return obj, tail
5658
5659
5660 SequenceOfState = namedtuple("SequenceOfState", (
5661     "version",
5662     "spec",
5663     "value",
5664     "bound_min",
5665     "bound_max",
5666     "tag",
5667     "expl",
5668     "default",
5669     "optional",
5670     "offset",
5671     "llen",
5672     "vlen",
5673     "expl_lenindef",
5674     "lenindef",
5675     "ber_encoded",
5676 ))
5677
5678
5679 class SequenceOf(Obj):
5680     """``SEQUENCE OF`` sequence type
5681
5682     For that kind of type you must specify the object it will carry on
5683     (bounds are for example here, not required)::
5684
5685         class Ints(SequenceOf):
5686             schema = Integer()
5687             bounds = (0, 2)
5688
5689     >>> ints = Ints()
5690     >>> ints.append(Integer(123))
5691     >>> ints.append(Integer(234))
5692     >>> ints
5693     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5694     >>> [int(i) for i in ints]
5695     [123, 234]
5696     >>> ints.append(Integer(345))
5697     Traceback (most recent call last):
5698     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5699     >>> ints[1]
5700     INTEGER 234
5701     >>> ints[1] = Integer(345)
5702     >>> ints
5703     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5704
5705     Also you can initialize sequence with preinitialized values:
5706
5707     >>> ints = Ints([Integer(123), Integer(234)])
5708     """
5709     __slots__ = ("spec", "_bound_min", "_bound_max")
5710     tag_default = tag_encode(form=TagFormConstructed, num=16)
5711     asn1_type_name = "SEQUENCE OF"
5712
5713     def __init__(
5714             self,
5715             value=None,
5716             schema=None,
5717             bounds=None,
5718             impl=None,
5719             expl=None,
5720             default=None,
5721             optional=False,
5722             _decoded=(0, 0, 0),
5723     ):
5724         super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
5725         if schema is None:
5726             schema = getattr(self, "schema", None)
5727         if schema is None:
5728             raise ValueError("schema must be specified")
5729         self.spec = schema
5730         self._bound_min, self._bound_max = getattr(
5731             self,
5732             "bounds",
5733             (0, float("+inf")),
5734         ) if bounds is None else bounds
5735         self._value = []
5736         if value is not None:
5737             self._value = self._value_sanitize(value)
5738         if default is not None:
5739             default_value = self._value_sanitize(default)
5740             default_obj = self.__class__(
5741                 schema=schema,
5742                 impl=self.tag,
5743                 expl=self._expl,
5744             )
5745             default_obj._value = default_value
5746             self.default = default_obj
5747             if value is None:
5748                 self._value = copy(default_obj._value)
5749
5750     def _value_sanitize(self, value):
5751         if issubclass(value.__class__, SequenceOf):
5752             value = value._value
5753         elif hasattr(value, "__iter__"):
5754             value = list(value)
5755         else:
5756             raise InvalidValueType((self.__class__, iter))
5757         if not self._bound_min <= len(value) <= self._bound_max:
5758             raise BoundsError(self._bound_min, len(value), self._bound_max)
5759         for v in value:
5760             if not isinstance(v, self.spec.__class__):
5761                 raise InvalidValueType((self.spec.__class__,))
5762         return value
5763
5764     @property
5765     def ready(self):
5766         return all(v.ready for v in self._value)
5767
5768     @property
5769     def bered(self):
5770         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5771             return True
5772         return any(v.bered for v in self._value)
5773
5774     def __getstate__(self):
5775         return SequenceOfState(
5776             __version__,
5777             self.spec,
5778             [copy(v) for v in self._value],
5779             self._bound_min,
5780             self._bound_max,
5781             self.tag,
5782             self._expl,
5783             self.default,
5784             self.optional,
5785             self.offset,
5786             self.llen,
5787             self.vlen,
5788             self.expl_lenindef,
5789             self.lenindef,
5790             self.ber_encoded,
5791         )
5792
5793     def __setstate__(self, state):
5794         super(SequenceOf, self).__setstate__(state)
5795         self.spec = state.spec
5796         self._value = state.value
5797         self._bound_min = state.bound_min
5798         self._bound_max = state.bound_max
5799         self.tag = state.tag
5800         self._expl = state.expl
5801         self.default = state.default
5802         self.optional = state.optional
5803         self.offset = state.offset
5804         self.llen = state.llen
5805         self.vlen = state.vlen
5806         self.expl_lenindef = state.expl_lenindef
5807         self.lenindef = state.lenindef
5808         self.ber_encoded = state.ber_encoded
5809
5810     def __eq__(self, their):
5811         if isinstance(their, self.__class__):
5812             return (
5813                 self.spec == their.spec and
5814                 self.tag == their.tag and
5815                 self._expl == their._expl and
5816                 self._value == their._value
5817             )
5818         if hasattr(their, "__iter__"):
5819             return self._value == list(their)
5820         return False
5821
5822     def __call__(
5823             self,
5824             value=None,
5825             bounds=None,
5826             impl=None,
5827             expl=None,
5828             default=None,
5829             optional=None,
5830     ):
5831         return self.__class__(
5832             value=value,
5833             schema=self.spec,
5834             bounds=(
5835                 (self._bound_min, self._bound_max)
5836                 if bounds is None else bounds
5837             ),
5838             impl=self.tag if impl is None else impl,
5839             expl=self._expl if expl is None else expl,
5840             default=self.default if default is None else default,
5841             optional=self.optional if optional is None else optional,
5842         )
5843
5844     def __contains__(self, key):
5845         return key in self._value
5846
5847     def append(self, value):
5848         if not isinstance(value, self.spec.__class__):
5849             raise InvalidValueType((self.spec.__class__,))
5850         if len(self._value) + 1 > self._bound_max:
5851             raise BoundsError(
5852                 self._bound_min,
5853                 len(self._value) + 1,
5854                 self._bound_max,
5855             )
5856         self._value.append(value)
5857
5858     def __iter__(self):
5859         self._assert_ready()
5860         return iter(self._value)
5861
5862     def __len__(self):
5863         self._assert_ready()
5864         return len(self._value)
5865
5866     def __setitem__(self, key, value):
5867         if not isinstance(value, self.spec.__class__):
5868             raise InvalidValueType((self.spec.__class__,))
5869         self._value[key] = self.spec(value=value)
5870
5871     def __getitem__(self, key):
5872         return self._value[key]
5873
5874     def _encoded_values(self):
5875         return [v.encode() for v in self._value]
5876
5877     def _encode(self):
5878         v = b"".join(self._encoded_values())
5879         return b"".join((self.tag, len_encode(len(v)), v))
5880
5881     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5882         try:
5883             t, tlen, lv = tag_strip(tlv)
5884         except DecodeError as err:
5885             raise err.__class__(
5886                 msg=err.msg,
5887                 klass=self.__class__,
5888                 decode_path=decode_path,
5889                 offset=offset,
5890             )
5891         if t != self.tag:
5892             raise TagMismatch(
5893                 klass=self.__class__,
5894                 decode_path=decode_path,
5895                 offset=offset,
5896             )
5897         if tag_only:
5898             return None
5899         lenindef = False
5900         ctx_bered = ctx.get("bered", False)
5901         try:
5902             l, llen, v = len_decode(lv)
5903         except LenIndefForm as err:
5904             if not ctx_bered:
5905                 raise err.__class__(
5906                     msg=err.msg,
5907                     klass=self.__class__,
5908                     decode_path=decode_path,
5909                     offset=offset,
5910                 )
5911             l, llen, v = 0, 1, lv[1:]
5912             lenindef = True
5913         except DecodeError as err:
5914             raise err.__class__(
5915                 msg=err.msg,
5916                 klass=self.__class__,
5917                 decode_path=decode_path,
5918                 offset=offset,
5919             )
5920         if l > len(v):
5921             raise NotEnoughData(
5922                 "encoded length is longer than data",
5923                 klass=self.__class__,
5924                 decode_path=decode_path,
5925                 offset=offset,
5926             )
5927         if not lenindef:
5928             v, tail = v[:l], v[l:]
5929         vlen = 0
5930         sub_offset = offset + tlen + llen
5931         _value = []
5932         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5933         value_prev = memoryview(v[:0])
5934         ber_encoded = False
5935         spec = self.spec
5936         while len(v) > 0:
5937             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5938                 break
5939             sub_decode_path = decode_path + (str(len(_value)),)
5940             value, v_tail = spec.decode(
5941                 v,
5942                 sub_offset,
5943                 leavemm=True,
5944                 decode_path=sub_decode_path,
5945                 ctx=ctx,
5946                 _ctx_immutable=False,
5947             )
5948             value_len = value.fulllen
5949             if ordering_check:
5950                 if value_prev.tobytes() > v[:value_len].tobytes():
5951                     if ctx_bered or ctx_allow_unordered_set:
5952                         ber_encoded = True
5953                     else:
5954                         raise DecodeError(
5955                             "unordered " + self.asn1_type_name,
5956                             klass=self.__class__,
5957                             decode_path=sub_decode_path,
5958                             offset=sub_offset,
5959                         )
5960                 value_prev = v[:value_len]
5961             _value.append(value)
5962             sub_offset += value_len
5963             vlen += value_len
5964             v = v_tail
5965         try:
5966             obj = self.__class__(
5967                 value=_value,
5968                 schema=spec,
5969                 bounds=(self._bound_min, self._bound_max),
5970                 impl=self.tag,
5971                 expl=self._expl,
5972                 default=self.default,
5973                 optional=self.optional,
5974                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5975             )
5976         except BoundsError as err:
5977             raise DecodeError(
5978                 msg=str(err),
5979                 klass=self.__class__,
5980                 decode_path=decode_path,
5981                 offset=offset,
5982             )
5983         if lenindef:
5984             if v[:EOC_LEN].tobytes() != EOC:
5985                 raise DecodeError(
5986                     "no EOC",
5987                     klass=self.__class__,
5988                     decode_path=decode_path,
5989                     offset=offset,
5990                 )
5991             obj.lenindef = True
5992             tail = v[EOC_LEN:]
5993         obj.ber_encoded = ber_encoded
5994         return obj, tail
5995
5996     def __repr__(self):
5997         return "%s[%s]" % (
5998             pp_console_row(next(self.pps())),
5999             ", ".join(repr(v) for v in self._value),
6000         )
6001
6002     def pps(self, decode_path=()):
6003         yield _pp(
6004             obj=self,
6005             asn1_type_name=self.asn1_type_name,
6006             obj_name=self.__class__.__name__,
6007             decode_path=decode_path,
6008             optional=self.optional,
6009             default=self == self.default,
6010             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6011             expl=None if self._expl is None else tag_decode(self._expl),
6012             offset=self.offset,
6013             tlen=self.tlen,
6014             llen=self.llen,
6015             vlen=self.vlen,
6016             expl_offset=self.expl_offset if self.expled else None,
6017             expl_tlen=self.expl_tlen if self.expled else None,
6018             expl_llen=self.expl_llen if self.expled else None,
6019             expl_vlen=self.expl_vlen if self.expled else None,
6020             expl_lenindef=self.expl_lenindef,
6021             lenindef=self.lenindef,
6022             ber_encoded=self.ber_encoded,
6023             bered=self.bered,
6024         )
6025         for i, value in enumerate(self._value):
6026             yield value.pps(decode_path=decode_path + (str(i),))
6027         for pp in self.pps_lenindef(decode_path):
6028             yield pp
6029
6030
6031 class SetOf(SequenceOf):
6032     """``SET OF`` sequence type
6033
6034     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6035     """
6036     __slots__ = ()
6037     tag_default = tag_encode(form=TagFormConstructed, num=17)
6038     asn1_type_name = "SET OF"
6039
6040     def _encode(self):
6041         raws = self._encoded_values()
6042         raws.sort()
6043         v = b"".join(raws)
6044         return b"".join((self.tag, len_encode(len(v)), v))
6045
6046     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
6047         return super(SetOf, self)._decode(
6048             tlv,
6049             offset,
6050             decode_path,
6051             ctx,
6052             tag_only,
6053             ordering_check=True,
6054         )
6055
6056
6057 def obj_by_path(pypath):  # pragma: no cover
6058     """Import object specified as string Python path
6059
6060     Modules must be separated from classes/functions with ``:``.
6061
6062     >>> obj_by_path("foo.bar:Baz")
6063     <class 'foo.bar.Baz'>
6064     >>> obj_by_path("foo.bar:Baz.boo")
6065     <classmethod 'foo.bar.Baz.boo'>
6066     """
6067     mod, objs = pypath.rsplit(":", 1)
6068     from importlib import import_module
6069     obj = import_module(mod)
6070     for obj_name in objs.split("."):
6071         obj = getattr(obj, obj_name)
6072     return obj
6073
6074
6075 def generic_decoder():  # pragma: no cover
6076     # All of this below is a big hack with self references
6077     choice = PrimitiveTypes()
6078     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6079     choice.specs["SetOf"] = SetOf(schema=choice)
6080     for i in six_xrange(31):
6081         choice.specs["SequenceOf%d" % i] = SequenceOf(
6082             schema=choice,
6083             expl=tag_ctxc(i),
6084         )
6085     choice.specs["Any"] = Any()
6086
6087     # Class name equals to type name, to omit it from output
6088     class SEQUENCEOF(SequenceOf):
6089         __slots__ = ()
6090         schema = choice
6091
6092     def pprint_any(
6093             obj,
6094             oid_maps=(),
6095             with_colours=False,
6096             with_decode_path=False,
6097             decode_path_only=(),
6098     ):
6099         def _pprint_pps(pps):
6100             for pp in pps:
6101                 if hasattr(pp, "_fields"):
6102                     if (
6103                             decode_path_only != () and
6104                             pp.decode_path[:len(decode_path_only)] != decode_path_only
6105                     ):
6106                         continue
6107                     if pp.asn1_type_name == Choice.asn1_type_name:
6108                         continue
6109                     pp_kwargs = pp._asdict()
6110                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6111                     pp = _pp(**pp_kwargs)
6112                     yield pp_console_row(
6113                         pp,
6114                         oid_maps=oid_maps,
6115                         with_offsets=True,
6116                         with_blob=False,
6117                         with_colours=with_colours,
6118                         with_decode_path=with_decode_path,
6119                         decode_path_len_decrease=len(decode_path_only),
6120                     )
6121                     for row in pp_console_blob(
6122                             pp,
6123                             decode_path_len_decrease=len(decode_path_only),
6124                     ):
6125                         yield row
6126                 else:
6127                     for row in _pprint_pps(pp):
6128                         yield row
6129         return "\n".join(_pprint_pps(obj.pps()))
6130     return SEQUENCEOF(), pprint_any
6131
6132
6133 def main():  # pragma: no cover
6134     import argparse
6135     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6136     parser.add_argument(
6137         "--skip",
6138         type=int,
6139         default=0,
6140         help="Skip that number of bytes from the beginning",
6141     )
6142     parser.add_argument(
6143         "--oids",
6144         help="Python paths to dictionary with OIDs, comma separated",
6145     )
6146     parser.add_argument(
6147         "--schema",
6148         help="Python path to schema definition to use",
6149     )
6150     parser.add_argument(
6151         "--defines-by-path",
6152         help="Python path to decoder's defines_by_path",
6153     )
6154     parser.add_argument(
6155         "--nobered",
6156         action="store_true",
6157         help="Disallow BER encoding",
6158     )
6159     parser.add_argument(
6160         "--print-decode-path",
6161         action="store_true",
6162         help="Print decode paths",
6163     )
6164     parser.add_argument(
6165         "--decode-path-only",
6166         help="Print only specified decode path",
6167     )
6168     parser.add_argument(
6169         "--allow-expl-oob",
6170         action="store_true",
6171         help="Allow explicit tag out-of-bound",
6172     )
6173     parser.add_argument(
6174         "DERFile",
6175         type=argparse.FileType("rb"),
6176         help="Path to DER file you want to decode",
6177     )
6178     args = parser.parse_args()
6179     args.DERFile.seek(args.skip)
6180     der = memoryview(args.DERFile.read())
6181     args.DERFile.close()
6182     oid_maps = (
6183         [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6184         if args.oids else ()
6185     )
6186     if args.schema:
6187         schema = obj_by_path(args.schema)
6188         from functools import partial
6189         pprinter = partial(pprint, big_blobs=True)
6190     else:
6191         schema, pprinter = generic_decoder()
6192     ctx = {
6193         "bered": not args.nobered,
6194         "allow_expl_oob": args.allow_expl_oob,
6195     }
6196     if args.defines_by_path is not None:
6197         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6198     obj, tail = schema().decode(der, ctx=ctx)
6199     print(pprinter(
6200         obj,
6201         oid_maps=oid_maps,
6202         with_colours=environ.get("NO_COLOR") is None,
6203         with_decode_path=args.print_decode_path,
6204         decode_path_only=(
6205             () if args.decode_path_only is None else
6206             tuple(args.decode_path_only.split(":"))
6207         ),
6208     ))
6209     if tail != b"":
6210         print("\nTrailing data: %s" % hexenc(tail))
6211
6212
6213 if __name__ == "__main__":
6214     main()