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