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