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