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