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