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