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