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