]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Fully strict integers validation in *Time
[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("0123456789")
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_chunk(self, lv, offset, decode_path):
2634         try:
2635             l, llen, v = len_decode(lv)
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 l > len(v):
2644             raise NotEnoughData(
2645                 "encoded length is longer than data",
2646                 klass=self.__class__,
2647                 decode_path=decode_path,
2648                 offset=offset,
2649             )
2650         if l == 0:
2651             raise NotEnoughData(
2652                 "zero length",
2653                 klass=self.__class__,
2654                 decode_path=decode_path,
2655                 offset=offset,
2656             )
2657         pad_size = byte2int(v)
2658         if l == 1 and pad_size != 0:
2659             raise DecodeError(
2660                 "invalid empty value",
2661                 klass=self.__class__,
2662                 decode_path=decode_path,
2663                 offset=offset,
2664             )
2665         if pad_size > 7:
2666             raise DecodeError(
2667                 "too big pad",
2668                 klass=self.__class__,
2669                 decode_path=decode_path,
2670                 offset=offset,
2671             )
2672         if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2673             raise DecodeError(
2674                 "invalid pad",
2675                 klass=self.__class__,
2676                 decode_path=decode_path,
2677                 offset=offset,
2678             )
2679         v, tail = v[:l], v[l:]
2680         obj = self.__class__(
2681             value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2682             impl=self.tag,
2683             expl=self._expl,
2684             default=self.default,
2685             optional=self.optional,
2686             _specs=self.specs,
2687             _decoded=(offset, llen, l),
2688         )
2689         return obj, tail
2690
2691     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2692         try:
2693             t, tlen, lv = tag_strip(tlv)
2694         except DecodeError as err:
2695             raise err.__class__(
2696                 msg=err.msg,
2697                 klass=self.__class__,
2698                 decode_path=decode_path,
2699                 offset=offset,
2700             )
2701         if t == self.tag:
2702             if tag_only:  # pragma: no cover
2703                 return None
2704             return self._decode_chunk(lv, offset, decode_path)
2705         if t == self.tag_constructed:
2706             if not ctx.get("bered", False):
2707                 raise DecodeError(
2708                     "unallowed BER constructed encoding",
2709                     klass=self.__class__,
2710                     decode_path=decode_path,
2711                     offset=offset,
2712                 )
2713             if tag_only:  # pragma: no cover
2714                 return None
2715             lenindef = False
2716             try:
2717                 l, llen, v = len_decode(lv)
2718             except LenIndefForm:
2719                 llen, l, v = 1, 0, lv[1:]
2720                 lenindef = True
2721             except DecodeError as err:
2722                 raise err.__class__(
2723                     msg=err.msg,
2724                     klass=self.__class__,
2725                     decode_path=decode_path,
2726                     offset=offset,
2727                 )
2728             if l > len(v):
2729                 raise NotEnoughData(
2730                     "encoded length is longer than data",
2731                     klass=self.__class__,
2732                     decode_path=decode_path,
2733                     offset=offset,
2734                 )
2735             if not lenindef and l == 0:
2736                 raise NotEnoughData(
2737                     "zero length",
2738                     klass=self.__class__,
2739                     decode_path=decode_path,
2740                     offset=offset,
2741                 )
2742             chunks = []
2743             sub_offset = offset + tlen + llen
2744             vlen = 0
2745             while True:
2746                 if lenindef:
2747                     if v[:EOC_LEN].tobytes() == EOC:
2748                         break
2749                 else:
2750                     if vlen == l:
2751                         break
2752                     if vlen > l:
2753                         raise DecodeError(
2754                             "chunk out of bounds",
2755                             klass=self.__class__,
2756                             decode_path=decode_path + (str(len(chunks) - 1),),
2757                             offset=chunks[-1].offset,
2758                         )
2759                 sub_decode_path = decode_path + (str(len(chunks)),)
2760                 try:
2761                     chunk, v_tail = BitString().decode(
2762                         v,
2763                         offset=sub_offset,
2764                         decode_path=sub_decode_path,
2765                         leavemm=True,
2766                         ctx=ctx,
2767                         _ctx_immutable=False,
2768                     )
2769                 except TagMismatch:
2770                     raise DecodeError(
2771                         "expected BitString encoded chunk",
2772                         klass=self.__class__,
2773                         decode_path=sub_decode_path,
2774                         offset=sub_offset,
2775                     )
2776                 chunks.append(chunk)
2777                 sub_offset += chunk.tlvlen
2778                 vlen += chunk.tlvlen
2779                 v = v_tail
2780             if len(chunks) == 0:
2781                 raise DecodeError(
2782                     "no chunks",
2783                     klass=self.__class__,
2784                     decode_path=decode_path,
2785                     offset=offset,
2786                 )
2787             values = []
2788             bit_len = 0
2789             for chunk_i, chunk in enumerate(chunks[:-1]):
2790                 if chunk.bit_len % 8 != 0:
2791                     raise DecodeError(
2792                         "BitString chunk is not multiple of 8 bits",
2793                         klass=self.__class__,
2794                         decode_path=decode_path + (str(chunk_i),),
2795                         offset=chunk.offset,
2796                     )
2797                 values.append(bytes(chunk))
2798                 bit_len += chunk.bit_len
2799             chunk_last = chunks[-1]
2800             values.append(bytes(chunk_last))
2801             bit_len += chunk_last.bit_len
2802             obj = self.__class__(
2803                 value=(bit_len, b"".join(values)),
2804                 impl=self.tag,
2805                 expl=self._expl,
2806                 default=self.default,
2807                 optional=self.optional,
2808                 _specs=self.specs,
2809                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2810             )
2811             obj.lenindef = lenindef
2812             obj.ber_encoded = True
2813             return obj, (v[EOC_LEN:] if lenindef else v)
2814         raise TagMismatch(
2815             klass=self.__class__,
2816             decode_path=decode_path,
2817             offset=offset,
2818         )
2819
2820     def __repr__(self):
2821         return pp_console_row(next(self.pps()))
2822
2823     def pps(self, decode_path=()):
2824         value = None
2825         blob = None
2826         if self.ready:
2827             bit_len, blob = self._value
2828             value = "%d bits" % bit_len
2829             if len(self.specs) > 0:
2830                 blob = tuple(self.named)
2831         yield _pp(
2832             obj=self,
2833             asn1_type_name=self.asn1_type_name,
2834             obj_name=self.__class__.__name__,
2835             decode_path=decode_path,
2836             value=value,
2837             blob=blob,
2838             optional=self.optional,
2839             default=self == self.default,
2840             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2841             expl=None if self._expl is None else tag_decode(self._expl),
2842             offset=self.offset,
2843             tlen=self.tlen,
2844             llen=self.llen,
2845             vlen=self.vlen,
2846             expl_offset=self.expl_offset if self.expled else None,
2847             expl_tlen=self.expl_tlen if self.expled else None,
2848             expl_llen=self.expl_llen if self.expled else None,
2849             expl_vlen=self.expl_vlen if self.expled else None,
2850             expl_lenindef=self.expl_lenindef,
2851             lenindef=self.lenindef,
2852             ber_encoded=self.ber_encoded,
2853             bered=self.bered,
2854         )
2855         defined_by, defined = self.defined or (None, None)
2856         if defined_by is not None:
2857             yield defined.pps(
2858                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2859             )
2860         for pp in self.pps_lenindef(decode_path):
2861             yield pp
2862
2863
2864 OctetStringState = namedtuple("OctetStringState", (
2865     "version",
2866     "value",
2867     "bound_min",
2868     "bound_max",
2869     "tag",
2870     "expl",
2871     "default",
2872     "optional",
2873     "offset",
2874     "llen",
2875     "vlen",
2876     "expl_lenindef",
2877     "lenindef",
2878     "ber_encoded",
2879     "tag_constructed",
2880     "defined",
2881 ), **NAMEDTUPLE_KWARGS)
2882
2883
2884 class OctetString(Obj):
2885     """``OCTET STRING`` binary string type
2886
2887     >>> s = OctetString(b"hello world")
2888     OCTET STRING 11 bytes 68656c6c6f20776f726c64
2889     >>> s == OctetString(b"hello world")
2890     True
2891     >>> bytes(s)
2892     b'hello world'
2893
2894     >>> OctetString(b"hello", bounds=(4, 4))
2895     Traceback (most recent call last):
2896     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2897     >>> OctetString(b"hell", bounds=(4, 4))
2898     OCTET STRING 4 bytes 68656c6c
2899
2900     .. note::
2901
2902        Pay attention that OCTET STRING can be encoded both in primitive
2903        and constructed forms. Decoder always checks constructed form tag
2904        additionally to specified primitive one. If BER decoding is
2905        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2906        of DER restrictions.
2907     """
2908     __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2909     tag_default = tag_encode(4)
2910     asn1_type_name = "OCTET STRING"
2911
2912     def __init__(
2913             self,
2914             value=None,
2915             bounds=None,
2916             impl=None,
2917             expl=None,
2918             default=None,
2919             optional=False,
2920             _decoded=(0, 0, 0),
2921             ctx=None,
2922     ):
2923         """
2924         :param value: set the value. Either binary type, or
2925                       :py:class:`pyderasn.OctetString` object
2926         :param bounds: set ``(MIN, MAX)`` value size constraint.
2927                        (-inf, +inf) by default
2928         :param bytes impl: override default tag with ``IMPLICIT`` one
2929         :param bytes expl: override default tag with ``EXPLICIT`` one
2930         :param default: set default value. Type same as in ``value``
2931         :param bool optional: is object ``OPTIONAL`` in sequence
2932         """
2933         super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
2934         self._value = value
2935         self._bound_min, self._bound_max = getattr(
2936             self,
2937             "bounds",
2938             (0, float("+inf")),
2939         ) if bounds is None else bounds
2940         if value is not None:
2941             self._value = self._value_sanitize(value)
2942         if default is not None:
2943             default = self._value_sanitize(default)
2944             self.default = self.__class__(
2945                 value=default,
2946                 impl=self.tag,
2947                 expl=self._expl,
2948             )
2949             if self._value is None:
2950                 self._value = default
2951         self.defined = None
2952         tag_klass, _, tag_num = tag_decode(self.tag)
2953         self.tag_constructed = tag_encode(
2954             klass=tag_klass,
2955             form=TagFormConstructed,
2956             num=tag_num,
2957         )
2958
2959     def _value_sanitize(self, value):
2960         if value.__class__ == binary_type:
2961             pass
2962         elif issubclass(value.__class__, OctetString):
2963             value = value._value
2964         else:
2965             raise InvalidValueType((self.__class__, bytes))
2966         if not self._bound_min <= len(value) <= self._bound_max:
2967             raise BoundsError(self._bound_min, len(value), self._bound_max)
2968         return value
2969
2970     @property
2971     def ready(self):
2972         return self._value is not None
2973
2974     def __getstate__(self):
2975         return OctetStringState(
2976             __version__,
2977             self._value,
2978             self._bound_min,
2979             self._bound_max,
2980             self.tag,
2981             self._expl,
2982             self.default,
2983             self.optional,
2984             self.offset,
2985             self.llen,
2986             self.vlen,
2987             self.expl_lenindef,
2988             self.lenindef,
2989             self.ber_encoded,
2990             self.tag_constructed,
2991             self.defined,
2992         )
2993
2994     def __setstate__(self, state):
2995         super(OctetString, self).__setstate__(state)
2996         self._value = state.value
2997         self._bound_min = state.bound_min
2998         self._bound_max = state.bound_max
2999         self.tag = state.tag
3000         self._expl = state.expl
3001         self.default = state.default
3002         self.optional = state.optional
3003         self.offset = state.offset
3004         self.llen = state.llen
3005         self.vlen = state.vlen
3006         self.expl_lenindef = state.expl_lenindef
3007         self.lenindef = state.lenindef
3008         self.ber_encoded = state.ber_encoded
3009         self.tag_constructed = state.tag_constructed
3010         self.defined = state.defined
3011
3012     def __bytes__(self):
3013         self._assert_ready()
3014         return self._value
3015
3016     def __eq__(self, their):
3017         if their.__class__ == binary_type:
3018             return self._value == their
3019         if not issubclass(their.__class__, OctetString):
3020             return False
3021         return (
3022             self._value == their._value and
3023             self.tag == their.tag and
3024             self._expl == their._expl
3025         )
3026
3027     def __lt__(self, their):
3028         return self._value < their._value
3029
3030     def __call__(
3031             self,
3032             value=None,
3033             bounds=None,
3034             impl=None,
3035             expl=None,
3036             default=None,
3037             optional=None,
3038     ):
3039         return self.__class__(
3040             value=value,
3041             bounds=(
3042                 (self._bound_min, self._bound_max)
3043                 if bounds is None else bounds
3044             ),
3045             impl=self.tag if impl is None else impl,
3046             expl=self._expl if expl is None else expl,
3047             default=self.default if default is None else default,
3048             optional=self.optional if optional is None else optional,
3049         )
3050
3051     def _encode(self):
3052         self._assert_ready()
3053         return b"".join((
3054             self.tag,
3055             len_encode(len(self._value)),
3056             self._value,
3057         ))
3058
3059     def _decode_chunk(self, lv, offset, decode_path, ctx):
3060         try:
3061             l, llen, v = len_decode(lv)
3062         except DecodeError as err:
3063             raise err.__class__(
3064                 msg=err.msg,
3065                 klass=self.__class__,
3066                 decode_path=decode_path,
3067                 offset=offset,
3068             )
3069         if l > len(v):
3070             raise NotEnoughData(
3071                 "encoded length is longer than data",
3072                 klass=self.__class__,
3073                 decode_path=decode_path,
3074                 offset=offset,
3075             )
3076         v, tail = v[:l], v[l:]
3077         try:
3078             obj = self.__class__(
3079                 value=v.tobytes(),
3080                 bounds=(self._bound_min, self._bound_max),
3081                 impl=self.tag,
3082                 expl=self._expl,
3083                 default=self.default,
3084                 optional=self.optional,
3085                 _decoded=(offset, llen, l),
3086                 ctx=ctx,
3087             )
3088         except DecodeError as err:
3089             raise DecodeError(
3090                 msg=err.msg,
3091                 klass=self.__class__,
3092                 decode_path=decode_path,
3093                 offset=offset,
3094             )
3095         except BoundsError as err:
3096             raise DecodeError(
3097                 msg=str(err),
3098                 klass=self.__class__,
3099                 decode_path=decode_path,
3100                 offset=offset,
3101             )
3102         return obj, tail
3103
3104     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3105         try:
3106             t, tlen, lv = tag_strip(tlv)
3107         except DecodeError as err:
3108             raise err.__class__(
3109                 msg=err.msg,
3110                 klass=self.__class__,
3111                 decode_path=decode_path,
3112                 offset=offset,
3113             )
3114         if t == self.tag:
3115             if tag_only:
3116                 return None
3117             return self._decode_chunk(lv, offset, decode_path, ctx)
3118         if t == self.tag_constructed:
3119             if not ctx.get("bered", False):
3120                 raise DecodeError(
3121                     "unallowed BER constructed encoding",
3122                     klass=self.__class__,
3123                     decode_path=decode_path,
3124                     offset=offset,
3125                 )
3126             if tag_only:
3127                 return None
3128             lenindef = False
3129             try:
3130                 l, llen, v = len_decode(lv)
3131             except LenIndefForm:
3132                 llen, l, v = 1, 0, lv[1:]
3133                 lenindef = True
3134             except DecodeError as err:
3135                 raise err.__class__(
3136                     msg=err.msg,
3137                     klass=self.__class__,
3138                     decode_path=decode_path,
3139                     offset=offset,
3140                 )
3141             if l > len(v):
3142                 raise NotEnoughData(
3143                     "encoded length is longer than data",
3144                     klass=self.__class__,
3145                     decode_path=decode_path,
3146                     offset=offset,
3147                 )
3148             chunks = []
3149             sub_offset = offset + tlen + llen
3150             vlen = 0
3151             while True:
3152                 if lenindef:
3153                     if v[:EOC_LEN].tobytes() == EOC:
3154                         break
3155                 else:
3156                     if vlen == l:
3157                         break
3158                     if vlen > l:
3159                         raise DecodeError(
3160                             "chunk out of bounds",
3161                             klass=self.__class__,
3162                             decode_path=decode_path + (str(len(chunks) - 1),),
3163                             offset=chunks[-1].offset,
3164                         )
3165                 sub_decode_path = decode_path + (str(len(chunks)),)
3166                 try:
3167                     chunk, v_tail = OctetString().decode(
3168                         v,
3169                         offset=sub_offset,
3170                         decode_path=sub_decode_path,
3171                         leavemm=True,
3172                         ctx=ctx,
3173                         _ctx_immutable=False,
3174                     )
3175                 except TagMismatch:
3176                     raise DecodeError(
3177                         "expected OctetString encoded chunk",
3178                         klass=self.__class__,
3179                         decode_path=sub_decode_path,
3180                         offset=sub_offset,
3181                     )
3182                 chunks.append(chunk)
3183                 sub_offset += chunk.tlvlen
3184                 vlen += chunk.tlvlen
3185                 v = v_tail
3186             try:
3187                 obj = self.__class__(
3188                     value=b"".join(bytes(chunk) for chunk in chunks),
3189                     bounds=(self._bound_min, self._bound_max),
3190                     impl=self.tag,
3191                     expl=self._expl,
3192                     default=self.default,
3193                     optional=self.optional,
3194                     _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3195                     ctx=ctx,
3196                 )
3197             except DecodeError as err:
3198                 raise DecodeError(
3199                     msg=err.msg,
3200                     klass=self.__class__,
3201                     decode_path=decode_path,
3202                     offset=offset,
3203                 )
3204             except BoundsError as err:
3205                 raise DecodeError(
3206                     msg=str(err),
3207                     klass=self.__class__,
3208                     decode_path=decode_path,
3209                     offset=offset,
3210                 )
3211             obj.lenindef = lenindef
3212             obj.ber_encoded = True
3213             return obj, (v[EOC_LEN:] if lenindef else v)
3214         raise TagMismatch(
3215             klass=self.__class__,
3216             decode_path=decode_path,
3217             offset=offset,
3218         )
3219
3220     def __repr__(self):
3221         return pp_console_row(next(self.pps()))
3222
3223     def pps(self, decode_path=()):
3224         yield _pp(
3225             obj=self,
3226             asn1_type_name=self.asn1_type_name,
3227             obj_name=self.__class__.__name__,
3228             decode_path=decode_path,
3229             value=("%d bytes" % len(self._value)) if self.ready else None,
3230             blob=self._value if self.ready else None,
3231             optional=self.optional,
3232             default=self == self.default,
3233             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3234             expl=None if self._expl is None else tag_decode(self._expl),
3235             offset=self.offset,
3236             tlen=self.tlen,
3237             llen=self.llen,
3238             vlen=self.vlen,
3239             expl_offset=self.expl_offset if self.expled else None,
3240             expl_tlen=self.expl_tlen if self.expled else None,
3241             expl_llen=self.expl_llen if self.expled else None,
3242             expl_vlen=self.expl_vlen if self.expled else None,
3243             expl_lenindef=self.expl_lenindef,
3244             lenindef=self.lenindef,
3245             ber_encoded=self.ber_encoded,
3246             bered=self.bered,
3247         )
3248         defined_by, defined = self.defined or (None, None)
3249         if defined_by is not None:
3250             yield defined.pps(
3251                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3252             )
3253         for pp in self.pps_lenindef(decode_path):
3254             yield pp
3255
3256
3257 NullState = namedtuple("NullState", (
3258     "version",
3259     "tag",
3260     "expl",
3261     "default",
3262     "optional",
3263     "offset",
3264     "llen",
3265     "vlen",
3266     "expl_lenindef",
3267     "lenindef",
3268     "ber_encoded",
3269 ), **NAMEDTUPLE_KWARGS)
3270
3271
3272 class Null(Obj):
3273     """``NULL`` null object
3274
3275     >>> n = Null()
3276     NULL
3277     >>> n.ready
3278     True
3279     """
3280     __slots__ = ()
3281     tag_default = tag_encode(5)
3282     asn1_type_name = "NULL"
3283
3284     def __init__(
3285             self,
3286             value=None,  # unused, but Sequence passes it
3287             impl=None,
3288             expl=None,
3289             optional=False,
3290             _decoded=(0, 0, 0),
3291     ):
3292         """
3293         :param bytes impl: override default tag with ``IMPLICIT`` one
3294         :param bytes expl: override default tag with ``EXPLICIT`` one
3295         :param bool optional: is object ``OPTIONAL`` in sequence
3296         """
3297         super(Null, self).__init__(impl, expl, None, optional, _decoded)
3298         self.default = None
3299
3300     @property
3301     def ready(self):
3302         return True
3303
3304     def __getstate__(self):
3305         return NullState(
3306             __version__,
3307             self.tag,
3308             self._expl,
3309             self.default,
3310             self.optional,
3311             self.offset,
3312             self.llen,
3313             self.vlen,
3314             self.expl_lenindef,
3315             self.lenindef,
3316             self.ber_encoded,
3317         )
3318
3319     def __setstate__(self, state):
3320         super(Null, self).__setstate__(state)
3321         self.tag = state.tag
3322         self._expl = state.expl
3323         self.default = state.default
3324         self.optional = state.optional
3325         self.offset = state.offset
3326         self.llen = state.llen
3327         self.vlen = state.vlen
3328         self.expl_lenindef = state.expl_lenindef
3329         self.lenindef = state.lenindef
3330         self.ber_encoded = state.ber_encoded
3331
3332     def __eq__(self, their):
3333         if not issubclass(their.__class__, Null):
3334             return False
3335         return (
3336             self.tag == their.tag and
3337             self._expl == their._expl
3338         )
3339
3340     def __call__(
3341             self,
3342             value=None,
3343             impl=None,
3344             expl=None,
3345             optional=None,
3346     ):
3347         return self.__class__(
3348             impl=self.tag if impl is None else impl,
3349             expl=self._expl if expl is None else expl,
3350             optional=self.optional if optional is None else optional,
3351         )
3352
3353     def _encode(self):
3354         return self.tag + len_encode(0)
3355
3356     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3357         try:
3358             t, _, lv = tag_strip(tlv)
3359         except DecodeError as err:
3360             raise err.__class__(
3361                 msg=err.msg,
3362                 klass=self.__class__,
3363                 decode_path=decode_path,
3364                 offset=offset,
3365             )
3366         if t != self.tag:
3367             raise TagMismatch(
3368                 klass=self.__class__,
3369                 decode_path=decode_path,
3370                 offset=offset,
3371             )
3372         if tag_only:  # pragma: no cover
3373             return None
3374         try:
3375             l, _, v = len_decode(lv)
3376         except DecodeError as err:
3377             raise err.__class__(
3378                 msg=err.msg,
3379                 klass=self.__class__,
3380                 decode_path=decode_path,
3381                 offset=offset,
3382             )
3383         if l != 0:
3384             raise InvalidLength(
3385                 "Null must have zero length",
3386                 klass=self.__class__,
3387                 decode_path=decode_path,
3388                 offset=offset,
3389             )
3390         obj = self.__class__(
3391             impl=self.tag,
3392             expl=self._expl,
3393             optional=self.optional,
3394             _decoded=(offset, 1, 0),
3395         )
3396         return obj, v
3397
3398     def __repr__(self):
3399         return pp_console_row(next(self.pps()))
3400
3401     def pps(self, decode_path=()):
3402         yield _pp(
3403             obj=self,
3404             asn1_type_name=self.asn1_type_name,
3405             obj_name=self.__class__.__name__,
3406             decode_path=decode_path,
3407             optional=self.optional,
3408             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3409             expl=None if self._expl is None else tag_decode(self._expl),
3410             offset=self.offset,
3411             tlen=self.tlen,
3412             llen=self.llen,
3413             vlen=self.vlen,
3414             expl_offset=self.expl_offset if self.expled else None,
3415             expl_tlen=self.expl_tlen if self.expled else None,
3416             expl_llen=self.expl_llen if self.expled else None,
3417             expl_vlen=self.expl_vlen if self.expled else None,
3418             expl_lenindef=self.expl_lenindef,
3419             bered=self.bered,
3420         )
3421         for pp in self.pps_lenindef(decode_path):
3422             yield pp
3423
3424
3425 ObjectIdentifierState = namedtuple("ObjectIdentifierState", (
3426     "version",
3427     "value",
3428     "tag",
3429     "expl",
3430     "default",
3431     "optional",
3432     "offset",
3433     "llen",
3434     "vlen",
3435     "expl_lenindef",
3436     "lenindef",
3437     "ber_encoded",
3438     "defines",
3439 ), **NAMEDTUPLE_KWARGS)
3440
3441
3442 class ObjectIdentifier(Obj):
3443     """``OBJECT IDENTIFIER`` OID type
3444
3445     >>> oid = ObjectIdentifier((1, 2, 3))
3446     OBJECT IDENTIFIER 1.2.3
3447     >>> oid == ObjectIdentifier("1.2.3")
3448     True
3449     >>> tuple(oid)
3450     (1, 2, 3)
3451     >>> str(oid)
3452     '1.2.3'
3453     >>> oid + (4, 5) + ObjectIdentifier("1.7")
3454     OBJECT IDENTIFIER 1.2.3.4.5.1.7
3455
3456     >>> str(ObjectIdentifier((3, 1)))
3457     Traceback (most recent call last):
3458     pyderasn.InvalidOID: unacceptable first arc value
3459     """
3460     __slots__ = ("defines",)
3461     tag_default = tag_encode(6)
3462     asn1_type_name = "OBJECT IDENTIFIER"
3463
3464     def __init__(
3465             self,
3466             value=None,
3467             defines=(),
3468             impl=None,
3469             expl=None,
3470             default=None,
3471             optional=False,
3472             _decoded=(0, 0, 0),
3473     ):
3474         """
3475         :param value: set the value. Either tuples of integers,
3476                       string of "."-concatenated integers, or
3477                       :py:class:`pyderasn.ObjectIdentifier` object
3478         :param defines: sequence of tuples. Each tuple has two elements.
3479                         First one is relative to current one decode
3480                         path, aiming to the field defined by that OID.
3481                         Read about relative path in
3482                         :py:func:`pyderasn.abs_decode_path`. Second
3483                         tuple element is ``{OID: pyderasn.Obj()}``
3484                         dictionary, mapping between current OID value
3485                         and structure applied to defined field.
3486                         :ref:`Read about DEFINED BY <definedby>`
3487         :param bytes impl: override default tag with ``IMPLICIT`` one
3488         :param bytes expl: override default tag with ``EXPLICIT`` one
3489         :param default: set default value. Type same as in ``value``
3490         :param bool optional: is object ``OPTIONAL`` in sequence
3491         """
3492         super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
3493         self._value = value
3494         if value is not None:
3495             self._value = self._value_sanitize(value)
3496         if default is not None:
3497             default = self._value_sanitize(default)
3498             self.default = self.__class__(
3499                 value=default,
3500                 impl=self.tag,
3501                 expl=self._expl,
3502             )
3503             if self._value is None:
3504                 self._value = default
3505         self.defines = defines
3506
3507     def __add__(self, their):
3508         if their.__class__ == tuple:
3509             return self.__class__(self._value + their)
3510         if isinstance(their, self.__class__):
3511             return self.__class__(self._value + their._value)
3512         raise InvalidValueType((self.__class__, tuple))
3513
3514     def _value_sanitize(self, value):
3515         if issubclass(value.__class__, ObjectIdentifier):
3516             return value._value
3517         if isinstance(value, string_types):
3518             try:
3519                 value = tuple(pureint(arc) for arc in value.split("."))
3520             except ValueError:
3521                 raise InvalidOID("unacceptable arcs values")
3522         if value.__class__ == tuple:
3523             if len(value) < 2:
3524                 raise InvalidOID("less than 2 arcs")
3525             first_arc = value[0]
3526             if first_arc in (0, 1):
3527                 if not (0 <= value[1] <= 39):
3528                     raise InvalidOID("second arc is too wide")
3529             elif first_arc == 2:
3530                 pass
3531             else:
3532                 raise InvalidOID("unacceptable first arc value")
3533             if not all(arc >= 0 for arc in value):
3534                 raise InvalidOID("negative arc value")
3535             return value
3536         raise InvalidValueType((self.__class__, str, tuple))
3537
3538     @property
3539     def ready(self):
3540         return self._value is not None
3541
3542     def __getstate__(self):
3543         return ObjectIdentifierState(
3544             __version__,
3545             self._value,
3546             self.tag,
3547             self._expl,
3548             self.default,
3549             self.optional,
3550             self.offset,
3551             self.llen,
3552             self.vlen,
3553             self.expl_lenindef,
3554             self.lenindef,
3555             self.ber_encoded,
3556             self.defines,
3557         )
3558
3559     def __setstate__(self, state):
3560         super(ObjectIdentifier, self).__setstate__(state)
3561         self._value = state.value
3562         self.tag = state.tag
3563         self._expl = state.expl
3564         self.default = state.default
3565         self.optional = state.optional
3566         self.offset = state.offset
3567         self.llen = state.llen
3568         self.vlen = state.vlen
3569         self.expl_lenindef = state.expl_lenindef
3570         self.lenindef = state.lenindef
3571         self.ber_encoded = state.ber_encoded
3572         self.defines = state.defines
3573
3574     def __iter__(self):
3575         self._assert_ready()
3576         return iter(self._value)
3577
3578     def __str__(self):
3579         return ".".join(str(arc) for arc in self._value or ())
3580
3581     def __hash__(self):
3582         self._assert_ready()
3583         return hash(
3584             self.tag +
3585             bytes(self._expl or b"") +
3586             str(self._value).encode("ascii"),
3587         )
3588
3589     def __eq__(self, their):
3590         if their.__class__ == tuple:
3591             return self._value == their
3592         if not issubclass(their.__class__, ObjectIdentifier):
3593             return False
3594         return (
3595             self.tag == their.tag and
3596             self._expl == their._expl and
3597             self._value == their._value
3598         )
3599
3600     def __lt__(self, their):
3601         return self._value < their._value
3602
3603     def __call__(
3604             self,
3605             value=None,
3606             defines=None,
3607             impl=None,
3608             expl=None,
3609             default=None,
3610             optional=None,
3611     ):
3612         return self.__class__(
3613             value=value,
3614             defines=self.defines if defines is None else defines,
3615             impl=self.tag if impl is None else impl,
3616             expl=self._expl if expl is None else expl,
3617             default=self.default if default is None else default,
3618             optional=self.optional if optional is None else optional,
3619         )
3620
3621     def _encode(self):
3622         self._assert_ready()
3623         value = self._value
3624         first_value = value[1]
3625         first_arc = value[0]
3626         if first_arc == 0:
3627             pass
3628         elif first_arc == 1:
3629             first_value += 40
3630         elif first_arc == 2:
3631             first_value += 80
3632         else:  # pragma: no cover
3633             raise RuntimeError("invalid arc is stored")
3634         octets = [zero_ended_encode(first_value)]
3635         for arc in value[2:]:
3636             octets.append(zero_ended_encode(arc))
3637         v = b"".join(octets)
3638         return b"".join((self.tag, len_encode(len(v)), v))
3639
3640     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3641         try:
3642             t, _, lv = tag_strip(tlv)
3643         except DecodeError as err:
3644             raise err.__class__(
3645                 msg=err.msg,
3646                 klass=self.__class__,
3647                 decode_path=decode_path,
3648                 offset=offset,
3649             )
3650         if t != self.tag:
3651             raise TagMismatch(
3652                 klass=self.__class__,
3653                 decode_path=decode_path,
3654                 offset=offset,
3655             )
3656         if tag_only:  # pragma: no cover
3657             return None
3658         try:
3659             l, llen, v = len_decode(lv)
3660         except DecodeError as err:
3661             raise err.__class__(
3662                 msg=err.msg,
3663                 klass=self.__class__,
3664                 decode_path=decode_path,
3665                 offset=offset,
3666             )
3667         if l > len(v):
3668             raise NotEnoughData(
3669                 "encoded length is longer than data",
3670                 klass=self.__class__,
3671                 decode_path=decode_path,
3672                 offset=offset,
3673             )
3674         if l == 0:
3675             raise NotEnoughData(
3676                 "zero length",
3677                 klass=self.__class__,
3678                 decode_path=decode_path,
3679                 offset=offset,
3680             )
3681         v, tail = v[:l], v[l:]
3682         arcs = []
3683         ber_encoded = False
3684         while len(v) > 0:
3685             i = 0
3686             arc = 0
3687             while True:
3688                 octet = indexbytes(v, i)
3689                 if i == 0 and octet == 0x80:
3690                     if ctx.get("bered", False):
3691                         ber_encoded = True
3692                     else:
3693                         raise DecodeError("non normalized arc encoding")
3694                 arc = (arc << 7) | (octet & 0x7F)
3695                 if octet & 0x80 == 0:
3696                     arcs.append(arc)
3697                     v = v[i + 1:]
3698                     break
3699                 i += 1
3700                 if i == len(v):
3701                     raise DecodeError(
3702                         "unfinished OID",
3703                         klass=self.__class__,
3704                         decode_path=decode_path,
3705                         offset=offset,
3706                     )
3707         first_arc = 0
3708         second_arc = arcs[0]
3709         if 0 <= second_arc <= 39:
3710             first_arc = 0
3711         elif 40 <= second_arc <= 79:
3712             first_arc = 1
3713             second_arc -= 40
3714         else:
3715             first_arc = 2
3716             second_arc -= 80
3717         obj = self.__class__(
3718             value=tuple([first_arc, second_arc] + arcs[1:]),
3719             impl=self.tag,
3720             expl=self._expl,
3721             default=self.default,
3722             optional=self.optional,
3723             _decoded=(offset, llen, l),
3724         )
3725         if ber_encoded:
3726             obj.ber_encoded = True
3727         return obj, tail
3728
3729     def __repr__(self):
3730         return pp_console_row(next(self.pps()))
3731
3732     def pps(self, decode_path=()):
3733         yield _pp(
3734             obj=self,
3735             asn1_type_name=self.asn1_type_name,
3736             obj_name=self.__class__.__name__,
3737             decode_path=decode_path,
3738             value=str(self) if self.ready else None,
3739             optional=self.optional,
3740             default=self == self.default,
3741             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3742             expl=None if self._expl is None else tag_decode(self._expl),
3743             offset=self.offset,
3744             tlen=self.tlen,
3745             llen=self.llen,
3746             vlen=self.vlen,
3747             expl_offset=self.expl_offset if self.expled else None,
3748             expl_tlen=self.expl_tlen if self.expled else None,
3749             expl_llen=self.expl_llen if self.expled else None,
3750             expl_vlen=self.expl_vlen if self.expled else None,
3751             expl_lenindef=self.expl_lenindef,
3752             ber_encoded=self.ber_encoded,
3753             bered=self.bered,
3754         )
3755         for pp in self.pps_lenindef(decode_path):
3756             yield pp
3757
3758
3759 class Enumerated(Integer):
3760     """``ENUMERATED`` integer type
3761
3762     This type is identical to :py:class:`pyderasn.Integer`, but requires
3763     schema to be specified and does not accept values missing from it.
3764     """
3765     __slots__ = ()
3766     tag_default = tag_encode(10)
3767     asn1_type_name = "ENUMERATED"
3768
3769     def __init__(
3770             self,
3771             value=None,
3772             impl=None,
3773             expl=None,
3774             default=None,
3775             optional=False,
3776             _specs=None,
3777             _decoded=(0, 0, 0),
3778             bounds=None,  # dummy argument, workability for Integer.decode
3779     ):
3780         super(Enumerated, self).__init__(
3781             value, bounds, impl, expl, default, optional, _specs, _decoded,
3782         )
3783         if len(self.specs) == 0:
3784             raise ValueError("schema must be specified")
3785
3786     def _value_sanitize(self, value):
3787         if isinstance(value, self.__class__):
3788             value = value._value
3789         elif isinstance(value, integer_types):
3790             for _value in itervalues(self.specs):
3791                 if _value == value:
3792                     break
3793             else:
3794                 raise DecodeError(
3795                     "unknown integer value: %s" % value,
3796                     klass=self.__class__,
3797                 )
3798         elif isinstance(value, string_types):
3799             value = self.specs.get(value)
3800             if value is None:
3801                 raise ObjUnknown("integer value: %s" % value)
3802         else:
3803             raise InvalidValueType((self.__class__, int, str))
3804         return value
3805
3806     def __call__(
3807             self,
3808             value=None,
3809             impl=None,
3810             expl=None,
3811             default=None,
3812             optional=None,
3813             _specs=None,
3814     ):
3815         return self.__class__(
3816             value=value,
3817             impl=self.tag if impl is None else impl,
3818             expl=self._expl if expl is None else expl,
3819             default=self.default if default is None else default,
3820             optional=self.optional if optional is None else optional,
3821             _specs=self.specs,
3822         )
3823
3824
3825 def escape_control_unicode(c):
3826     if unicat(c)[0] == "C":
3827         c = repr(c).lstrip("u").strip("'")
3828     return c
3829
3830
3831 class CommonString(OctetString):
3832     """Common class for all strings
3833
3834     Everything resembles :py:class:`pyderasn.OctetString`, except
3835     ability to deal with unicode text strings.
3836
3837     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
3838     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3839     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
3840     True
3841     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
3842     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
3843     >>> str(s)
3844     'привет Ð¼Ð¸Ñ€'
3845     >>> hexenc(bytes(s))
3846     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3847
3848     >>> PrintableString("привет Ð¼Ð¸Ñ€")
3849     Traceback (most recent call last):
3850     pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3851
3852     >>> BMPString("ада", bounds=(2, 2))
3853     Traceback (most recent call last):
3854     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3855     >>> s = BMPString("ад", bounds=(2, 2))
3856     >>> s.encoding
3857     'utf-16-be'
3858     >>> hexenc(bytes(s))
3859     '04300434'
3860
3861     .. list-table::
3862        :header-rows: 1
3863
3864        * - Class
3865          - Text Encoding
3866        * - :py:class:`pyderasn.UTF8String`
3867          - utf-8
3868        * - :py:class:`pyderasn.NumericString`
3869          - ascii
3870        * - :py:class:`pyderasn.PrintableString`
3871          - ascii
3872        * - :py:class:`pyderasn.TeletexString`
3873          - ascii
3874        * - :py:class:`pyderasn.T61String`
3875          - ascii
3876        * - :py:class:`pyderasn.VideotexString`
3877          - iso-8859-1
3878        * - :py:class:`pyderasn.IA5String`
3879          - ascii
3880        * - :py:class:`pyderasn.GraphicString`
3881          - iso-8859-1
3882        * - :py:class:`pyderasn.VisibleString`
3883          - ascii
3884        * - :py:class:`pyderasn.ISO646String`
3885          - ascii
3886        * - :py:class:`pyderasn.GeneralString`
3887          - iso-8859-1
3888        * - :py:class:`pyderasn.UniversalString`
3889          - utf-32-be
3890        * - :py:class:`pyderasn.BMPString`
3891          - utf-16-be
3892     """
3893     __slots__ = ()
3894
3895     def _value_sanitize(self, value):
3896         value_raw = None
3897         value_decoded = None
3898         if isinstance(value, self.__class__):
3899             value_raw = value._value
3900         elif value.__class__ == text_type:
3901             value_decoded = value
3902         elif value.__class__ == binary_type:
3903             value_raw = value
3904         else:
3905             raise InvalidValueType((self.__class__, text_type, binary_type))
3906         try:
3907             value_raw = (
3908                 value_decoded.encode(self.encoding)
3909                 if value_raw is None else value_raw
3910             )
3911             value_decoded = (
3912                 value_raw.decode(self.encoding)
3913                 if value_decoded is None else value_decoded
3914             )
3915         except (UnicodeEncodeError, UnicodeDecodeError) as err:
3916             raise DecodeError(str(err))
3917         if not self._bound_min <= len(value_decoded) <= self._bound_max:
3918             raise BoundsError(
3919                 self._bound_min,
3920                 len(value_decoded),
3921                 self._bound_max,
3922             )
3923         return value_raw
3924
3925     def __eq__(self, their):
3926         if their.__class__ == binary_type:
3927             return self._value == their
3928         if their.__class__ == text_type:
3929             return self._value == their.encode(self.encoding)
3930         if not isinstance(their, self.__class__):
3931             return False
3932         return (
3933             self._value == their._value and
3934             self.tag == their.tag and
3935             self._expl == their._expl
3936         )
3937
3938     def __unicode__(self):
3939         if self.ready:
3940             return self._value.decode(self.encoding)
3941         return text_type(self._value)
3942
3943     def __repr__(self):
3944         return pp_console_row(next(self.pps(no_unicode=PY2)))
3945
3946     def pps(self, decode_path=(), no_unicode=False):
3947         value = None
3948         if self.ready:
3949             value = (
3950                 hexenc(bytes(self)) if no_unicode else
3951                 "".join(escape_control_unicode(c) for c in self.__unicode__())
3952             )
3953         yield _pp(
3954             obj=self,
3955             asn1_type_name=self.asn1_type_name,
3956             obj_name=self.__class__.__name__,
3957             decode_path=decode_path,
3958             value=value,
3959             optional=self.optional,
3960             default=self == self.default,
3961             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3962             expl=None if self._expl is None else tag_decode(self._expl),
3963             offset=self.offset,
3964             tlen=self.tlen,
3965             llen=self.llen,
3966             vlen=self.vlen,
3967             expl_offset=self.expl_offset if self.expled else None,
3968             expl_tlen=self.expl_tlen if self.expled else None,
3969             expl_llen=self.expl_llen if self.expled else None,
3970             expl_vlen=self.expl_vlen if self.expled else None,
3971             expl_lenindef=self.expl_lenindef,
3972             ber_encoded=self.ber_encoded,
3973             bered=self.bered,
3974         )
3975         for pp in self.pps_lenindef(decode_path):
3976             yield pp
3977
3978
3979 class UTF8String(CommonString):
3980     __slots__ = ()
3981     tag_default = tag_encode(12)
3982     encoding = "utf-8"
3983     asn1_type_name = "UTF8String"
3984
3985
3986 class AllowableCharsMixin(object):
3987     @property
3988     def allowable_chars(self):
3989         if PY2:
3990             return self._allowable_chars
3991         return frozenset(six_unichr(c) for c in self._allowable_chars)
3992
3993
3994 class NumericString(AllowableCharsMixin, CommonString):
3995     """Numeric string
3996
3997     Its value is properly sanitized: only ASCII digits with spaces can
3998     be stored.
3999
4000     >>> NumericString().allowable_chars
4001     frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4002     """
4003     __slots__ = ()
4004     tag_default = tag_encode(18)
4005     encoding = "ascii"
4006     asn1_type_name = "NumericString"
4007     _allowable_chars = frozenset(digits.encode("ascii") + b" ")
4008
4009     def _value_sanitize(self, value):
4010         value = super(NumericString, self)._value_sanitize(value)
4011         if not frozenset(value) <= self._allowable_chars:
4012             raise DecodeError("non-numeric value")
4013         return value
4014
4015
4016 PrintableStringState = namedtuple(
4017     "PrintableStringState",
4018     OctetStringState._fields + ("allowable_chars",),
4019     **NAMEDTUPLE_KWARGS
4020 )
4021
4022
4023 class PrintableString(AllowableCharsMixin, CommonString):
4024     """Printable string
4025
4026     Its value is properly sanitized: see X.680 41.4 table 10.
4027
4028     >>> PrintableString().allowable_chars
4029     frozenset([' ', "'", ..., 'z'])
4030     >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4031     PrintableString PrintableString foo*bar
4032     >>> obj.allow_asterisk, obj.allow_ampersand
4033     (True, False)
4034     """
4035     __slots__ = ()
4036     tag_default = tag_encode(19)
4037     encoding = "ascii"
4038     asn1_type_name = "PrintableString"
4039     _allowable_chars = frozenset(
4040         (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4041     )
4042     _asterisk = frozenset("*".encode("ascii"))
4043     _ampersand = frozenset("&".encode("ascii"))
4044
4045     def __init__(
4046             self,
4047             value=None,
4048             bounds=None,
4049             impl=None,
4050             expl=None,
4051             default=None,
4052             optional=False,
4053             _decoded=(0, 0, 0),
4054             ctx=None,
4055             allow_asterisk=False,
4056             allow_ampersand=False,
4057     ):
4058         """
4059         :param allow_asterisk: allow asterisk character
4060         :param allow_ampersand: allow ampersand character
4061         """
4062         if allow_asterisk:
4063             self._allowable_chars |= self._asterisk
4064         if allow_ampersand:
4065             self._allowable_chars |= self._ampersand
4066         super(PrintableString, self).__init__(
4067             value, bounds, impl, expl, default, optional, _decoded, ctx,
4068         )
4069
4070     @property
4071     def allow_asterisk(self):
4072         """Is asterisk character allowed?
4073         """
4074         return self._asterisk <= self._allowable_chars
4075
4076     @property
4077     def allow_ampersand(self):
4078         """Is ampersand character allowed?
4079         """
4080         return self._ampersand <= self._allowable_chars
4081
4082     def _value_sanitize(self, value):
4083         value = super(PrintableString, self)._value_sanitize(value)
4084         if not frozenset(value) <= self._allowable_chars:
4085             raise DecodeError("non-printable value")
4086         return value
4087
4088     def __getstate__(self):
4089         return PrintableStringState(
4090             *super(PrintableString, self).__getstate__(),
4091             **{"allowable_chars": self._allowable_chars}
4092         )
4093
4094     def __setstate__(self, state):
4095         super(PrintableString, self).__setstate__(state)
4096         self._allowable_chars = state.allowable_chars
4097
4098     def __call__(
4099             self,
4100             value=None,
4101             bounds=None,
4102             impl=None,
4103             expl=None,
4104             default=None,
4105             optional=None,
4106     ):
4107         return self.__class__(
4108             value=value,
4109             bounds=(
4110                 (self._bound_min, self._bound_max)
4111                 if bounds is None else bounds
4112             ),
4113             impl=self.tag if impl is None else impl,
4114             expl=self._expl if expl is None else expl,
4115             default=self.default if default is None else default,
4116             optional=self.optional if optional is None else optional,
4117             allow_asterisk=self.allow_asterisk,
4118             allow_ampersand=self.allow_ampersand,
4119         )
4120
4121
4122 class TeletexString(CommonString):
4123     __slots__ = ()
4124     tag_default = tag_encode(20)
4125     encoding = "ascii"
4126     asn1_type_name = "TeletexString"
4127
4128
4129 class T61String(TeletexString):
4130     __slots__ = ()
4131     asn1_type_name = "T61String"
4132
4133
4134 class VideotexString(CommonString):
4135     __slots__ = ()
4136     tag_default = tag_encode(21)
4137     encoding = "iso-8859-1"
4138     asn1_type_name = "VideotexString"
4139
4140
4141 class IA5String(CommonString):
4142     __slots__ = ()
4143     tag_default = tag_encode(22)
4144     encoding = "ascii"
4145     asn1_type_name = "IA5"
4146
4147
4148 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4149 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
4150 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
4151
4152
4153 class VisibleString(CommonString):
4154     __slots__ = ()
4155     tag_default = tag_encode(26)
4156     encoding = "ascii"
4157     asn1_type_name = "VisibleString"
4158
4159
4160 UTCTimeState = namedtuple(
4161     "UTCTimeState",
4162     OctetStringState._fields + ("ber_raw",),
4163     **NAMEDTUPLE_KWARGS
4164 )
4165
4166
4167 def str_to_time_fractions(value):
4168     v = pureint(value)
4169     year, v = (v // 10**10), (v % 10**10)
4170     month, v = (v // 10**8), (v % 10**8)
4171     day, v = (v // 10**6), (v % 10**6)
4172     hour, v = (v // 10**4), (v % 10**4)
4173     minute, second = (v // 100), (v % 100)
4174     return year, month, day, hour, minute, second
4175
4176
4177 class UTCTime(VisibleString):
4178     """``UTCTime`` datetime type
4179
4180     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4181     UTCTime UTCTime 2017-09-30T22:07:50
4182     >>> str(t)
4183     '170930220750Z'
4184     >>> bytes(t)
4185     b'170930220750Z'
4186     >>> t.todatetime()
4187     datetime.datetime(2017, 9, 30, 22, 7, 50)
4188     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
4189     datetime.datetime(1957, 9, 30, 22, 7, 50)
4190
4191     If BER encoded value was met, then ``ber_raw`` attribute will hold
4192     its raw representation.
4193
4194     .. warning::
4195
4196        Pay attention that UTCTime can not hold full year, so all years
4197        having < 50 years are treated as 20xx, 19xx otherwise, according
4198        to X.509 recommendation.
4199
4200     .. warning::
4201
4202        No strict validation of UTC offsets are made, but very crude:
4203
4204        * minutes are not exceeding 60
4205        * offset value is not exceeding 14 hours
4206     """
4207     __slots__ = ("ber_raw",)
4208     tag_default = tag_encode(23)
4209     encoding = "ascii"
4210     asn1_type_name = "UTCTime"
4211
4212     def __init__(
4213             self,
4214             value=None,
4215             impl=None,
4216             expl=None,
4217             default=None,
4218             optional=False,
4219             _decoded=(0, 0, 0),
4220             bounds=None,  # dummy argument, workability for OctetString.decode
4221             ctx=None,
4222     ):
4223         """
4224         :param value: set the value. Either datetime type, or
4225                       :py:class:`pyderasn.UTCTime` object
4226         :param bytes impl: override default tag with ``IMPLICIT`` one
4227         :param bytes expl: override default tag with ``EXPLICIT`` one
4228         :param default: set default value. Type same as in ``value``
4229         :param bool optional: is object ``OPTIONAL`` in sequence
4230         """
4231         super(UTCTime, self).__init__(
4232             None, None, impl, expl, None, optional, _decoded, ctx,
4233         )
4234         self._value = value
4235         self.ber_raw = None
4236         if value is not None:
4237             self._value, self.ber_raw = self._value_sanitize(value, ctx)
4238             self.ber_encoded = self.ber_raw is not None
4239         if default is not None:
4240             default, _ = self._value_sanitize(default)
4241             self.default = self.__class__(
4242                 value=default,
4243                 impl=self.tag,
4244                 expl=self._expl,
4245             )
4246             if self._value is None:
4247                 self._value = default
4248             optional = True
4249         self.optional = optional
4250
4251     def _strptime_bered(self, value):
4252         year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
4253         value = value[10:]
4254         if len(value) == 0:
4255             raise ValueError("no timezone")
4256         year += 2000 if year < 50 else 1900
4257         decoded = datetime(year, month, day, hour, minute)
4258         offset = 0
4259         if value[-1] == "Z":
4260             value = value[:-1]
4261         else:
4262             if len(value) < 5:
4263                 raise ValueError("invalid UTC offset")
4264             if value[-5] == "-":
4265                 sign = -1
4266             elif value[-5] == "+":
4267                 sign = 1
4268             else:
4269                 raise ValueError("invalid UTC offset")
4270             v = pureint(value[-4:])
4271             offset, v = (60 * (v % 100)), v // 100
4272             if offset >= 3600:
4273                 raise ValueError("invalid UTC offset minutes")
4274             offset += 3600 * v
4275             if offset > 14 * 3600:
4276                 raise ValueError("too big UTC offset")
4277             offset *= sign
4278             value = value[:-5]
4279         if len(value) == 0:
4280             return offset, decoded
4281         if len(value) != 2:
4282             raise ValueError("invalid UTC offset seconds")
4283         seconds = pureint(value)
4284         if seconds >= 60:
4285             raise ValueError("invalid seconds value")
4286         return offset, decoded + timedelta(seconds=seconds)
4287
4288     def _strptime(self, value):
4289         # datetime.strptime's format: %y%m%d%H%M%SZ
4290         if len(value) != LEN_YYMMDDHHMMSSZ:
4291             raise ValueError("invalid UTCTime length")
4292         if value[-1] != "Z":
4293             raise ValueError("non UTC timezone")
4294         year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
4295         year += 2000 if year < 50 else 1900
4296         return datetime(year, month, day, hour, minute, second)
4297
4298     def _dt_sanitize(self, value):
4299         if value.year < 1950 or value.year > 2049:
4300             raise ValueError("UTCTime can hold only 1950-2049 years")
4301         return value.replace(microsecond=0)
4302
4303     def _value_sanitize(self, value, ctx=None):
4304         if value.__class__ == binary_type:
4305             try:
4306                 value_decoded = value.decode("ascii")
4307             except (UnicodeEncodeError, UnicodeDecodeError) as err:
4308                 raise DecodeError("invalid UTCTime encoding: %r" % err)
4309             err = None
4310             try:
4311                 return self._strptime(value_decoded), None
4312             except (TypeError, ValueError) as _err:
4313                 err = _err
4314                 if (ctx is not None) and ctx.get("bered", False):
4315                     try:
4316                         offset, _value = self._strptime_bered(value_decoded)
4317                         _value = _value - timedelta(seconds=offset)
4318                         return self._dt_sanitize(_value), value
4319                     except (TypeError, ValueError, OverflowError) as _err:
4320                         err = _err
4321             raise DecodeError(
4322                 "invalid %s format: %r" % (self.asn1_type_name, err),
4323                 klass=self.__class__,
4324             )
4325         if isinstance(value, self.__class__):
4326             return value._value, None
4327         if value.__class__ == datetime:
4328             return self._dt_sanitize(value), None
4329         raise InvalidValueType((self.__class__, datetime))
4330
4331     def _pp_value(self):
4332         if self.ready:
4333             value = self._value.isoformat()
4334             if self.ber_encoded:
4335                 value += " (%s)" % self.ber_raw
4336             return value
4337
4338     def __unicode__(self):
4339         if self.ready:
4340             value = self._value.isoformat()
4341             if self.ber_encoded:
4342                 value += " (%s)" % self.ber_raw
4343             return value
4344         return text_type(self._pp_value())
4345
4346     def __getstate__(self):
4347         return UTCTimeState(
4348             *super(UTCTime, self).__getstate__(),
4349             **{"ber_raw": self.ber_raw}
4350         )
4351
4352     def __setstate__(self, state):
4353         super(UTCTime, self).__setstate__(state)
4354         self.ber_raw = state.ber_raw
4355
4356     def __bytes__(self):
4357         self._assert_ready()
4358         return self._encode_time()
4359
4360     def __eq__(self, their):
4361         if their.__class__ == binary_type:
4362             return self._encode_time() == their
4363         if their.__class__ == datetime:
4364             return self.todatetime() == their
4365         if not isinstance(their, self.__class__):
4366             return False
4367         return (
4368             self._value == their._value and
4369             self.tag == their.tag and
4370             self._expl == their._expl
4371         )
4372
4373     def _encode_time(self):
4374         return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
4375
4376     def _encode(self):
4377         self._assert_ready()
4378         value = self._encode_time()
4379         return b"".join((self.tag, len_encode(len(value)), value))
4380
4381     def todatetime(self):
4382         return self._value
4383
4384     def __repr__(self):
4385         return pp_console_row(next(self.pps()))
4386
4387     def pps(self, decode_path=()):
4388         yield _pp(
4389             obj=self,
4390             asn1_type_name=self.asn1_type_name,
4391             obj_name=self.__class__.__name__,
4392             decode_path=decode_path,
4393             value=self._pp_value(),
4394             optional=self.optional,
4395             default=self == self.default,
4396             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4397             expl=None if self._expl is None else tag_decode(self._expl),
4398             offset=self.offset,
4399             tlen=self.tlen,
4400             llen=self.llen,
4401             vlen=self.vlen,
4402             expl_offset=self.expl_offset if self.expled else None,
4403             expl_tlen=self.expl_tlen if self.expled else None,
4404             expl_llen=self.expl_llen if self.expled else None,
4405             expl_vlen=self.expl_vlen if self.expled else None,
4406             expl_lenindef=self.expl_lenindef,
4407             ber_encoded=self.ber_encoded,
4408             bered=self.bered,
4409         )
4410         for pp in self.pps_lenindef(decode_path):
4411             yield pp
4412
4413
4414 class GeneralizedTime(UTCTime):
4415     """``GeneralizedTime`` datetime type
4416
4417     This type is similar to :py:class:`pyderasn.UTCTime`.
4418
4419     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
4420     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
4421     >>> str(t)
4422     '20170930220750.000123Z'
4423     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
4424     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
4425
4426     .. warning::
4427
4428        Only microsecond fractions are supported in DER encoding.
4429        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
4430        higher precision values.
4431
4432     .. warning::
4433
4434        BER encoded data can loss information (accuracy) during decoding
4435        because of float transformations.
4436
4437     .. warning::
4438
4439        Local times (without explicit timezone specification) are treated
4440        as UTC one, no transformations are made.
4441
4442     .. warning::
4443
4444        Zero year is unsupported.
4445     """
4446     __slots__ = ()
4447     tag_default = tag_encode(24)
4448     asn1_type_name = "GeneralizedTime"
4449
4450     def _dt_sanitize(self, value):
4451         return value
4452
4453     def _strptime_bered(self, value):
4454         if len(value) < 4 + 3 * 2:
4455             raise ValueError("invalid GeneralizedTime")
4456         year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
4457         decoded = datetime(year, month, day, hour)
4458         offset, value = 0, value[10:]
4459         if len(value) == 0:
4460             return offset, decoded
4461         if value[-1] == "Z":
4462             value = value[:-1]
4463         else:
4464             for char, sign in (("-", -1), ("+", 1)):
4465                 idx = value.rfind(char)
4466                 if idx == -1:
4467                     continue
4468                 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
4469                 v = pureint(offset_raw)
4470                 if len(offset_raw) == 4:
4471                     offset, v = (60 * (v % 100)), v // 100
4472                     if offset >= 3600:
4473                         raise ValueError("invalid UTC offset minutes")
4474                 elif len(offset_raw) == 2:
4475                     pass
4476                 else:
4477                     raise ValueError("invalid UTC offset")
4478                 offset += 3600 * v
4479                 if offset > 14 * 3600:
4480                     raise ValueError("too big UTC offset")
4481                 offset *= sign
4482                 break
4483         if len(value) == 0:
4484             return offset, decoded
4485         if value[0] in DECIMAL_SIGNS:
4486             return offset, (
4487                 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
4488             )
4489         if len(value) < 2:
4490             raise ValueError("stripped minutes")
4491         decoded += timedelta(seconds=60 * pureint(value[:2]))
4492         value = value[2:]
4493         if len(value) == 0:
4494             return offset, decoded
4495         if value[0] in DECIMAL_SIGNS:
4496             return offset, (
4497                 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
4498             )
4499         if len(value) < 2:
4500             raise ValueError("stripped seconds")
4501         decoded += timedelta(seconds=pureint(value[:2]))
4502         value = value[2:]
4503         if len(value) == 0:
4504             return offset, decoded
4505         if value[0] not in DECIMAL_SIGNS:
4506             raise ValueError("invalid format after seconds")
4507         return offset, (
4508             decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
4509         )
4510
4511     def _strptime(self, value):
4512         l = len(value)
4513         if l == LEN_YYYYMMDDHHMMSSZ:
4514             # datetime.strptime's format: %Y%m%d%H%M%SZ
4515             if value[-1] != "Z":
4516                 raise ValueError("non UTC timezone")
4517             return datetime(*str_to_time_fractions(value[:-1]))
4518         if l >= LEN_YYYYMMDDHHMMSSDMZ:
4519             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
4520             if value[-1] != "Z":
4521                 raise ValueError("non UTC timezone")
4522             if value[14] != ".":
4523                 raise ValueError("no fractions separator")
4524             us = value[15:-1]
4525             if us[-1] == "0":
4526                 raise ValueError("trailing zero")
4527             us_len = len(us)
4528             if us_len > 6:
4529                 raise ValueError("only microsecond fractions are supported")
4530             us = pureint(us + ("0" * (6 - us_len)))
4531             year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
4532             return datetime(year, month, day, hour, minute, second, us)
4533         raise ValueError("invalid GeneralizedTime length")
4534
4535     def _encode_time(self):
4536         value = self._value
4537         encoded = value.strftime("%Y%m%d%H%M%S")
4538         if value.microsecond > 0:
4539             encoded += (".%06d" % value.microsecond).rstrip("0")
4540         return (encoded + "Z").encode("ascii")
4541
4542
4543 class GraphicString(CommonString):
4544     __slots__ = ()
4545     tag_default = tag_encode(25)
4546     encoding = "iso-8859-1"
4547     asn1_type_name = "GraphicString"
4548
4549
4550 class ISO646String(VisibleString):
4551     __slots__ = ()
4552     asn1_type_name = "ISO646String"
4553
4554
4555 class GeneralString(CommonString):
4556     __slots__ = ()
4557     tag_default = tag_encode(27)
4558     encoding = "iso-8859-1"
4559     asn1_type_name = "GeneralString"
4560
4561
4562 class UniversalString(CommonString):
4563     __slots__ = ()
4564     tag_default = tag_encode(28)
4565     encoding = "utf-32-be"
4566     asn1_type_name = "UniversalString"
4567
4568
4569 class BMPString(CommonString):
4570     __slots__ = ()
4571     tag_default = tag_encode(30)
4572     encoding = "utf-16-be"
4573     asn1_type_name = "BMPString"
4574
4575
4576 ChoiceState = namedtuple("ChoiceState", (
4577     "version",
4578     "specs",
4579     "value",
4580     "tag",
4581     "expl",
4582     "default",
4583     "optional",
4584     "offset",
4585     "llen",
4586     "vlen",
4587     "expl_lenindef",
4588     "lenindef",
4589     "ber_encoded",
4590 ), **NAMEDTUPLE_KWARGS)
4591
4592
4593 class Choice(Obj):
4594     """``CHOICE`` special type
4595
4596     ::
4597
4598         class GeneralName(Choice):
4599             schema = (
4600                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4601                 ("dNSName", IA5String(impl=tag_ctxp(2))),
4602             )
4603
4604     >>> gn = GeneralName()
4605     GeneralName CHOICE
4606     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4607     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4608     >>> gn["dNSName"] = IA5String("bar.baz")
4609     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4610     >>> gn["rfc822Name"]
4611     None
4612     >>> gn["dNSName"]
4613     [2] IA5String IA5 bar.baz
4614     >>> gn.choice
4615     'dNSName'
4616     >>> gn.value == gn["dNSName"]
4617     True
4618     >>> gn.specs
4619     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4620
4621     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4622     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4623     """
4624     __slots__ = ("specs",)
4625     tag_default = None
4626     asn1_type_name = "CHOICE"
4627
4628     def __init__(
4629             self,
4630             value=None,
4631             schema=None,
4632             impl=None,
4633             expl=None,
4634             default=None,
4635             optional=False,
4636             _decoded=(0, 0, 0),
4637     ):
4638         """
4639         :param value: set the value. Either ``(choice, value)`` tuple, or
4640                       :py:class:`pyderasn.Choice` object
4641         :param bytes impl: can not be set, do **not** use it
4642         :param bytes expl: override default tag with ``EXPLICIT`` one
4643         :param default: set default value. Type same as in ``value``
4644         :param bool optional: is object ``OPTIONAL`` in sequence
4645         """
4646         if impl is not None:
4647             raise ValueError("no implicit tag allowed for CHOICE")
4648         super(Choice, self).__init__(None, expl, default, optional, _decoded)
4649         if schema is None:
4650             schema = getattr(self, "schema", ())
4651         if len(schema) == 0:
4652             raise ValueError("schema must be specified")
4653         self.specs = (
4654             schema if schema.__class__ == OrderedDict else OrderedDict(schema)
4655         )
4656         self._value = None
4657         if value is not None:
4658             self._value = self._value_sanitize(value)
4659         if default is not None:
4660             default_value = self._value_sanitize(default)
4661             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4662             default_obj.specs = self.specs
4663             default_obj._value = default_value
4664             self.default = default_obj
4665             if value is None:
4666                 self._value = copy(default_obj._value)
4667
4668     def _value_sanitize(self, value):
4669         if (value.__class__ == tuple) and len(value) == 2:
4670             choice, obj = value
4671             spec = self.specs.get(choice)
4672             if spec is None:
4673                 raise ObjUnknown(choice)
4674             if not isinstance(obj, spec.__class__):
4675                 raise InvalidValueType((spec,))
4676             return (choice, spec(obj))
4677         if isinstance(value, self.__class__):
4678             return value._value
4679         raise InvalidValueType((self.__class__, tuple))
4680
4681     @property
4682     def ready(self):
4683         return self._value is not None and self._value[1].ready
4684
4685     @property
4686     def bered(self):
4687         return self.expl_lenindef or (
4688             (self._value is not None) and
4689             self._value[1].bered
4690         )
4691
4692     def __getstate__(self):
4693         return ChoiceState(
4694             __version__,
4695             self.specs,
4696             copy(self._value),
4697             self.tag,
4698             self._expl,
4699             self.default,
4700             self.optional,
4701             self.offset,
4702             self.llen,
4703             self.vlen,
4704             self.expl_lenindef,
4705             self.lenindef,
4706             self.ber_encoded,
4707         )
4708
4709     def __setstate__(self, state):
4710         super(Choice, self).__setstate__(state)
4711         self.specs = state.specs
4712         self._value = state.value
4713         self._expl = state.expl
4714         self.default = state.default
4715         self.optional = state.optional
4716         self.offset = state.offset
4717         self.llen = state.llen
4718         self.vlen = state.vlen
4719         self.expl_lenindef = state.expl_lenindef
4720         self.lenindef = state.lenindef
4721         self.ber_encoded = state.ber_encoded
4722
4723     def __eq__(self, their):
4724         if (their.__class__ == tuple) and len(their) == 2:
4725             return self._value == their
4726         if not isinstance(their, self.__class__):
4727             return False
4728         return (
4729             self.specs == their.specs and
4730             self._value == their._value
4731         )
4732
4733     def __call__(
4734             self,
4735             value=None,
4736             expl=None,
4737             default=None,
4738             optional=None,
4739     ):
4740         return self.__class__(
4741             value=value,
4742             schema=self.specs,
4743             expl=self._expl if expl is None else expl,
4744             default=self.default if default is None else default,
4745             optional=self.optional if optional is None else optional,
4746         )
4747
4748     @property
4749     def choice(self):
4750         self._assert_ready()
4751         return self._value[0]
4752
4753     @property
4754     def value(self):
4755         self._assert_ready()
4756         return self._value[1]
4757
4758     def __getitem__(self, key):
4759         if key not in self.specs:
4760             raise ObjUnknown(key)
4761         if self._value is None:
4762             return None
4763         choice, value = self._value
4764         if choice != key:
4765             return None
4766         return value
4767
4768     def __setitem__(self, key, value):
4769         spec = self.specs.get(key)
4770         if spec is None:
4771             raise ObjUnknown(key)
4772         if not isinstance(value, spec.__class__):
4773             raise InvalidValueType((spec.__class__,))
4774         self._value = (key, spec(value))
4775
4776     @property
4777     def tlen(self):
4778         return 0
4779
4780     @property
4781     def decoded(self):
4782         return self._value[1].decoded if self.ready else False
4783
4784     def _encode(self):
4785         self._assert_ready()
4786         return self._value[1].encode()
4787
4788     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4789         for choice, spec in iteritems(self.specs):
4790             sub_decode_path = decode_path + (choice,)
4791             try:
4792                 spec.decode(
4793                     tlv,
4794                     offset=offset,
4795                     leavemm=True,
4796                     decode_path=sub_decode_path,
4797                     ctx=ctx,
4798                     tag_only=True,
4799                     _ctx_immutable=False,
4800                 )
4801             except TagMismatch:
4802                 continue
4803             break
4804         else:
4805             raise TagMismatch(
4806                 klass=self.__class__,
4807                 decode_path=decode_path,
4808                 offset=offset,
4809             )
4810         if tag_only:  # pragma: no cover
4811             return None
4812         value, tail = spec.decode(
4813             tlv,
4814             offset=offset,
4815             leavemm=True,
4816             decode_path=sub_decode_path,
4817             ctx=ctx,
4818             _ctx_immutable=False,
4819         )
4820         obj = self.__class__(
4821             schema=self.specs,
4822             expl=self._expl,
4823             default=self.default,
4824             optional=self.optional,
4825             _decoded=(offset, 0, value.fulllen),
4826         )
4827         obj._value = (choice, value)
4828         return obj, tail
4829
4830     def __repr__(self):
4831         value = pp_console_row(next(self.pps()))
4832         if self.ready:
4833             value = "%s[%r]" % (value, self.value)
4834         return value
4835
4836     def pps(self, decode_path=()):
4837         yield _pp(
4838             obj=self,
4839             asn1_type_name=self.asn1_type_name,
4840             obj_name=self.__class__.__name__,
4841             decode_path=decode_path,
4842             value=self.choice if self.ready else None,
4843             optional=self.optional,
4844             default=self == self.default,
4845             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4846             expl=None if self._expl is None else tag_decode(self._expl),
4847             offset=self.offset,
4848             tlen=self.tlen,
4849             llen=self.llen,
4850             vlen=self.vlen,
4851             expl_lenindef=self.expl_lenindef,
4852             bered=self.bered,
4853         )
4854         if self.ready:
4855             yield self.value.pps(decode_path=decode_path + (self.choice,))
4856         for pp in self.pps_lenindef(decode_path):
4857             yield pp
4858
4859
4860 class PrimitiveTypes(Choice):
4861     """Predefined ``CHOICE`` for all generic primitive types
4862
4863     It could be useful for general decoding of some unspecified values:
4864
4865     >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
4866     OCTET STRING 3 bytes 666f6f
4867     >>> PrimitiveTypes().decod(hexdec("0203123456")).value
4868     INTEGER 1193046
4869     """
4870     __slots__ = ()
4871     schema = tuple((klass.__name__, klass()) for klass in (
4872         Boolean,
4873         Integer,
4874         BitString,
4875         OctetString,
4876         Null,
4877         ObjectIdentifier,
4878         UTF8String,
4879         NumericString,
4880         PrintableString,
4881         TeletexString,
4882         VideotexString,
4883         IA5String,
4884         UTCTime,
4885         GeneralizedTime,
4886         GraphicString,
4887         VisibleString,
4888         ISO646String,
4889         GeneralString,
4890         UniversalString,
4891         BMPString,
4892     ))
4893
4894
4895 AnyState = namedtuple("AnyState", (
4896     "version",
4897     "value",
4898     "tag",
4899     "expl",
4900     "optional",
4901     "offset",
4902     "llen",
4903     "vlen",
4904     "expl_lenindef",
4905     "lenindef",
4906     "ber_encoded",
4907     "defined",
4908 ), **NAMEDTUPLE_KWARGS)
4909
4910
4911 class Any(Obj):
4912     """``ANY`` special type
4913
4914     >>> Any(Integer(-123))
4915     ANY 020185
4916     >>> a = Any(OctetString(b"hello world").encode())
4917     ANY 040b68656c6c6f20776f726c64
4918     >>> hexenc(bytes(a))
4919     b'0x040x0bhello world'
4920     """
4921     __slots__ = ("defined",)
4922     tag_default = tag_encode(0)
4923     asn1_type_name = "ANY"
4924
4925     def __init__(
4926             self,
4927             value=None,
4928             expl=None,
4929             optional=False,
4930             _decoded=(0, 0, 0),
4931     ):
4932         """
4933         :param value: set the value. Either any kind of pyderasn's
4934                       **ready** object, or bytes. Pay attention that
4935                       **no** validation is performed is raw binary value
4936                       is valid TLV
4937         :param bytes expl: override default tag with ``EXPLICIT`` one
4938         :param bool optional: is object ``OPTIONAL`` in sequence
4939         """
4940         super(Any, self).__init__(None, expl, None, optional, _decoded)
4941         self._value = None if value is None else self._value_sanitize(value)
4942         self.defined = None
4943
4944     def _value_sanitize(self, value):
4945         if value.__class__ == binary_type:
4946             return value
4947         if isinstance(value, self.__class__):
4948             return value._value
4949         if isinstance(value, Obj):
4950             return value.encode()
4951         raise InvalidValueType((self.__class__, Obj, binary_type))
4952
4953     @property
4954     def ready(self):
4955         return self._value is not None
4956
4957     @property
4958     def bered(self):
4959         if self.expl_lenindef or self.lenindef:
4960             return True
4961         if self.defined is None:
4962             return False
4963         return self.defined[1].bered
4964
4965     def __getstate__(self):
4966         return AnyState(
4967             __version__,
4968             self._value,
4969             self.tag,
4970             self._expl,
4971             self.optional,
4972             self.offset,
4973             self.llen,
4974             self.vlen,
4975             self.expl_lenindef,
4976             self.lenindef,
4977             self.ber_encoded,
4978             self.defined,
4979         )
4980
4981     def __setstate__(self, state):
4982         super(Any, self).__setstate__(state)
4983         self._value = state.value
4984         self.tag = state.tag
4985         self._expl = state.expl
4986         self.optional = state.optional
4987         self.offset = state.offset
4988         self.llen = state.llen
4989         self.vlen = state.vlen
4990         self.expl_lenindef = state.expl_lenindef
4991         self.lenindef = state.lenindef
4992         self.ber_encoded = state.ber_encoded
4993         self.defined = state.defined
4994
4995     def __eq__(self, their):
4996         if their.__class__ == binary_type:
4997             return self._value == their
4998         if issubclass(their.__class__, Any):
4999             return self._value == their._value
5000         return False
5001
5002     def __call__(
5003             self,
5004             value=None,
5005             expl=None,
5006             optional=None,
5007     ):
5008         return self.__class__(
5009             value=value,
5010             expl=self._expl if expl is None else expl,
5011             optional=self.optional if optional is None else optional,
5012         )
5013
5014     def __bytes__(self):
5015         self._assert_ready()
5016         return self._value
5017
5018     @property
5019     def tlen(self):
5020         return 0
5021
5022     def _encode(self):
5023         self._assert_ready()
5024         return self._value
5025
5026     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5027         try:
5028             t, tlen, lv = tag_strip(tlv)
5029         except DecodeError as err:
5030             raise err.__class__(
5031                 msg=err.msg,
5032                 klass=self.__class__,
5033                 decode_path=decode_path,
5034                 offset=offset,
5035             )
5036         try:
5037             l, llen, v = len_decode(lv)
5038         except LenIndefForm as err:
5039             if not ctx.get("bered", False):
5040                 raise err.__class__(
5041                     msg=err.msg,
5042                     klass=self.__class__,
5043                     decode_path=decode_path,
5044                     offset=offset,
5045                 )
5046             llen, vlen, v = 1, 0, lv[1:]
5047             sub_offset = offset + tlen + llen
5048             chunk_i = 0
5049             while v[:EOC_LEN].tobytes() != EOC:
5050                 chunk, v = Any().decode(
5051                     v,
5052                     offset=sub_offset,
5053                     decode_path=decode_path + (str(chunk_i),),
5054                     leavemm=True,
5055                     ctx=ctx,
5056                     _ctx_immutable=False,
5057                 )
5058                 vlen += chunk.tlvlen
5059                 sub_offset += chunk.tlvlen
5060                 chunk_i += 1
5061             tlvlen = tlen + llen + vlen + EOC_LEN
5062             obj = self.__class__(
5063                 value=tlv[:tlvlen].tobytes(),
5064                 expl=self._expl,
5065                 optional=self.optional,
5066                 _decoded=(offset, 0, tlvlen),
5067             )
5068             obj.lenindef = True
5069             obj.tag = t.tobytes()
5070             return obj, v[EOC_LEN:]
5071         except DecodeError as err:
5072             raise err.__class__(
5073                 msg=err.msg,
5074                 klass=self.__class__,
5075                 decode_path=decode_path,
5076                 offset=offset,
5077             )
5078         if l > len(v):
5079             raise NotEnoughData(
5080                 "encoded length is longer than data",
5081                 klass=self.__class__,
5082                 decode_path=decode_path,
5083                 offset=offset,
5084             )
5085         tlvlen = tlen + llen + l
5086         v, tail = tlv[:tlvlen], v[l:]
5087         obj = self.__class__(
5088             value=v.tobytes(),
5089             expl=self._expl,
5090             optional=self.optional,
5091             _decoded=(offset, 0, tlvlen),
5092         )
5093         obj.tag = t.tobytes()
5094         return obj, tail
5095
5096     def __repr__(self):
5097         return pp_console_row(next(self.pps()))
5098
5099     def pps(self, decode_path=()):
5100         yield _pp(
5101             obj=self,
5102             asn1_type_name=self.asn1_type_name,
5103             obj_name=self.__class__.__name__,
5104             decode_path=decode_path,
5105             blob=self._value if self.ready else None,
5106             optional=self.optional,
5107             default=self == self.default,
5108             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5109             expl=None if self._expl is None else tag_decode(self._expl),
5110             offset=self.offset,
5111             tlen=self.tlen,
5112             llen=self.llen,
5113             vlen=self.vlen,
5114             expl_offset=self.expl_offset if self.expled else None,
5115             expl_tlen=self.expl_tlen if self.expled else None,
5116             expl_llen=self.expl_llen if self.expled else None,
5117             expl_vlen=self.expl_vlen if self.expled else None,
5118             expl_lenindef=self.expl_lenindef,
5119             lenindef=self.lenindef,
5120             bered=self.bered,
5121         )
5122         defined_by, defined = self.defined or (None, None)
5123         if defined_by is not None:
5124             yield defined.pps(
5125                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
5126             )
5127         for pp in self.pps_lenindef(decode_path):
5128             yield pp
5129
5130
5131 ########################################################################
5132 # ASN.1 constructed types
5133 ########################################################################
5134
5135 def get_def_by_path(defines_by_path, sub_decode_path):
5136     """Get define by decode path
5137     """
5138     for path, define in defines_by_path:
5139         if len(path) != len(sub_decode_path):
5140             continue
5141         for p1, p2 in zip(path, sub_decode_path):
5142             if (p1 != any) and (p1 != p2):
5143                 break
5144         else:
5145             return define
5146
5147
5148 def abs_decode_path(decode_path, rel_path):
5149     """Create an absolute decode path from current and relative ones
5150
5151     :param decode_path: current decode path, starting point. Tuple of strings
5152     :param rel_path: relative path to ``decode_path``. Tuple of strings.
5153                      If first tuple's element is "/", then treat it as
5154                      an absolute path, ignoring ``decode_path`` as
5155                      starting point. Also this tuple can contain ".."
5156                      elements, stripping the leading element from
5157                      ``decode_path``
5158
5159     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
5160     ("foo", "bar", "baz", "whatever")
5161     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
5162     ("foo", "whatever")
5163     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
5164     ("baz", "whatever")
5165     """
5166     if rel_path[0] == "/":
5167         return rel_path[1:]
5168     if rel_path[0] == "..":
5169         return abs_decode_path(decode_path[:-1], rel_path[1:])
5170     return decode_path + rel_path
5171
5172
5173 SequenceState = namedtuple("SequenceState", (
5174     "version",
5175     "specs",
5176     "value",
5177     "tag",
5178     "expl",
5179     "default",
5180     "optional",
5181     "offset",
5182     "llen",
5183     "vlen",
5184     "expl_lenindef",
5185     "lenindef",
5186     "ber_encoded",
5187 ), **NAMEDTUPLE_KWARGS)
5188
5189
5190 class Sequence(Obj):
5191     """``SEQUENCE`` structure type
5192
5193     You have to make specification of sequence::
5194
5195         class Extension(Sequence):
5196             schema = (
5197                 ("extnID", ObjectIdentifier()),
5198                 ("critical", Boolean(default=False)),
5199                 ("extnValue", OctetString()),
5200             )
5201
5202     Then, you can work with it as with dictionary.
5203
5204     >>> ext = Extension()
5205     >>> Extension().specs
5206     OrderedDict([
5207         ('extnID', OBJECT IDENTIFIER),
5208         ('critical', BOOLEAN False OPTIONAL DEFAULT),
5209         ('extnValue', OCTET STRING),
5210     ])
5211     >>> ext["extnID"] = "1.2.3"
5212     Traceback (most recent call last):
5213     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
5214     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
5215
5216     You can determine if sequence is ready to be encoded:
5217
5218     >>> ext.ready
5219     False
5220     >>> ext.encode()
5221     Traceback (most recent call last):
5222     pyderasn.ObjNotReady: object is not ready: extnValue
5223     >>> ext["extnValue"] = OctetString(b"foobar")
5224     >>> ext.ready
5225     True
5226
5227     Value you want to assign, must have the same **type** as in
5228     corresponding specification, but it can have different tags,
5229     optional/default attributes -- they will be taken from specification
5230     automatically::
5231
5232         class TBSCertificate(Sequence):
5233             schema = (
5234                 ("version", Version(expl=tag_ctxc(0), default="v1")),
5235             [...]
5236
5237     >>> tbs = TBSCertificate()
5238     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
5239
5240     Assign ``None`` to remove value from sequence.
5241
5242     You can set values in Sequence during its initialization:
5243
5244     >>> AlgorithmIdentifier((
5245         ("algorithm", ObjectIdentifier("1.2.3")),
5246         ("parameters", Any(Null()))
5247     ))
5248     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
5249
5250     You can determine if value exists/set in the sequence and take its value:
5251
5252     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
5253     (True, True, False)
5254     >>> ext["extnID"]
5255     OBJECT IDENTIFIER 1.2.3
5256
5257     But pay attention that if value has default, then it won't be (not
5258     in) in the sequence (because ``DEFAULT`` must not be encoded in
5259     DER), but you can read its value:
5260
5261     >>> "critical" in ext, ext["critical"]
5262     (False, BOOLEAN False)
5263     >>> ext["critical"] = Boolean(True)
5264     >>> "critical" in ext, ext["critical"]
5265     (True, BOOLEAN True)
5266
5267     All defaulted values are always optional.
5268
5269     .. _allow_default_values_ctx:
5270
5271     DER prohibits default value encoding and will raise an error if
5272     default value is unexpectedly met during decode.
5273     If :ref:`bered <bered_ctx>` context option is set, then no error
5274     will be raised, but ``bered`` attribute set. You can disable strict
5275     defaulted values existence validation by setting
5276     ``"allow_default_values": True`` :ref:`context <ctx>` option.
5277
5278     Two sequences are equal if they have equal specification (schema),
5279     implicit/explicit tagging and the same values.
5280     """
5281     __slots__ = ("specs",)
5282     tag_default = tag_encode(form=TagFormConstructed, num=16)
5283     asn1_type_name = "SEQUENCE"
5284
5285     def __init__(
5286             self,
5287             value=None,
5288             schema=None,
5289             impl=None,
5290             expl=None,
5291             default=None,
5292             optional=False,
5293             _decoded=(0, 0, 0),
5294     ):
5295         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
5296         if schema is None:
5297             schema = getattr(self, "schema", ())
5298         self.specs = (
5299             schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5300         )
5301         self._value = {}
5302         if value is not None:
5303             if issubclass(value.__class__, Sequence):
5304                 self._value = value._value
5305             elif hasattr(value, "__iter__"):
5306                 for seq_key, seq_value in value:
5307                     self[seq_key] = seq_value
5308             else:
5309                 raise InvalidValueType((Sequence,))
5310         if default is not None:
5311             if not issubclass(default.__class__, Sequence):
5312                 raise InvalidValueType((Sequence,))
5313             default_value = default._value
5314             default_obj = self.__class__(impl=self.tag, expl=self._expl)
5315             default_obj.specs = self.specs
5316             default_obj._value = default_value
5317             self.default = default_obj
5318             if value is None:
5319                 self._value = copy(default_obj._value)
5320
5321     @property
5322     def ready(self):
5323         for name, spec in iteritems(self.specs):
5324             value = self._value.get(name)
5325             if value is None:
5326                 if spec.optional:
5327                     continue
5328                 return False
5329             if not value.ready:
5330                 return False
5331         return True
5332
5333     @property
5334     def bered(self):
5335         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5336             return True
5337         return any(value.bered for value in itervalues(self._value))
5338
5339     def __getstate__(self):
5340         return SequenceState(
5341             __version__,
5342             self.specs,
5343             {k: copy(v) for k, v in iteritems(self._value)},
5344             self.tag,
5345             self._expl,
5346             self.default,
5347             self.optional,
5348             self.offset,
5349             self.llen,
5350             self.vlen,
5351             self.expl_lenindef,
5352             self.lenindef,
5353             self.ber_encoded,
5354         )
5355
5356     def __setstate__(self, state):
5357         super(Sequence, self).__setstate__(state)
5358         self.specs = state.specs
5359         self._value = state.value
5360         self.tag = state.tag
5361         self._expl = state.expl
5362         self.default = state.default
5363         self.optional = state.optional
5364         self.offset = state.offset
5365         self.llen = state.llen
5366         self.vlen = state.vlen
5367         self.expl_lenindef = state.expl_lenindef
5368         self.lenindef = state.lenindef
5369         self.ber_encoded = state.ber_encoded
5370
5371     def __eq__(self, their):
5372         if not isinstance(their, self.__class__):
5373             return False
5374         return (
5375             self.specs == their.specs and
5376             self.tag == their.tag and
5377             self._expl == their._expl and
5378             self._value == their._value
5379         )
5380
5381     def __call__(
5382             self,
5383             value=None,
5384             impl=None,
5385             expl=None,
5386             default=None,
5387             optional=None,
5388     ):
5389         return self.__class__(
5390             value=value,
5391             schema=self.specs,
5392             impl=self.tag if impl is None else impl,
5393             expl=self._expl if expl is None else expl,
5394             default=self.default if default is None else default,
5395             optional=self.optional if optional is None else optional,
5396         )
5397
5398     def __contains__(self, key):
5399         return key in self._value
5400
5401     def __setitem__(self, key, value):
5402         spec = self.specs.get(key)
5403         if spec is None:
5404             raise ObjUnknown(key)
5405         if value is None:
5406             self._value.pop(key, None)
5407             return
5408         if not isinstance(value, spec.__class__):
5409             raise InvalidValueType((spec.__class__,))
5410         value = spec(value=value)
5411         if spec.default is not None and value == spec.default:
5412             self._value.pop(key, None)
5413             return
5414         self._value[key] = value
5415
5416     def __getitem__(self, key):
5417         value = self._value.get(key)
5418         if value is not None:
5419             return value
5420         spec = self.specs.get(key)
5421         if spec is None:
5422             raise ObjUnknown(key)
5423         if spec.default is not None:
5424             return spec.default
5425         return None
5426
5427     def _encoded_values(self):
5428         raws = []
5429         for name, spec in iteritems(self.specs):
5430             value = self._value.get(name)
5431             if value is None:
5432                 if spec.optional:
5433                     continue
5434                 raise ObjNotReady(name)
5435             raws.append(value.encode())
5436         return raws
5437
5438     def _encode(self):
5439         v = b"".join(self._encoded_values())
5440         return b"".join((self.tag, len_encode(len(v)), v))
5441
5442     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5443         try:
5444             t, tlen, lv = tag_strip(tlv)
5445         except DecodeError as err:
5446             raise err.__class__(
5447                 msg=err.msg,
5448                 klass=self.__class__,
5449                 decode_path=decode_path,
5450                 offset=offset,
5451             )
5452         if t != self.tag:
5453             raise TagMismatch(
5454                 klass=self.__class__,
5455                 decode_path=decode_path,
5456                 offset=offset,
5457             )
5458         if tag_only:  # pragma: no cover
5459             return None
5460         lenindef = False
5461         ctx_bered = ctx.get("bered", False)
5462         try:
5463             l, llen, v = len_decode(lv)
5464         except LenIndefForm as err:
5465             if not ctx_bered:
5466                 raise err.__class__(
5467                     msg=err.msg,
5468                     klass=self.__class__,
5469                     decode_path=decode_path,
5470                     offset=offset,
5471                 )
5472             l, llen, v = 0, 1, lv[1:]
5473             lenindef = True
5474         except DecodeError as err:
5475             raise err.__class__(
5476                 msg=err.msg,
5477                 klass=self.__class__,
5478                 decode_path=decode_path,
5479                 offset=offset,
5480             )
5481         if l > len(v):
5482             raise NotEnoughData(
5483                 "encoded length is longer than data",
5484                 klass=self.__class__,
5485                 decode_path=decode_path,
5486                 offset=offset,
5487             )
5488         if not lenindef:
5489             v, tail = v[:l], v[l:]
5490         vlen = 0
5491         sub_offset = offset + tlen + llen
5492         values = {}
5493         ber_encoded = False
5494         ctx_allow_default_values = ctx.get("allow_default_values", False)
5495         for name, spec in iteritems(self.specs):
5496             if spec.optional and (
5497                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
5498                     len(v) == 0
5499             ):
5500                 continue
5501             sub_decode_path = decode_path + (name,)
5502             try:
5503                 value, v_tail = spec.decode(
5504                     v,
5505                     sub_offset,
5506                     leavemm=True,
5507                     decode_path=sub_decode_path,
5508                     ctx=ctx,
5509                     _ctx_immutable=False,
5510                 )
5511             except TagMismatch as err:
5512                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
5513                     continue
5514                 raise
5515
5516             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
5517             if defined is not None:
5518                 defined_by, defined_spec = defined
5519                 if issubclass(value.__class__, SequenceOf):
5520                     for i, _value in enumerate(value):
5521                         sub_sub_decode_path = sub_decode_path + (
5522                             str(i),
5523                             DecodePathDefBy(defined_by),
5524                         )
5525                         defined_value, defined_tail = defined_spec.decode(
5526                             memoryview(bytes(_value)),
5527                             sub_offset + (
5528                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5529                                 if value.expled else (value.tlen + value.llen)
5530                             ),
5531                             leavemm=True,
5532                             decode_path=sub_sub_decode_path,
5533                             ctx=ctx,
5534                             _ctx_immutable=False,
5535                         )
5536                         if len(defined_tail) > 0:
5537                             raise DecodeError(
5538                                 "remaining data",
5539                                 klass=self.__class__,
5540                                 decode_path=sub_sub_decode_path,
5541                                 offset=offset,
5542                             )
5543                         _value.defined = (defined_by, defined_value)
5544                 else:
5545                     defined_value, defined_tail = defined_spec.decode(
5546                         memoryview(bytes(value)),
5547                         sub_offset + (
5548                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
5549                             if value.expled else (value.tlen + value.llen)
5550                         ),
5551                         leavemm=True,
5552                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5553                         ctx=ctx,
5554                         _ctx_immutable=False,
5555                     )
5556                     if len(defined_tail) > 0:
5557                         raise DecodeError(
5558                             "remaining data",
5559                             klass=self.__class__,
5560                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
5561                             offset=offset,
5562                         )
5563                     value.defined = (defined_by, defined_value)
5564
5565             value_len = value.fulllen
5566             vlen += value_len
5567             sub_offset += value_len
5568             v = v_tail
5569             if spec.default is not None and value == spec.default:
5570                 if ctx_bered or ctx_allow_default_values:
5571                     ber_encoded = True
5572                 else:
5573                     raise DecodeError(
5574                         "DEFAULT value met",
5575                         klass=self.__class__,
5576                         decode_path=sub_decode_path,
5577                         offset=sub_offset,
5578                     )
5579             values[name] = value
5580
5581             spec_defines = getattr(spec, "defines", ())
5582             if len(spec_defines) == 0:
5583                 defines_by_path = ctx.get("defines_by_path", ())
5584                 if len(defines_by_path) > 0:
5585                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
5586             if spec_defines is not None and len(spec_defines) > 0:
5587                 for rel_path, schema in spec_defines:
5588                     defined = schema.get(value, None)
5589                     if defined is not None:
5590                         ctx.setdefault("_defines", []).append((
5591                             abs_decode_path(sub_decode_path[:-1], rel_path),
5592                             (value, defined),
5593                         ))
5594         if lenindef:
5595             if v[:EOC_LEN].tobytes() != EOC:
5596                 raise DecodeError(
5597                     "no EOC",
5598                     klass=self.__class__,
5599                     decode_path=decode_path,
5600                     offset=offset,
5601                 )
5602             tail = v[EOC_LEN:]
5603             vlen += EOC_LEN
5604         elif len(v) > 0:
5605             raise DecodeError(
5606                 "remaining data",
5607                 klass=self.__class__,
5608                 decode_path=decode_path,
5609                 offset=offset,
5610             )
5611         obj = self.__class__(
5612             schema=self.specs,
5613             impl=self.tag,
5614             expl=self._expl,
5615             default=self.default,
5616             optional=self.optional,
5617             _decoded=(offset, llen, vlen),
5618         )
5619         obj._value = values
5620         obj.lenindef = lenindef
5621         obj.ber_encoded = ber_encoded
5622         return obj, tail
5623
5624     def __repr__(self):
5625         value = pp_console_row(next(self.pps()))
5626         cols = []
5627         for name in self.specs:
5628             _value = self._value.get(name)
5629             if _value is None:
5630                 continue
5631             cols.append("%s: %s" % (name, repr(_value)))
5632         return "%s[%s]" % (value, "; ".join(cols))
5633
5634     def pps(self, decode_path=()):
5635         yield _pp(
5636             obj=self,
5637             asn1_type_name=self.asn1_type_name,
5638             obj_name=self.__class__.__name__,
5639             decode_path=decode_path,
5640             optional=self.optional,
5641             default=self == self.default,
5642             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5643             expl=None if self._expl is None else tag_decode(self._expl),
5644             offset=self.offset,
5645             tlen=self.tlen,
5646             llen=self.llen,
5647             vlen=self.vlen,
5648             expl_offset=self.expl_offset if self.expled else None,
5649             expl_tlen=self.expl_tlen if self.expled else None,
5650             expl_llen=self.expl_llen if self.expled else None,
5651             expl_vlen=self.expl_vlen if self.expled else None,
5652             expl_lenindef=self.expl_lenindef,
5653             lenindef=self.lenindef,
5654             ber_encoded=self.ber_encoded,
5655             bered=self.bered,
5656         )
5657         for name in self.specs:
5658             value = self._value.get(name)
5659             if value is None:
5660                 continue
5661             yield value.pps(decode_path=decode_path + (name,))
5662         for pp in self.pps_lenindef(decode_path):
5663             yield pp
5664
5665
5666 class Set(Sequence):
5667     """``SET`` structure type
5668
5669     Its usage is identical to :py:class:`pyderasn.Sequence`.
5670
5671     .. _allow_unordered_set_ctx:
5672
5673     DER prohibits unordered values encoding and will raise an error
5674     during decode. If If :ref:`bered <bered_ctx>` context option is set,
5675     then no error will occure. Also you can disable strict values
5676     ordering check by setting ``"allow_unordered_set": True``
5677     :ref:`context <ctx>` option.
5678     """
5679     __slots__ = ()
5680     tag_default = tag_encode(form=TagFormConstructed, num=17)
5681     asn1_type_name = "SET"
5682
5683     def _encode(self):
5684         raws = self._encoded_values()
5685         raws.sort()
5686         v = b"".join(raws)
5687         return b"".join((self.tag, len_encode(len(v)), v))
5688
5689     def _specs_items(self):
5690         return iteritems(self.specs)
5691
5692     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5693         try:
5694             t, tlen, lv = tag_strip(tlv)
5695         except DecodeError as err:
5696             raise err.__class__(
5697                 msg=err.msg,
5698                 klass=self.__class__,
5699                 decode_path=decode_path,
5700                 offset=offset,
5701             )
5702         if t != self.tag:
5703             raise TagMismatch(
5704                 klass=self.__class__,
5705                 decode_path=decode_path,
5706                 offset=offset,
5707             )
5708         if tag_only:
5709             return None
5710         lenindef = False
5711         ctx_bered = ctx.get("bered", False)
5712         try:
5713             l, llen, v = len_decode(lv)
5714         except LenIndefForm as err:
5715             if not ctx_bered:
5716                 raise err.__class__(
5717                     msg=err.msg,
5718                     klass=self.__class__,
5719                     decode_path=decode_path,
5720                     offset=offset,
5721                 )
5722             l, llen, v = 0, 1, lv[1:]
5723             lenindef = True
5724         except DecodeError as err:
5725             raise err.__class__(
5726                 msg=err.msg,
5727                 klass=self.__class__,
5728                 decode_path=decode_path,
5729                 offset=offset,
5730             )
5731         if l > len(v):
5732             raise NotEnoughData(
5733                 "encoded length is longer than data",
5734                 klass=self.__class__,
5735                 offset=offset,
5736             )
5737         if not lenindef:
5738             v, tail = v[:l], v[l:]
5739         vlen = 0
5740         sub_offset = offset + tlen + llen
5741         values = {}
5742         ber_encoded = False
5743         ctx_allow_default_values = ctx.get("allow_default_values", False)
5744         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5745         value_prev = memoryview(v[:0])
5746
5747         while len(v) > 0:
5748             if lenindef and v[:EOC_LEN].tobytes() == EOC:
5749                 break
5750             for name, spec in self._specs_items():
5751                 sub_decode_path = decode_path + (name,)
5752                 try:
5753                     spec.decode(
5754                         v,
5755                         sub_offset,
5756                         leavemm=True,
5757                         decode_path=sub_decode_path,
5758                         ctx=ctx,
5759                         tag_only=True,
5760                         _ctx_immutable=False,
5761                     )
5762                 except TagMismatch:
5763                     continue
5764                 break
5765             else:
5766                 raise TagMismatch(
5767                     klass=self.__class__,
5768                     decode_path=decode_path,
5769                     offset=offset,
5770                 )
5771             value, v_tail = spec.decode(
5772                 v,
5773                 sub_offset,
5774                 leavemm=True,
5775                 decode_path=sub_decode_path,
5776                 ctx=ctx,
5777                 _ctx_immutable=False,
5778             )
5779             value_len = value.fulllen
5780             if value_prev.tobytes() > v[:value_len].tobytes():
5781                 if ctx_bered or ctx_allow_unordered_set:
5782                     ber_encoded = True
5783                 else:
5784                     raise DecodeError(
5785                         "unordered " + self.asn1_type_name,
5786                         klass=self.__class__,
5787                         decode_path=sub_decode_path,
5788                         offset=sub_offset,
5789                     )
5790             if spec.default is None or value != spec.default:
5791                 pass
5792             elif ctx_bered or ctx_allow_default_values:
5793                 ber_encoded = True
5794             else:
5795                 raise DecodeError(
5796                     "DEFAULT value met",
5797                     klass=self.__class__,
5798                     decode_path=sub_decode_path,
5799                     offset=sub_offset,
5800                 )
5801             values[name] = value
5802             value_prev = v[:value_len]
5803             sub_offset += value_len
5804             vlen += value_len
5805             v = v_tail
5806         obj = self.__class__(
5807             schema=self.specs,
5808             impl=self.tag,
5809             expl=self._expl,
5810             default=self.default,
5811             optional=self.optional,
5812             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5813         )
5814         if lenindef:
5815             if v[:EOC_LEN].tobytes() != EOC:
5816                 raise DecodeError(
5817                     "no EOC",
5818                     klass=self.__class__,
5819                     decode_path=decode_path,
5820                     offset=offset,
5821                 )
5822             tail = v[EOC_LEN:]
5823             obj.lenindef = True
5824         obj._value = values
5825         if not obj.ready:
5826             raise DecodeError(
5827                 "not all values are ready",
5828                 klass=self.__class__,
5829                 decode_path=decode_path,
5830                 offset=offset,
5831             )
5832         obj.ber_encoded = ber_encoded
5833         return obj, tail
5834
5835
5836 SequenceOfState = namedtuple("SequenceOfState", (
5837     "version",
5838     "spec",
5839     "value",
5840     "bound_min",
5841     "bound_max",
5842     "tag",
5843     "expl",
5844     "default",
5845     "optional",
5846     "offset",
5847     "llen",
5848     "vlen",
5849     "expl_lenindef",
5850     "lenindef",
5851     "ber_encoded",
5852 ), **NAMEDTUPLE_KWARGS)
5853
5854
5855 class SequenceOf(Obj):
5856     """``SEQUENCE OF`` sequence type
5857
5858     For that kind of type you must specify the object it will carry on
5859     (bounds are for example here, not required)::
5860
5861         class Ints(SequenceOf):
5862             schema = Integer()
5863             bounds = (0, 2)
5864
5865     >>> ints = Ints()
5866     >>> ints.append(Integer(123))
5867     >>> ints.append(Integer(234))
5868     >>> ints
5869     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5870     >>> [int(i) for i in ints]
5871     [123, 234]
5872     >>> ints.append(Integer(345))
5873     Traceback (most recent call last):
5874     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5875     >>> ints[1]
5876     INTEGER 234
5877     >>> ints[1] = Integer(345)
5878     >>> ints
5879     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5880
5881     Also you can initialize sequence with preinitialized values:
5882
5883     >>> ints = Ints([Integer(123), Integer(234)])
5884     """
5885     __slots__ = ("spec", "_bound_min", "_bound_max")
5886     tag_default = tag_encode(form=TagFormConstructed, num=16)
5887     asn1_type_name = "SEQUENCE OF"
5888
5889     def __init__(
5890             self,
5891             value=None,
5892             schema=None,
5893             bounds=None,
5894             impl=None,
5895             expl=None,
5896             default=None,
5897             optional=False,
5898             _decoded=(0, 0, 0),
5899     ):
5900         super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
5901         if schema is None:
5902             schema = getattr(self, "schema", None)
5903         if schema is None:
5904             raise ValueError("schema must be specified")
5905         self.spec = schema
5906         self._bound_min, self._bound_max = getattr(
5907             self,
5908             "bounds",
5909             (0, float("+inf")),
5910         ) if bounds is None else bounds
5911         self._value = []
5912         if value is not None:
5913             self._value = self._value_sanitize(value)
5914         if default is not None:
5915             default_value = self._value_sanitize(default)
5916             default_obj = self.__class__(
5917                 schema=schema,
5918                 impl=self.tag,
5919                 expl=self._expl,
5920             )
5921             default_obj._value = default_value
5922             self.default = default_obj
5923             if value is None:
5924                 self._value = copy(default_obj._value)
5925
5926     def _value_sanitize(self, value):
5927         if issubclass(value.__class__, SequenceOf):
5928             value = value._value
5929         elif hasattr(value, "__iter__"):
5930             value = list(value)
5931         else:
5932             raise InvalidValueType((self.__class__, iter))
5933         if not self._bound_min <= len(value) <= self._bound_max:
5934             raise BoundsError(self._bound_min, len(value), self._bound_max)
5935         for v in value:
5936             if not isinstance(v, self.spec.__class__):
5937                 raise InvalidValueType((self.spec.__class__,))
5938         return value
5939
5940     @property
5941     def ready(self):
5942         return all(v.ready for v in self._value)
5943
5944     @property
5945     def bered(self):
5946         if self.expl_lenindef or self.lenindef or self.ber_encoded:
5947             return True
5948         return any(v.bered for v in self._value)
5949
5950     def __getstate__(self):
5951         return SequenceOfState(
5952             __version__,
5953             self.spec,
5954             [copy(v) for v in self._value],
5955             self._bound_min,
5956             self._bound_max,
5957             self.tag,
5958             self._expl,
5959             self.default,
5960             self.optional,
5961             self.offset,
5962             self.llen,
5963             self.vlen,
5964             self.expl_lenindef,
5965             self.lenindef,
5966             self.ber_encoded,
5967         )
5968
5969     def __setstate__(self, state):
5970         super(SequenceOf, self).__setstate__(state)
5971         self.spec = state.spec
5972         self._value = state.value
5973         self._bound_min = state.bound_min
5974         self._bound_max = state.bound_max
5975         self.tag = state.tag
5976         self._expl = state.expl
5977         self.default = state.default
5978         self.optional = state.optional
5979         self.offset = state.offset
5980         self.llen = state.llen
5981         self.vlen = state.vlen
5982         self.expl_lenindef = state.expl_lenindef
5983         self.lenindef = state.lenindef
5984         self.ber_encoded = state.ber_encoded
5985
5986     def __eq__(self, their):
5987         if isinstance(their, self.__class__):
5988             return (
5989                 self.spec == their.spec and
5990                 self.tag == their.tag and
5991                 self._expl == their._expl and
5992                 self._value == their._value
5993             )
5994         if hasattr(their, "__iter__"):
5995             return self._value == list(their)
5996         return False
5997
5998     def __call__(
5999             self,
6000             value=None,
6001             bounds=None,
6002             impl=None,
6003             expl=None,
6004             default=None,
6005             optional=None,
6006     ):
6007         return self.__class__(
6008             value=value,
6009             schema=self.spec,
6010             bounds=(
6011                 (self._bound_min, self._bound_max)
6012                 if bounds is None else bounds
6013             ),
6014             impl=self.tag if impl is None else impl,
6015             expl=self._expl if expl is None else expl,
6016             default=self.default if default is None else default,
6017             optional=self.optional if optional is None else optional,
6018         )
6019
6020     def __contains__(self, key):
6021         return key in self._value
6022
6023     def append(self, value):
6024         if not isinstance(value, self.spec.__class__):
6025             raise InvalidValueType((self.spec.__class__,))
6026         if len(self._value) + 1 > self._bound_max:
6027             raise BoundsError(
6028                 self._bound_min,
6029                 len(self._value) + 1,
6030                 self._bound_max,
6031             )
6032         self._value.append(value)
6033
6034     def __iter__(self):
6035         self._assert_ready()
6036         return iter(self._value)
6037
6038     def __len__(self):
6039         self._assert_ready()
6040         return len(self._value)
6041
6042     def __setitem__(self, key, value):
6043         if not isinstance(value, self.spec.__class__):
6044             raise InvalidValueType((self.spec.__class__,))
6045         self._value[key] = self.spec(value=value)
6046
6047     def __getitem__(self, key):
6048         return self._value[key]
6049
6050     def _encoded_values(self):
6051         return [v.encode() for v in self._value]
6052
6053     def _encode(self):
6054         v = b"".join(self._encoded_values())
6055         return b"".join((self.tag, len_encode(len(v)), v))
6056
6057     def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
6058         try:
6059             t, tlen, lv = tag_strip(tlv)
6060         except DecodeError as err:
6061             raise err.__class__(
6062                 msg=err.msg,
6063                 klass=self.__class__,
6064                 decode_path=decode_path,
6065                 offset=offset,
6066             )
6067         if t != self.tag:
6068             raise TagMismatch(
6069                 klass=self.__class__,
6070                 decode_path=decode_path,
6071                 offset=offset,
6072             )
6073         if tag_only:
6074             return None
6075         lenindef = False
6076         ctx_bered = ctx.get("bered", False)
6077         try:
6078             l, llen, v = len_decode(lv)
6079         except LenIndefForm as err:
6080             if not ctx_bered:
6081                 raise err.__class__(
6082                     msg=err.msg,
6083                     klass=self.__class__,
6084                     decode_path=decode_path,
6085                     offset=offset,
6086                 )
6087             l, llen, v = 0, 1, lv[1:]
6088             lenindef = True
6089         except DecodeError as err:
6090             raise err.__class__(
6091                 msg=err.msg,
6092                 klass=self.__class__,
6093                 decode_path=decode_path,
6094                 offset=offset,
6095             )
6096         if l > len(v):
6097             raise NotEnoughData(
6098                 "encoded length is longer than data",
6099                 klass=self.__class__,
6100                 decode_path=decode_path,
6101                 offset=offset,
6102             )
6103         if not lenindef:
6104             v, tail = v[:l], v[l:]
6105         vlen = 0
6106         sub_offset = offset + tlen + llen
6107         _value = []
6108         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6109         value_prev = memoryview(v[:0])
6110         ber_encoded = False
6111         spec = self.spec
6112         while len(v) > 0:
6113             if lenindef and v[:EOC_LEN].tobytes() == EOC:
6114                 break
6115             sub_decode_path = decode_path + (str(len(_value)),)
6116             value, v_tail = spec.decode(
6117                 v,
6118                 sub_offset,
6119                 leavemm=True,
6120                 decode_path=sub_decode_path,
6121                 ctx=ctx,
6122                 _ctx_immutable=False,
6123             )
6124             value_len = value.fulllen
6125             if ordering_check:
6126                 if value_prev.tobytes() > v[:value_len].tobytes():
6127                     if ctx_bered or ctx_allow_unordered_set:
6128                         ber_encoded = True
6129                     else:
6130                         raise DecodeError(
6131                             "unordered " + self.asn1_type_name,
6132                             klass=self.__class__,
6133                             decode_path=sub_decode_path,
6134                             offset=sub_offset,
6135                         )
6136                 value_prev = v[:value_len]
6137             _value.append(value)
6138             sub_offset += value_len
6139             vlen += value_len
6140             v = v_tail
6141         try:
6142             obj = self.__class__(
6143                 value=_value,
6144                 schema=spec,
6145                 bounds=(self._bound_min, self._bound_max),
6146                 impl=self.tag,
6147                 expl=self._expl,
6148                 default=self.default,
6149                 optional=self.optional,
6150                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6151             )
6152         except BoundsError as err:
6153             raise DecodeError(
6154                 msg=str(err),
6155                 klass=self.__class__,
6156                 decode_path=decode_path,
6157                 offset=offset,
6158             )
6159         if lenindef:
6160             if v[:EOC_LEN].tobytes() != EOC:
6161                 raise DecodeError(
6162                     "no EOC",
6163                     klass=self.__class__,
6164                     decode_path=decode_path,
6165                     offset=offset,
6166                 )
6167             obj.lenindef = True
6168             tail = v[EOC_LEN:]
6169         obj.ber_encoded = ber_encoded
6170         return obj, tail
6171
6172     def __repr__(self):
6173         return "%s[%s]" % (
6174             pp_console_row(next(self.pps())),
6175             ", ".join(repr(v) for v in self._value),
6176         )
6177
6178     def pps(self, decode_path=()):
6179         yield _pp(
6180             obj=self,
6181             asn1_type_name=self.asn1_type_name,
6182             obj_name=self.__class__.__name__,
6183             decode_path=decode_path,
6184             optional=self.optional,
6185             default=self == self.default,
6186             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6187             expl=None if self._expl is None else tag_decode(self._expl),
6188             offset=self.offset,
6189             tlen=self.tlen,
6190             llen=self.llen,
6191             vlen=self.vlen,
6192             expl_offset=self.expl_offset if self.expled else None,
6193             expl_tlen=self.expl_tlen if self.expled else None,
6194             expl_llen=self.expl_llen if self.expled else None,
6195             expl_vlen=self.expl_vlen if self.expled else None,
6196             expl_lenindef=self.expl_lenindef,
6197             lenindef=self.lenindef,
6198             ber_encoded=self.ber_encoded,
6199             bered=self.bered,
6200         )
6201         for i, value in enumerate(self._value):
6202             yield value.pps(decode_path=decode_path + (str(i),))
6203         for pp in self.pps_lenindef(decode_path):
6204             yield pp
6205
6206
6207 class SetOf(SequenceOf):
6208     """``SET OF`` sequence type
6209
6210     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
6211     """
6212     __slots__ = ()
6213     tag_default = tag_encode(form=TagFormConstructed, num=17)
6214     asn1_type_name = "SET OF"
6215
6216     def _encode(self):
6217         raws = self._encoded_values()
6218         raws.sort()
6219         v = b"".join(raws)
6220         return b"".join((self.tag, len_encode(len(v)), v))
6221
6222     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
6223         return super(SetOf, self)._decode(
6224             tlv,
6225             offset,
6226             decode_path,
6227             ctx,
6228             tag_only,
6229             ordering_check=True,
6230         )
6231
6232
6233 def obj_by_path(pypath):  # pragma: no cover
6234     """Import object specified as string Python path
6235
6236     Modules must be separated from classes/functions with ``:``.
6237
6238     >>> obj_by_path("foo.bar:Baz")
6239     <class 'foo.bar.Baz'>
6240     >>> obj_by_path("foo.bar:Baz.boo")
6241     <classmethod 'foo.bar.Baz.boo'>
6242     """
6243     mod, objs = pypath.rsplit(":", 1)
6244     from importlib import import_module
6245     obj = import_module(mod)
6246     for obj_name in objs.split("."):
6247         obj = getattr(obj, obj_name)
6248     return obj
6249
6250
6251 def generic_decoder():  # pragma: no cover
6252     # All of this below is a big hack with self references
6253     choice = PrimitiveTypes()
6254     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
6255     choice.specs["SetOf"] = SetOf(schema=choice)
6256     for i in six_xrange(31):
6257         choice.specs["SequenceOf%d" % i] = SequenceOf(
6258             schema=choice,
6259             expl=tag_ctxc(i),
6260         )
6261     choice.specs["Any"] = Any()
6262
6263     # Class name equals to type name, to omit it from output
6264     class SEQUENCEOF(SequenceOf):
6265         __slots__ = ()
6266         schema = choice
6267
6268     def pprint_any(
6269             obj,
6270             oid_maps=(),
6271             with_colours=False,
6272             with_decode_path=False,
6273             decode_path_only=(),
6274     ):
6275         def _pprint_pps(pps):
6276             for pp in pps:
6277                 if hasattr(pp, "_fields"):
6278                     if (
6279                             decode_path_only != () and
6280                             pp.decode_path[:len(decode_path_only)] != decode_path_only
6281                     ):
6282                         continue
6283                     if pp.asn1_type_name == Choice.asn1_type_name:
6284                         continue
6285                     pp_kwargs = pp._asdict()
6286                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
6287                     pp = _pp(**pp_kwargs)
6288                     yield pp_console_row(
6289                         pp,
6290                         oid_maps=oid_maps,
6291                         with_offsets=True,
6292                         with_blob=False,
6293                         with_colours=with_colours,
6294                         with_decode_path=with_decode_path,
6295                         decode_path_len_decrease=len(decode_path_only),
6296                     )
6297                     for row in pp_console_blob(
6298                             pp,
6299                             decode_path_len_decrease=len(decode_path_only),
6300                     ):
6301                         yield row
6302                 else:
6303                     for row in _pprint_pps(pp):
6304                         yield row
6305         return "\n".join(_pprint_pps(obj.pps()))
6306     return SEQUENCEOF(), pprint_any
6307
6308
6309 def main():  # pragma: no cover
6310     import argparse
6311     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
6312     parser.add_argument(
6313         "--skip",
6314         type=int,
6315         default=0,
6316         help="Skip that number of bytes from the beginning",
6317     )
6318     parser.add_argument(
6319         "--oids",
6320         help="Python paths to dictionary with OIDs, comma separated",
6321     )
6322     parser.add_argument(
6323         "--schema",
6324         help="Python path to schema definition to use",
6325     )
6326     parser.add_argument(
6327         "--defines-by-path",
6328         help="Python path to decoder's defines_by_path",
6329     )
6330     parser.add_argument(
6331         "--nobered",
6332         action="store_true",
6333         help="Disallow BER encoding",
6334     )
6335     parser.add_argument(
6336         "--print-decode-path",
6337         action="store_true",
6338         help="Print decode paths",
6339     )
6340     parser.add_argument(
6341         "--decode-path-only",
6342         help="Print only specified decode path",
6343     )
6344     parser.add_argument(
6345         "--allow-expl-oob",
6346         action="store_true",
6347         help="Allow explicit tag out-of-bound",
6348     )
6349     parser.add_argument(
6350         "DERFile",
6351         type=argparse.FileType("rb"),
6352         help="Path to DER file you want to decode",
6353     )
6354     args = parser.parse_args()
6355     args.DERFile.seek(args.skip)
6356     der = memoryview(args.DERFile.read())
6357     args.DERFile.close()
6358     oid_maps = (
6359         [obj_by_path(_path) for _path in (args.oids or "").split(",")]
6360         if args.oids else ()
6361     )
6362     if args.schema:
6363         schema = obj_by_path(args.schema)
6364         from functools import partial
6365         pprinter = partial(pprint, big_blobs=True)
6366     else:
6367         schema, pprinter = generic_decoder()
6368     ctx = {
6369         "bered": not args.nobered,
6370         "allow_expl_oob": args.allow_expl_oob,
6371     }
6372     if args.defines_by_path is not None:
6373         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
6374     obj, tail = schema().decode(der, ctx=ctx)
6375     print(pprinter(
6376         obj,
6377         oid_maps=oid_maps,
6378         with_colours=environ.get("NO_COLOR") is None,
6379         with_decode_path=args.print_decode_path,
6380         decode_path_only=(
6381             () if args.decode_path_only is None else
6382             tuple(args.decode_path_only.split(":"))
6383         ),
6384     ))
6385     if tail != b"":
6386         print("\nTrailing data: %s" % hexenc(tail))
6387
6388
6389 if __name__ == "__main__":
6390     main()