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