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