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