]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
keep_memoryview context option
[pyderasn.git] / pyderasn.py
1 #!/usr/bin/env python
2 # coding: utf-8
3 # cython: language_level=3
4 # pylint: disable=line-too-long,superfluous-parens,protected-access,too-many-lines
5 # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements
6 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures
7 # Copyright (C) 2017-2022 Sergey Matveev <stargrave@stargrave.org>
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU Lesser General Public License as
11 # published by the Free Software Foundation, version 3 of the License.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 """Python ASN.1 DER/CER/BER codec with abstract structures
21
22 This library allows you to marshal various structures in ASN.1 DER/CER
23 format, unmarshal BER/CER/DER ones.
24
25     >>> i = Integer(123)
26     >>> raw = i.encode()
27     >>> Integer().decod(raw) == i
28     True
29
30 There are primitive types, holding single values
31 (:py:class:`pyderasn.BitString`,
32 :py:class:`pyderasn.Boolean`,
33 :py:class:`pyderasn.Enumerated`,
34 :py:class:`pyderasn.GeneralizedTime`,
35 :py:class:`pyderasn.Integer`,
36 :py:class:`pyderasn.Null`,
37 :py:class:`pyderasn.ObjectIdentifier`,
38 :py:class:`pyderasn.OctetString`,
39 :py:class:`pyderasn.UTCTime`,
40 :py:class:`various strings <pyderasn.CommonString>`
41 (:py:class:`pyderasn.BMPString`,
42 :py:class:`pyderasn.GeneralString`,
43 :py:class:`pyderasn.GraphicString`,
44 :py:class:`pyderasn.IA5String`,
45 :py:class:`pyderasn.ISO646String`,
46 :py:class:`pyderasn.NumericString`,
47 :py:class:`pyderasn.PrintableString`,
48 :py:class:`pyderasn.T61String`,
49 :py:class:`pyderasn.TeletexString`,
50 :py:class:`pyderasn.UniversalString`,
51 :py:class:`pyderasn.UTF8String`,
52 :py:class:`pyderasn.VideotexString`,
53 :py:class:`pyderasn.VisibleString`)),
54 constructed types, holding multiple primitive types
55 (:py:class:`pyderasn.Sequence`,
56 :py:class:`pyderasn.SequenceOf`,
57 :py:class:`pyderasn.Set`,
58 :py:class:`pyderasn.SetOf`),
59 and special types like
60 :py:class:`pyderasn.Any` and
61 :py:class:`pyderasn.Choice`.
62
63 Common for most types
64 ---------------------
65
66 Tags
67 ____
68
69 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
70 the default tag used during coding process. You can override it with
71 either ``IMPLICIT`` (using either ``impl`` keyword argument or ``impl``
72 class attribute), or ``EXPLICIT`` one (using either ``expl`` keyword
73 argument or ``expl`` class attribute). Both arguments take raw binary
74 string, containing that tag. You can **not** set implicit and explicit
75 tags simultaneously.
76
77 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
78 functions, allowing you to easily create ``CONTEXT``
79 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
80 number.
81
82 .. note::
83
84    EXPLICIT tags always have **constructed** tag. PyDERASN does not
85    explicitly check correctness of schema input here.
86
87 .. note::
88
89    Implicit tags have **primitive** (``tag_ctxp``) encoding for
90    primitive values.
91
92 ::
93
94     >>> Integer(impl=tag_ctxp(1))
95     [1] INTEGER
96     >>> Integer(expl=tag_ctxc(2))
97     [2] EXPLICIT INTEGER
98
99 Implicit tag is not explicitly shown.
100
101 Two objects of the same type, but with different implicit/explicit tags
102 are **not** equal.
103
104 You can get object's effective tag (either default or implicited) through
105 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
106 function::
107
108     >>> tag_decode(tag_ctxc(123))
109     (128, 32, 123)
110     >>> klass, form, num = tag_decode(tag_ctxc(123))
111     >>> klass == TagClassContext
112     True
113     >>> form == TagFormConstructed
114     True
115
116 To determine if object has explicit tag, use ``expled`` boolean property
117 and ``expl_tag`` property, returning explicit tag's value.
118
119 Default/optional
120 ________________
121
122 Many objects in sequences could be ``OPTIONAL`` and could have
123 ``DEFAULT`` value. You can specify that object's property using
124 corresponding keyword arguments.
125
126     >>> Integer(optional=True, default=123)
127     INTEGER 123 OPTIONAL DEFAULT
128
129 Those specifications do not play any role in primitive value encoding,
130 but are taken into account when dealing with sequences holding them. For
131 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
132 ``version`` field::
133
134     class Version(Integer):
135         schema = (
136             ("v1", 0),
137             ("v2", 1),
138             ("v3", 2),
139         )
140     class TBSCertificate(Sequence):
141         schema = (
142             ("version", Version(expl=tag_ctxc(0), default="v1")),
143         [...]
144
145 When default argument is used and value is not specified, then it equals
146 to default one.
147
148 .. _bounds:
149
150 Size constraints
151 ________________
152
153 Some objects give ability to set value size constraints. This is either
154 possible integer value, or allowed length of various strings and
155 sequences. Constraints are set in the following way::
156
157     class X(...):
158         bounds = (MIN, MAX)
159
160 And values satisfaction is checked as: ``MIN <= X <= MAX``.
161
162 For simplicity you can also set bounds the following way::
163
164     bounded_x = X(bounds=(MIN, MAX))
165
166 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
167 raised.
168
169 Common methods
170 ______________
171
172 All objects have ``ready`` boolean property, that tells if object is
173 ready to be encoded. If that kind of action is performed on unready
174 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
175
176 All objects are friendly to ``copy.copy()`` and copied objects can be
177 safely mutated.
178
179 Also all objects can be safely ``pickle``-d, but pay attention that
180 pickling among different PyDERASN versions is prohibited.
181
182 .. _decoding:
183
184 Decoding
185 --------
186
187 Decoding is performed using :py:meth:`pyderasn.Obj.decode` method.
188 ``offset`` optional argument could be used to set initial object's
189 offset in the binary data, for convenience. It returns decoded object
190 and remaining unmarshalled data (tail). Internally all work is done on
191 ``memoryview(data)``, and you can leave returning tail as a memoryview,
192 by specifying ``leavemm=True`` argument.
193
194 Also note convenient :py:meth:`pyderasn.Obj.decod` method, that
195 immediately checks and raises if there is non-empty tail.
196
197 When object is decoded, ``decoded`` property is true and you can safely
198 use following properties:
199
200 * ``offset`` -- position including initial offset where object's tag starts
201 * ``tlen`` -- length of object's tag
202 * ``llen`` -- length of object's length value
203 * ``vlen`` -- length of object's value
204 * ``tlvlen`` -- length of the whole object
205
206 Pay attention that those values do **not** include anything related to
207 explicit tag. If you want to know information about it, then use:
208
209 * ``expled`` -- to know if explicit tag is set
210 * ``expl_offset`` (it is lesser than ``offset``)
211 * ``expl_tlen``,
212 * ``expl_llen``
213 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
214 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
215   ``offset`` otherwise
216 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
217   ``tlvlen`` otherwise
218
219 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
220
221 .. _ctx:
222
223 Context
224 _______
225
226 You can specify so called context keyword argument during
227 :py:meth:`pyderasn.Obj.decode` invocation. It is dictionary containing
228 various options governing decoding process.
229
230 Currently available context options:
231
232 * :ref:`allow_default_values <allow_default_values_ctx>`
233 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
234 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
235 * :ref:`bered <bered_ctx>`
236 * :ref:`defines_by_path <defines_by_path_ctx>`
237 * :ref:`evgen_mode_upto <evgen_mode_upto_ctx>`
238 * :ref:`keep_memoryview <keep_memoryview_ctx>`
239
240 .. _pprinting:
241
242 Pretty printing
243 ---------------
244
245 All objects have ``pps()`` method, that is a generator of
246 :py:class:`pyderasn.PP` namedtuple, holding various raw information
247 about the object. If ``pps`` is called on sequences, then all underlying
248 ``PP`` will be yielded.
249
250 You can use :py:func:`pyderasn.pp_console_row` function, converting
251 those ``PP`` to human readable string. Actually exactly it is used for
252 all object ``repr``. But it is easy to write custom formatters.
253
254     >>> from pyderasn import pprint
255     >>> encoded = Integer(-12345).encode()
256     >>> obj, tail = Integer().decode(encoded)
257     >>> print(pprint(obj))
258         0   [1,1,   2] INTEGER -12345
259
260 .. _pprint_example:
261
262 Example certificate::
263
264     >>> print(pprint(crt))
265         0   [1,3,1604] Certificate SEQUENCE
266         4   [1,3,1453]  . tbsCertificate: TBSCertificate SEQUENCE
267        10-2 [1,1,   1]  . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
268        13   [1,1,   3]  . . serialNumber: CertificateSerialNumber INTEGER 61595
269        18   [1,1,  13]  . . signature: AlgorithmIdentifier SEQUENCE
270        20   [1,1,   9]  . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
271        31   [0,0,   2]  . . . parameters: [UNIV 5] ANY OPTIONAL
272                         . . . . 05:00
273        33   [0,0, 278]  . . issuer: Name CHOICE rdnSequence
274        33   [1,3, 274]  . . . rdnSequence: RDNSequence SEQUENCE OF
275        37   [1,1,  11]  . . . . 0: RelativeDistinguishedName SET OF
276        39   [1,1,   9]  . . . . . 0: AttributeTypeAndValue SEQUENCE
277        41   [1,1,   3]  . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
278        46   [0,0,   4]  . . . . . . value: [UNIV 19] AttributeValue ANY
279                         . . . . . . . 13:02:45:53
280     [...]
281      1461   [1,1,  13]  . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
282      1463   [1,1,   9]  . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
283      1474   [0,0,   2]  . . parameters: [UNIV 5] ANY OPTIONAL
284                         . . . 05:00
285      1476   [1,2, 129]  . signatureValue: BIT STRING 1024 bits
286                         . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
287                         . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
288      [...]
289
290     Trailing data: 0a
291
292 Let's parse that output, human::
293
294        10-2 [1,1,   1]    . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
295        ^  ^  ^ ^    ^     ^   ^        ^            ^       ^       ^  ^
296        0  1  2 3    4     5   6        7            8       9       10 11
297
298 ::
299
300        20   [1,1,   9]    . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
301        ^     ^ ^    ^     ^     ^          ^                 ^
302        0     2 3    4     5     6          9                 10
303
304 ::
305
306        33   [0,0, 278]    . . issuer: Name CHOICE rdnSequence
307        ^     ^ ^    ^     ^   ^       ^    ^      ^
308        0     2 3    4     5   6       8    9      10
309
310 ::
311
312        52-2∞ B [1,1,1054]∞  . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
313              ^           ^                                 ^   ^            ^
314             12          13                                14   9            10
315
316 :0:
317  Offset of the object, where its DER/BER encoding begins.
318  Pay attention that it does **not** include explicit tag.
319 :1:
320  If explicit tag exists, then this is its length (tag + encoded length).
321 :2:
322  Length of object's tag. For example CHOICE does not have its own tag,
323  so it is zero.
324 :3:
325  Length of encoded length.
326 :4:
327  Length of encoded value.
328 :5:
329  Visual indentation to show the depth of object in the hierarchy.
330 :6:
331  Object's name inside SEQUENCE/CHOICE.
332 :7:
333  If either IMPLICIT or EXPLICIT tag is set, then it will be shown
334  here. "IMPLICIT" is omitted.
335 :8:
336  Object's class name, if set. Omitted if it is just an ordinary simple
337  value (like with ``algorithm`` in example above).
338 :9:
339  Object's ASN.1 type.
340 :10:
341  Object's value, if set. Can consist of multiple words (like OCTET/BIT
342  STRINGs above). We see ``v3`` value in Version, because it is named.
343  ``rdnSequence`` is the choice of CHOICE type.
344 :11:
345  Possible other flags like OPTIONAL and DEFAULT, if value equals to the
346  default one, specified in the schema.
347 :12:
348  Shows does object contains any kind of BER encoded data (possibly
349  Sequence holding BER-encoded underlying value).
350 :13:
351  Only applicable to BER encoded data. Indefinite length encoding mark.
352 :14:
353  Only applicable to BER encoded data. If object has BER-specific
354  encoding, then ``BER`` will be shown. It does not depend on indefinite
355  length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
356  (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime``
357  could be BERed.
358
359 Also it could be helpful to add quick ASN.1 pprinting command in your
360 pdb's configuration file::
361
362     alias pp1 import pyderasn ;; print(pyderasn.pprint(%1, oid_maps=(locals().get("OID_STR_TO_NAME", {}),)))
363
364 .. _definedby:
365
366 DEFINED BY
367 ----------
368
369 ASN.1 structures often have ANY and OCTET STRING fields, that are
370 DEFINED BY some previously met ObjectIdentifier. This library provides
371 ability to specify mapping between some OID and field that must be
372 decoded with specific specification.
373
374 .. _defines:
375
376 defines kwarg
377 _____________
378
379 :py:class:`pyderasn.ObjectIdentifier` field inside
380 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
381 necessary for decoding structures. For example, CMS (:rfc:`5652`)
382 container::
383
384     class ContentInfo(Sequence):
385         schema = (
386             ("contentType", ContentType(defines=((("content",), {
387                 id_digestedData: DigestedData(),
388                 id_signedData: SignedData(),
389             }),))),
390             ("content", Any(expl=tag_ctxc(0))),
391         )
392
393 ``contentType`` field tells that it defines that ``content`` must be
394 decoded with ``SignedData`` specification, if ``contentType`` equals to
395 ``id-signedData``. The same applies to ``DigestedData``. If
396 ``contentType`` contains unknown OID, then no automatic decoding is
397 done.
398
399 You can specify multiple fields, that will be autodecoded -- that is why
400 ``defines`` kwarg is a sequence. You can specify defined field
401 relatively or absolutely to current decode path. For example ``defines``
402 for AlgorithmIdentifier of X.509's
403 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
404
405         (
406             (("parameters",), {
407                 id_ecPublicKey: ECParameters(),
408                 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
409             }),
410             (("..", "subjectPublicKey"), {
411                 id_rsaEncryption: RSAPublicKey(),
412                 id_GostR3410_2001: OctetString(),
413             }),
414         ),
415
416 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
417 autodecode its parameters inside SPKI's algorithm and its public key
418 itself.
419
420 Following types can be automatically decoded (DEFINED BY):
421
422 * :py:class:`pyderasn.Any`
423 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
424 * :py:class:`pyderasn.OctetString`
425 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
426   ``Any``/``BitString``/``OctetString``-s
427
428 When any of those fields is automatically decoded, then ``.defined``
429 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
430 was defined, ``value`` contains corresponding decoded value. For example
431 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
432
433 .. _defines_by_path_ctx:
434
435 defines_by_path context option
436 ______________________________
437
438 Sometimes you either can not or do not want to explicitly set *defines*
439 in the schema. You can dynamically apply those definitions when calling
440 :py:meth:`pyderasn.Obj.decode` method.
441
442 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
443 value must be sequence of following tuples::
444
445     (decode_path, defines)
446
447 where ``decode_path`` is a tuple holding so-called decode path to the
448 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
449 ``defines``, holding exactly the same value as accepted in its
450 :ref:`keyword argument <defines>`.
451
452 For example, again for CMS, you want to automatically decode
453 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
454 structures it may hold. Also, automatically decode ``controlSequence``
455 of ``PKIResponse``::
456
457     content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
458         (
459             ("contentType",),
460             ((("content",), {id_signedData: SignedData()}),),
461         ),
462         (
463             (
464                 "content",
465                 DecodePathDefBy(id_signedData),
466                 "encapContentInfo",
467                 "eContentType",
468             ),
469             ((("eContent",), {
470                 id_cct_PKIData: PKIData(),
471                 id_cct_PKIResponse: PKIResponse(),
472             })),
473         ),
474         (
475             (
476                 "content",
477                 DecodePathDefBy(id_signedData),
478                 "encapContentInfo",
479                 "eContent",
480                 DecodePathDefBy(id_cct_PKIResponse),
481                 "controlSequence",
482                 any,
483                 "attrType",
484             ),
485             ((("attrValues",), {
486                 id_cmc_recipientNonce: RecipientNonce(),
487                 id_cmc_senderNonce: SenderNonce(),
488                 id_cmc_statusInfoV2: CMCStatusInfoV2(),
489                 id_cmc_transactionId: TransactionId(),
490             })),
491         ),
492     )})
493
494 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
495 First function is useful for path construction when some automatic
496 decoding is already done. ``any`` means literally any value it meet --
497 useful for SEQUENCE/SET OF-s.
498
499 .. _bered_ctx:
500
501 BER encoding
502 ------------
503
504 By default PyDERASN accepts only DER encoded data. By default it encodes
505 to DER. But you can optionally enable BER decoding with setting
506 ``bered`` :ref:`context <ctx>` argument to True. Indefinite lengths and
507 constructed primitive types should be parsed successfully.
508
509 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
510   attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
511   STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``,
512   ``UTCTime``, ``GeneralizedTime`` can contain it.
513 * If object has an indefinite length encoding, then its ``lenindef``
514   attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
515   ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
516   contain it.
517 * If object has an indefinite length encoded explicit tag, then
518   ``expl_lenindef`` is set to True.
519 * If object has either any of BER-related encoding (explicit tag
520   indefinite length, object's indefinite length, BER-encoding) or any
521   underlying component has that kind of encoding, then ``bered``
522   attribute is set to True. For example SignedData CMS can have
523   ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
524   ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
525
526 EOC (end-of-contents) token's length is taken in advance in object's
527 value length.
528
529 .. _allow_expl_oob_ctx:
530
531 Allow explicit tag out-of-bound
532 -------------------------------
533
534 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
535 one value, more than one object. If you set ``allow_expl_oob`` context
536 option to True, then no error will be raised and that invalid encoding
537 will be silently further processed. But pay attention that offsets and
538 lengths will be invalid in that case.
539
540 .. warning::
541
542    This option should be used only for skipping some decode errors, just
543    to see the decoded structure somehow.
544
545 .. _streaming:
546
547 Streaming and dealing with huge structures
548 ------------------------------------------
549
550 .. _evgen_mode:
551
552 evgen mode
553 __________
554
555 ASN.1 structures can be huge, they can hold millions of objects inside
556 (for example Certificate Revocation Lists (CRL), holding revocation
557 state for every previously issued X.509 certificate). CACert.org's 8 MiB
558 CRL file takes more than half a gigabyte of memory to hold the decoded
559 structure.
560
561 If you just simply want to check the signature over the ``tbsCertList``,
562 you can create specialized schema with that field represented as
563 OctetString for example::
564
565     class TBSCertListFast(Sequence):
566         schema = (
567             [...]
568             ("revokedCertificates", OctetString(
569                 impl=SequenceOf.tag_default,
570                 optional=True,
571             )),
572             [...]
573         )
574
575 This allows you to quickly decode a few fields and check the signature
576 over the ``tbsCertList`` bytes.
577
578 But how can you get all certificate's serial number from it, after you
579 trust that CRL after signature validation? You can use so called
580 ``evgen`` (event generation) mode, to catch the events/facts of some
581 successful object decoding. Let's use command line capabilities::
582
583     $ python -m pyderasn --schema tests.test_crl:CertificateList --evgen revoke.crl
584          10     [1,1,   1]   . . version: Version INTEGER v2 (01) OPTIONAL
585          15     [1,1,   9]   . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
586          26     [0,0,   2]   . . . parameters: [UNIV 5] ANY OPTIONAL
587          13     [1,1,  13]   . . signature: AlgorithmIdentifier SEQUENCE
588          34     [1,1,   3]   . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.10
589          39     [0,0,   9]   . . . . . . value: [UNIV 19] AttributeValue ANY
590          32     [1,1,  14]   . . . . . 0: AttributeTypeAndValue SEQUENCE
591          30     [1,1,  16]   . . . . 0: RelativeDistinguishedName SET OF
592     [...]
593         188     [1,1,   1]   . . . . userCertificate: CertificateSerialNumber INTEGER 17 (11)
594         191     [1,1,  13]   . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
595         191     [0,0,  15]   . . . . revocationDate: Time CHOICE utcTime
596         191     [1,1,  13]   . . . . . utcTime: UTCTime UTCTime 2003-04-01T14:25:08
597         186     [1,1,  18]   . . . 0: RevokedCertificate SEQUENCE
598         208     [1,1,   1]   . . . . userCertificate: CertificateSerialNumber INTEGER 20 (14)
599         211     [1,1,  13]   . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
600         211     [0,0,  15]   . . . . revocationDate: Time CHOICE utcTime
601         211     [1,1,  13]   . . . . . utcTime: UTCTime UTCTime 2002-10-01T02:18:01
602         206     [1,1,  18]   . . . 1: RevokedCertificate SEQUENCE
603     [...]
604     9144992     [0,0,  15]   . . . . revocationDate: Time CHOICE utcTime
605     9144992     [1,1,  13]   . . . . . utcTime: UTCTime UTCTime 2020-02-08T07:25:06
606     9144985     [1,1,  20]   . . . 415755: RevokedCertificate SEQUENCE
607       181     [1,4,9144821]   . . revokedCertificates: RevokedCertificates SEQUENCE OF OPTIONAL
608         5     [1,4,9144997]   . tbsCertList: TBSCertList SEQUENCE
609     9145009     [1,1,   9]   . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.13
610     9145020     [0,0,   2]   . . parameters: [UNIV 5] ANY OPTIONAL
611     9145007     [1,1,  13]   . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
612     9145022     [1,3, 513]   . signatureValue: BIT STRING 4096 bits
613         0     [1,4,9145534]  CertificateList SEQUENCE
614
615 Here we see how decoder works: it decodes SEQUENCE's tag, length, then
616 decodes underlying values. It can not tell if SEQUENCE is decoded, so
617 the event of the upper level SEQUENCE is the last one we see.
618 ``version`` field is just a single INTEGER -- it is decoded and event is
619 fired immediately. Then we see that ``algorithm`` and ``parameters``
620 fields are decoded and only after them the ``signature`` SEQUENCE is
621 fired as a successfully decoded. There are 4 events for each revoked
622 certificate entry in that CRL: ``userCertificate`` serial number,
623 ``utcTime`` of ``revocationDate`` CHOICE, ``RevokedCertificate`` itself
624 as a one of entity in ``revokedCertificates`` SEQUENCE OF.
625
626 We can do that in our ordinary Python code and understand where we are
627 by looking at deterministically generated decode paths (do not forget
628 about useful ``--print-decode-path`` CLI option). We must use
629 :py:meth:`pyderasn.Obj.decode_evgen` method, instead of ordinary
630 :py:meth:`pyderasn.Obj.decode`. It is generator yielding ``(decode_path,
631 obj, tail)`` tuples::
632
633     for decode_path, obj, _ in CertificateList().decode_evgen(crl_raw):
634         if (
635             len(decode_path) == 4 and
636             decode_path[:2] == ("tbsCertList", "revokedCertificates"),
637             decode_path[3] == "userCertificate"
638         ):
639             print("serial number:", int(obj))
640
641 Virtually it does not take any memory except at least needed for single
642 object storage. You can easily use that mode to determine required
643 object ``.offset`` and ``.*len`` to be able to decode it separately, or
644 maybe verify signature upon it just by taking bytes by ``.offset`` and
645 ``.tlvlen``.
646
647 .. _evgen_mode_upto_ctx:
648
649 evgen_mode_upto
650 _______________
651
652 There is full ability to get any kind of data from the CRL in the
653 example above. However it is not too convenient to get the whole
654 ``RevokedCertificate`` structure, that is pretty lightweight and one may
655 do not want to disassemble it. You can use ``evgen_mode_upto``
656 :ref:`ctx <ctx>` option that semantically equals to
657 :ref:`defines_by_path <defines_by_path_ctx>` -- list of decode paths
658 mapped to any non-None value. If specified decode path is met, then any
659 subsequent objects won't be decoded in evgen mode. That allows us to
660 parse the CRL above with fully assembled ``RevokedCertificate``::
661
662     for decode_path, obj, _ in CertificateList().decode_evgen(
663         crl_raw,
664         ctx={"evgen_mode_upto": (
665             (("tbsCertList", "revokedCertificates", any), True),
666         )},
667     ):
668         if (
669             len(decode_path) == 3 and
670             decode_path[:2] == ("tbsCertList", "revokedCertificates"),
671         ):
672             print("serial number:", int(obj["userCertificate"]))
673
674 .. note::
675
676    SEQUENCE/SET values with DEFAULT specified are automatically decoded
677    without evgen mode.
678
679 .. _mmap:
680
681 mmap-ed file
682 ____________
683
684 POSIX compliant systems have ``mmap`` syscall, giving ability to work
685 the memory mapped file. You can deal with the file like it was an
686 ordinary binary string, allowing you not to load it to the memory first.
687 Also you can use them as an input for OCTET STRING, taking no Python
688 memory for their storage.
689
690 There is convenient :py:func:`pyderasn.file_mmaped` function that
691 creates read-only memoryview on the file contents::
692
693     with open("huge", "rb") as fd:
694         raw = file_mmaped(fd)
695         obj = Something.decode(raw)
696
697 .. warning::
698
699    mmap maps the **whole** file. So it plays no role if you seek-ed it
700    before. Take the slice of the resulting memoryview with required
701    offset instead.
702
703 .. note::
704
705    If you use ZFS as underlying storage, then pay attention that
706    currently most platforms does not deal good with ZFS ARC and ordinary
707    page cache used for mmaps. It can take twice the necessary size in
708    the memory: both in page cache and ZFS ARC.
709
710 .. _keep_memoryview_ctx:
711
712 That read-only memoryview could be safe to be used as a value inside
713 decoded :py:class:`pyderasn.OctetString` and :py:class:`pyderasn.Any`
714 objects. You can enable that by setting `"keep_memoryview": True` in
715 :ref:`decode context <ctx>`. No OCTET STRING and ANY values will be
716 copied to memory. Of course that works only in DER encoding, where the
717 value is continuously encoded.
718
719 CER encoding
720 ____________
721
722 We can parse any kind of data now, but how can we produce files
723 streamingly, without storing their encoded representation in memory?
724 SEQUENCE by default encodes in memory all its values, joins them in huge
725 binary string, just to know the exact size of SEQUENCE's value for
726 encoding it in TLV. DER requires you to know all exact sizes of the
727 objects.
728
729 You can use CER encoding mode, that slightly differs from the DER, but
730 does not require exact sizes knowledge, allowing streaming encoding
731 directly to some writer/buffer. Just use
732 :py:meth:`pyderasn.Obj.encode_cer` method, providing the writer where
733 encoded data will flow::
734
735     with open("result", "wb") as fd:
736         obj.encode_cer(fd.write)
737
738 ::
739
740     buf = io.BytesIO()
741     obj.encode_cer(buf.write)
742
743 If you do not want to create in-memory buffer every time, then you can
744 use :py:func:`pyderasn.encode_cer` function::
745
746     data = encode_cer(obj)
747
748 Remember that CER is **not valid** DER in most cases, so you **have to**
749 use :ref:`bered <bered_ctx>` :ref:`ctx <ctx>` option during its
750 decoding. Also currently there is **no** validation that provided CER is
751 valid one -- you are sure that it has only valid BER encoding.
752
753 .. warning::
754
755    SET OF values can not be streamingly encoded, because they are
756    required to be sorted byte-by-byte. Big SET OF values still will take
757    much memory. Use neither SET nor SET OF values, as modern ASN.1
758    also recommends too.
759
760 Do not forget about using :ref:`mmap-ed <mmap>` memoryviews for your
761 OCTET STRINGs! They will be streamingly copied from underlying file to
762 the buffer using 1 KB chunks.
763
764 Some structures require that some of the elements have to be forcefully
765 DER encoded. For example ``SignedData`` CMS requires you to encode
766 ``SignedAttributes`` and X.509 certificates in DER form, allowing you to
767 encode everything else in BER. You can tell any of the structures to be
768 forcefully encoded in DER during CER encoding, by specifying
769 ``der_forced=True`` attribute::
770
771     class Certificate(Sequence):
772         schema = (...)
773         der_forced = True
774
775     class SignedAttributes(SetOf):
776         schema = Attribute()
777         bounds = (1, float("+inf"))
778         der_forced = True
779
780 .. _agg_octet_string:
781
782 agg_octet_string
783 ________________
784
785 In most cases, huge quantity of binary data is stored as OCTET STRING.
786 CER encoding splits it on 1 KB chunks. BER allows splitting on various
787 levels of chunks inclusion::
788
789     SOME STRING[CONSTRUCTED]
790         OCTET STRING[CONSTRUCTED]
791             OCTET STRING[PRIMITIVE]
792                 DATA CHUNK
793             OCTET STRING[PRIMITIVE]
794                 DATA CHUNK
795             OCTET STRING[PRIMITIVE]
796                 DATA CHUNK
797         OCTET STRING[PRIMITIVE]
798             DATA CHUNK
799         OCTET STRING[CONSTRUCTED]
800             OCTET STRING[PRIMITIVE]
801                 DATA CHUNK
802             OCTET STRING[PRIMITIVE]
803                 DATA CHUNK
804         OCTET STRING[CONSTRUCTED]
805             OCTET STRING[CONSTRUCTED]
806                 OCTET STRING[PRIMITIVE]
807                     DATA CHUNK
808
809 You can not just take the offset and some ``.vlen`` of the STRING and
810 treat it as the payload. If you decode it without
811 :ref:`evgen mode <evgen_mode>`, then it will be automatically aggregated
812 and ``bytes()`` will give the whole payload contents.
813
814 You are forced to use :ref:`evgen mode <evgen_mode>` for decoding for
815 small memory footprint. There is convenient
816 :py:func:`pyderasn.agg_octet_string` helper for reconstructing the
817 payload. Let's assume you have got BER/CER encoded ``ContentInfo`` with
818 huge ``SignedData`` and ``EncapsulatedContentInfo``. Let's calculate the
819 SHA512 digest of its ``eContent``::
820
821     fd = open("data.p7m", "rb")
822     raw = file_mmaped(fd)
823     ctx = {"bered": True}
824     for decode_path, obj, _ in ContentInfo().decode_evgen(raw, ctx=ctx):
825         if decode_path == ("content",):
826             content = obj
827             break
828     else:
829         raise ValueError("no content found")
830     hasher_state = sha512()
831     def hasher(data):
832         hasher_state.update(data)
833         return len(data)
834     evgens = SignedData().decode_evgen(
835         raw[content.offset:],
836         offset=content.offset,
837         ctx=ctx,
838     )
839     agg_octet_string(evgens, ("encapContentInfo", "eContent"), raw, hasher)
840     fd.close()
841     digest = hasher_state.digest()
842
843 Simply replace ``hasher`` with some writeable file's ``fd.write`` to
844 copy the payload (without BER/CER encoding interleaved overhead) in it.
845 Virtually it won't take memory more than for keeping small structures
846 and 1 KB binary chunks.
847
848 .. _seqof-iterators:
849
850 SEQUENCE OF iterators
851 _____________________
852
853 You can use iterators as a value in :py:class:`pyderasn.SequenceOf`
854 classes. The only difference with providing the full list of objects, is
855 that type and bounds checking is done during encoding process. Also
856 sequence's value will be emptied after encoding, forcing you to set its
857 value again.
858
859 This is very useful when you have to create some huge objects, like
860 CRLs, with thousands and millions of entities inside. You can write the
861 generator taking necessary data from the database and giving the
862 ``RevokedCertificate`` objects. Only binary representation of that
863 objects will take memory during DER encoding.
864
865 2-pass DER encoding
866 -------------------
867
868 There is ability to do 2-pass encoding to DER, writing results directly
869 to specified writer (buffer, file, whatever). It could be 1.5+ times
870 slower than ordinary encoding, but it takes little memory for 1st pass
871 state storing. For example, 1st pass state for CACert.org's CRL with
872 ~416K of certificate entries takes nearly 3.5 MB of memory.
873 ``SignedData`` with several gigabyte ``EncapsulatedContentInfo`` takes
874 nearly 0.5 KB of memory.
875
876 If you use :ref:`mmap-ed <mmap>` memoryviews, :ref:`SEQUENCE OF
877 iterators <seqof-iterators>` and write directly to opened file, then
878 there is very small memory footprint.
879
880 1st pass traverses through all the objects of the structure and returns
881 the size of DER encoded structure, together with 1st pass state object.
882 That state contains precalculated lengths for various objects inside the
883 structure.
884
885 ::
886
887     fulllen, state = obj.encode1st()
888
889 2nd pass takes the writer and 1st pass state. It traverses through all
890 the objects again, but writes their encoded representation to the writer.
891
892 ::
893
894     with open("result", "wb") as fd:
895         obj.encode2nd(fd.write, iter(state))
896
897 .. warning::
898
899    You **MUST NOT** use 1st pass state if anything is changed in the
900    objects. It is intended to be used immediately after 1st pass is
901    done!
902
903 If you use :ref:`SEQUENCE OF iterators <seqof-iterators>`, then you
904 have to reinitialize the values after the 1st pass. And you **have to**
905 be sure that the iterator gives exactly the same values as previously.
906 Yes, you have to run your iterator twice -- because this is two pass
907 encoding mode.
908
909 If you want to encode to the memory, then you can use convenient
910 :py:func:`pyderasn.encode2pass` helper.
911
912 .. _browser:
913
914 ASN.1 browser
915 -------------
916 .. autofunction:: pyderasn.browse
917
918 Base Obj
919 --------
920 .. autoclass:: pyderasn.Obj
921    :members:
922
923 Primitive types
924 ---------------
925
926 Boolean
927 _______
928 .. autoclass:: pyderasn.Boolean
929    :members: __init__
930
931 Integer
932 _______
933 .. autoclass:: pyderasn.Integer
934    :members: __init__, named, tohex
935
936 BitString
937 _________
938 .. autoclass:: pyderasn.BitString
939    :members: __init__, bit_len, named
940
941 OctetString
942 ___________
943 .. autoclass:: pyderasn.OctetString
944    :members: __init__
945
946 Null
947 ____
948 .. autoclass:: pyderasn.Null
949    :members: __init__
950
951 ObjectIdentifier
952 ________________
953 .. autoclass:: pyderasn.ObjectIdentifier
954    :members: __init__
955
956 Enumerated
957 __________
958 .. autoclass:: pyderasn.Enumerated
959
960 CommonString
961 ____________
962 .. autoclass:: pyderasn.CommonString
963
964 NumericString
965 _____________
966 .. autoclass:: pyderasn.NumericString
967
968 PrintableString
969 _______________
970 .. autoclass:: pyderasn.PrintableString
971    :members: __init__, allow_asterisk, allow_ampersand
972
973 IA5String
974 _________
975 .. autoclass:: pyderasn.IA5String
976
977 VisibleString
978 _____________
979 .. autoclass:: pyderasn.VisibleString
980
981 UTCTime
982 _______
983 .. autoclass:: pyderasn.UTCTime
984    :members: __init__, todatetime, totzdatetime
985
986 GeneralizedTime
987 _______________
988 .. autoclass:: pyderasn.GeneralizedTime
989    :members: __init__, todatetime, totzdatetime
990
991 Special types
992 -------------
993
994 Choice
995 ______
996 .. autoclass:: pyderasn.Choice
997    :members: __init__, choice, value
998
999 PrimitiveTypes
1000 ______________
1001 .. autoclass:: PrimitiveTypes
1002
1003 Any
1004 ___
1005 .. autoclass:: pyderasn.Any
1006    :members: __init__
1007
1008 Constructed types
1009 -----------------
1010
1011 Sequence
1012 ________
1013 .. autoclass:: pyderasn.Sequence
1014    :members: __init__
1015
1016 Set
1017 ___
1018 .. autoclass:: pyderasn.Set
1019    :members: __init__
1020
1021 SequenceOf
1022 __________
1023 .. autoclass:: pyderasn.SequenceOf
1024    :members: __init__
1025
1026 SetOf
1027 _____
1028 .. autoclass:: pyderasn.SetOf
1029    :members: __init__
1030
1031 Various
1032 -------
1033
1034 .. autofunction:: pyderasn.abs_decode_path
1035 .. autofunction:: pyderasn.agg_octet_string
1036 .. autofunction:: pyderasn.ascii_visualize
1037 .. autofunction:: pyderasn.colonize_hex
1038 .. autofunction:: pyderasn.encode2pass
1039 .. autofunction:: pyderasn.encode_cer
1040 .. autofunction:: pyderasn.file_mmaped
1041 .. autofunction:: pyderasn.hexenc
1042 .. autofunction:: pyderasn.hexdec
1043 .. autofunction:: pyderasn.hexdump
1044 .. autofunction:: pyderasn.tag_encode
1045 .. autofunction:: pyderasn.tag_decode
1046 .. autofunction:: pyderasn.tag_ctxp
1047 .. autofunction:: pyderasn.tag_ctxc
1048 .. autoclass:: pyderasn.DecodeError
1049    :members: __init__
1050 .. autoclass:: pyderasn.NotEnoughData
1051 .. autoclass:: pyderasn.ExceedingData
1052 .. autoclass:: pyderasn.LenIndefForm
1053 .. autoclass:: pyderasn.TagMismatch
1054 .. autoclass:: pyderasn.InvalidLength
1055 .. autoclass:: pyderasn.InvalidOID
1056 .. autoclass:: pyderasn.ObjUnknown
1057 .. autoclass:: pyderasn.ObjNotReady
1058 .. autoclass:: pyderasn.InvalidValueType
1059 .. autoclass:: pyderasn.BoundsError
1060
1061 .. _cmdline:
1062
1063 Command-line usage
1064 ------------------
1065
1066 You can decode DER/BER files using command line abilities::
1067
1068     $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
1069
1070 If there is no schema for your file, then you can try parsing it without,
1071 but of course IMPLICIT tags will often make it impossible. But result is
1072 good enough for the certificate above::
1073
1074     $ python -m pyderasn path/to/file
1075         0   [1,3,1604]  . >: SEQUENCE OF
1076         4   [1,3,1453]  . . >: SEQUENCE OF
1077         8   [0,0,   5]  . . . . >: [0] ANY
1078                         . . . . . A0:03:02:01:02
1079        13   [1,1,   3]  . . . . >: INTEGER 61595
1080        18   [1,1,  13]  . . . . >: SEQUENCE OF
1081        20   [1,1,   9]  . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1082        31   [1,1,   0]  . . . . . . >: NULL
1083        33   [1,3, 274]  . . . . >: SEQUENCE OF
1084        37   [1,1,  11]  . . . . . . >: SET OF
1085        39   [1,1,   9]  . . . . . . . . >: SEQUENCE OF
1086        41   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
1087        46   [1,1,   2]  . . . . . . . . . . >: PrintableString PrintableString ES
1088     [...]
1089      1409   [1,1,  50]  . . . . . . >: SEQUENCE OF
1090      1411   [1,1,   8]  . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
1091      1421   [1,1,  38]  . . . . . . . . >: OCTET STRING 38 bytes
1092                         . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
1093                         . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
1094                         . . . . . . . . . 61:2E:63:6F:6D:2F
1095      1461   [1,1,  13]  . . >: SEQUENCE OF
1096      1463   [1,1,   9]  . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1097      1474   [1,1,   0]  . . . . >: NULL
1098      1476   [1,2, 129]  . . >: BIT STRING 1024 bits
1099                         . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
1100                         . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
1101     [...]
1102
1103 Human readable OIDs
1104 ___________________
1105
1106 If you have got dictionaries with ObjectIdentifiers, like example one
1107 from ``tests/test_crts.py``::
1108
1109     stroid2name = {
1110         "1.2.840.113549.1.1.1": "id-rsaEncryption",
1111         "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
1112         [...]
1113         "2.5.4.10": "id-at-organizationName",
1114         "2.5.4.11": "id-at-organizationalUnitName",
1115     }
1116
1117 then you can pass it to pretty printer to see human readable OIDs::
1118
1119     $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
1120     [...]
1121        37   [1,1,  11]  . . . . . . >: SET OF
1122        39   [1,1,   9]  . . . . . . . . >: SEQUENCE OF
1123        41   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
1124        46   [1,1,   2]  . . . . . . . . . . >: PrintableString PrintableString ES
1125        50   [1,1,  18]  . . . . . . >: SET OF
1126        52   [1,1,  16]  . . . . . . . . >: SEQUENCE OF
1127        54   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
1128        59   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
1129        70   [1,1,  18]  . . . . . . >: SET OF
1130        72   [1,1,  16]  . . . . . . . . >: SEQUENCE OF
1131        74   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
1132        79   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
1133     [...]
1134
1135 Decode paths
1136 ____________
1137
1138 Each decoded element has so-called decode path: sequence of structure
1139 names it is passing during the decode process. Each element has its own
1140 unique path inside the whole ASN.1 tree. You can print it out with
1141 ``--print-decode-path`` option::
1142
1143     $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
1144        0    [1,3,1604]  Certificate SEQUENCE []
1145        4    [1,3,1453]   . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
1146       10-2  [1,1,   1]   . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
1147       13    [1,1,   3]   . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
1148       18    [1,1,  13]   . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
1149       20    [1,1,   9]   . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
1150       31    [0,0,   2]   . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
1151                          . . . . 05:00
1152       33    [0,0, 278]   . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
1153       33    [1,3, 274]   . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
1154       37    [1,1,  11]   . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
1155       39    [1,1,   9]   . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
1156       41    [1,1,   3]   . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
1157       46    [0,0,   4]   . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
1158                          . . . . . . . 13:02:45:53
1159       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]
1160     [...]
1161
1162 Now you can print only the specified tree, for example signature algorithm::
1163
1164     $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
1165       18    [1,1,  13]  AlgorithmIdentifier SEQUENCE
1166       20    [1,1,   9]   . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
1167       31    [0,0,   2]   . parameters: [UNIV 5] ANY OPTIONAL
1168                          . . 05:00
1169 """
1170
1171 from array import array
1172 from collections import namedtuple
1173 from collections import OrderedDict
1174 from copy import copy
1175 from datetime import datetime
1176 from datetime import timedelta
1177 from io import BytesIO
1178 from math import ceil
1179 from operator import attrgetter
1180 from string import ascii_letters
1181 from string import digits
1182 from struct import Struct as struct_Struct
1183 from sys import maxsize as sys_maxsize
1184 from sys import version_info
1185 from unicodedata import category as unicat
1186
1187 try:
1188     from termcolor import colored
1189 except ImportError:  # pragma: no cover
1190     def colored(what, *args, **kwargs):
1191         return what
1192
1193 try:
1194     from dateutil.tz import UTC as tzUTC
1195 except ImportError:  # pragma: no cover
1196     tzUTC = "missing"
1197
1198
1199 __version__ = "9.2"
1200
1201 __all__ = (
1202     "agg_octet_string",
1203     "Any",
1204     "BitString",
1205     "BMPString",
1206     "Boolean",
1207     "BoundsError",
1208     "Choice",
1209     "colonize_hex",
1210     "DecodeError",
1211     "DecodePathDefBy",
1212     "encode2pass",
1213     "encode_cer",
1214     "Enumerated",
1215     "ExceedingData",
1216     "file_mmaped",
1217     "GeneralizedTime",
1218     "GeneralString",
1219     "GraphicString",
1220     "hexdec",
1221     "hexenc",
1222     "IA5String",
1223     "Integer",
1224     "InvalidLength",
1225     "InvalidOID",
1226     "InvalidValueType",
1227     "ISO646String",
1228     "LenIndefForm",
1229     "NotEnoughData",
1230     "Null",
1231     "NumericString",
1232     "obj_by_path",
1233     "ObjectIdentifier",
1234     "ObjNotReady",
1235     "ObjUnknown",
1236     "OctetString",
1237     "PrimitiveTypes",
1238     "PrintableString",
1239     "Sequence",
1240     "SequenceOf",
1241     "Set",
1242     "SetOf",
1243     "T61String",
1244     "tag_ctxc",
1245     "tag_ctxp",
1246     "tag_decode",
1247     "TagClassApplication",
1248     "TagClassContext",
1249     "TagClassPrivate",
1250     "TagClassUniversal",
1251     "TagFormConstructed",
1252     "TagFormPrimitive",
1253     "TagMismatch",
1254     "TeletexString",
1255     "UniversalString",
1256     "UTCTime",
1257     "UTF8String",
1258     "VideotexString",
1259     "VisibleString",
1260 )
1261
1262 TagClassUniversal = 0
1263 TagClassApplication = 1 << 6
1264 TagClassContext = 1 << 7
1265 TagClassPrivate = 1 << 6 | 1 << 7
1266 TagFormPrimitive = 0
1267 TagFormConstructed = 1 << 5
1268 TagClassReprs = {
1269     TagClassContext: "",
1270     TagClassApplication: "APPLICATION ",
1271     TagClassPrivate: "PRIVATE ",
1272     TagClassUniversal: "UNIV ",
1273 }
1274 EOC = b"\x00\x00"
1275 EOC_LEN = len(EOC)
1276 LENINDEF = b"\x80"  # length indefinite mark
1277 LENINDEF_PP_CHAR = "∞"
1278 NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
1279 SET01 = frozenset("01")
1280 DECIMALS = frozenset(digits)
1281 DECIMAL_SIGNS = ".,"
1282 NEXT_ATTR_NAME = "__next__"
1283
1284
1285 def file_mmaped(fd):
1286     """Make mmap-ed memoryview for reading from file
1287
1288     :param fd: file object
1289     :returns: memoryview over read-only mmap-ing of the whole file
1290
1291     .. warning::
1292
1293        It does not work under Windows.
1294     """
1295     import mmap
1296     return memoryview(mmap.mmap(fd.fileno(), length=0, prot=mmap.PROT_READ))
1297
1298
1299 def pureint(value):
1300     if not set(value) <= DECIMALS:
1301         raise ValueError("non-pure integer")
1302     return int(value)
1303
1304
1305 def fractions2float(fractions_raw):
1306     pureint(fractions_raw)
1307     return float("0." + fractions_raw)
1308
1309
1310 def get_def_by_path(defines_by_path, sub_decode_path):
1311     """Get define by decode path
1312     """
1313     for path, define in defines_by_path:
1314         if len(path) != len(sub_decode_path):
1315             continue
1316         for p1, p2 in zip(path, sub_decode_path):
1317             if (p1 is not any) and (p1 != p2):
1318                 break
1319         else:
1320             return define
1321
1322
1323 ########################################################################
1324 # Errors
1325 ########################################################################
1326
1327 class ASN1Error(ValueError):
1328     pass
1329
1330
1331 class DecodeError(ASN1Error):
1332     def __init__(self, msg="", klass=None, decode_path=(), offset=0):
1333         """
1334         :param str msg: reason of decode failing
1335         :param klass: optional exact DecodeError inherited class (like
1336                       :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
1337                       :py:exc:`InvalidLength`)
1338         :param decode_path: tuple of strings. It contains human
1339                             readable names of the fields through which
1340                             decoding process has passed
1341         :param int offset: binary offset where failure happened
1342         """
1343         super().__init__()
1344         self.msg = msg
1345         self.klass = klass
1346         self.decode_path = decode_path
1347         self.offset = offset
1348
1349     def __str__(self):
1350         return " ".join(
1351             c for c in (
1352                 "" if self.klass is None else self.klass.__name__,
1353                 (
1354                     ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
1355                     if len(self.decode_path) > 0 else ""
1356                 ),
1357                 ("(at %d)" % self.offset) if self.offset > 0 else "",
1358                 self.msg,
1359             ) if c != ""
1360         )
1361
1362     def __repr__(self):
1363         return "%s(%s)" % (self.__class__.__name__, self)
1364
1365
1366 class NotEnoughData(DecodeError):
1367     pass
1368
1369
1370 class ExceedingData(ASN1Error):
1371     def __init__(self, nbytes):
1372         super().__init__()
1373         self.nbytes = nbytes
1374
1375     def __str__(self):
1376         return "%d trailing bytes" % self.nbytes
1377
1378     def __repr__(self):
1379         return "%s(%s)" % (self.__class__.__name__, self)
1380
1381
1382 class LenIndefForm(DecodeError):
1383     pass
1384
1385
1386 class TagMismatch(DecodeError):
1387     pass
1388
1389
1390 class InvalidLength(DecodeError):
1391     pass
1392
1393
1394 class InvalidOID(DecodeError):
1395     pass
1396
1397
1398 class ObjUnknown(ASN1Error):
1399     def __init__(self, name):
1400         super().__init__()
1401         self.name = name
1402
1403     def __str__(self):
1404         return "object is unknown: %s" % self.name
1405
1406     def __repr__(self):
1407         return "%s(%s)" % (self.__class__.__name__, self)
1408
1409
1410 class ObjNotReady(ASN1Error):
1411     def __init__(self, name):
1412         super().__init__()
1413         self.name = name
1414
1415     def __str__(self):
1416         return "object is not ready: %s" % self.name
1417
1418     def __repr__(self):
1419         return "%s(%s)" % (self.__class__.__name__, self)
1420
1421
1422 class InvalidValueType(ASN1Error):
1423     def __init__(self, expected_types):
1424         super().__init__()
1425         self.expected_types = expected_types
1426
1427     def __str__(self):
1428         return "invalid value type, expected: %s" % ", ".join(
1429             [repr(t) for t in self.expected_types]
1430         )
1431
1432     def __repr__(self):
1433         return "%s(%s)" % (self.__class__.__name__, self)
1434
1435
1436 class BoundsError(ASN1Error):
1437     def __init__(self, bound_min, value, bound_max):
1438         super().__init__()
1439         self.bound_min = bound_min
1440         self.value = value
1441         self.bound_max = bound_max
1442
1443     def __str__(self):
1444         return "unsatisfied bounds: %s <= %s <= %s" % (
1445             self.bound_min,
1446             self.value,
1447             self.bound_max,
1448         )
1449
1450     def __repr__(self):
1451         return "%s(%s)" % (self.__class__.__name__, self)
1452
1453
1454 ########################################################################
1455 # Basic coders
1456 ########################################################################
1457
1458 def hexdec(data):
1459     """Binary data to hexadecimal string convert
1460     """
1461     return bytes.fromhex(data)
1462
1463
1464 def hexenc(data):
1465     """Hexadecimal string to binary data convert
1466     """
1467     return data.hex()
1468
1469
1470 def int_bytes_len(num, byte_len=8):
1471     if num == 0:
1472         return 1
1473     return int(ceil(float(num.bit_length()) / byte_len))
1474
1475
1476 def zero_ended_encode(num):
1477     octets = bytearray(int_bytes_len(num, 7))
1478     i = len(octets) - 1
1479     octets[i] = num & 0x7F
1480     num >>= 7
1481     i -= 1
1482     while num > 0:
1483         octets[i] = 0x80 | (num & 0x7F)
1484         num >>= 7
1485         i -= 1
1486     return bytes(octets)
1487
1488
1489 int2byte = struct_Struct(">B").pack
1490
1491
1492 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
1493     """Encode tag to binary form
1494
1495     :param int num: tag's number
1496     :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
1497                       :py:data:`pyderasn.TagClassContext`,
1498                       :py:data:`pyderasn.TagClassApplication`,
1499                       :py:data:`pyderasn.TagClassPrivate`)
1500     :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
1501                      :py:data:`pyderasn.TagFormConstructed`)
1502     """
1503     if num < 31:
1504         # [XX|X|.....]
1505         return int2byte(klass | form | num)
1506     # [XX|X|11111][1.......][1.......] ... [0.......]
1507     return int2byte(klass | form | 31) + zero_ended_encode(num)
1508
1509
1510 def tag_decode(tag):
1511     """Decode tag from binary form
1512
1513     .. warning::
1514
1515        No validation is performed, assuming that it has already passed.
1516
1517     It returns tuple with three integers, as
1518     :py:func:`pyderasn.tag_encode` accepts.
1519     """
1520     first_octet = tag[0]
1521     klass = first_octet & 0xC0
1522     form = first_octet & 0x20
1523     if first_octet & 0x1F < 0x1F:
1524         return (klass, form, first_octet & 0x1F)
1525     num = 0
1526     for octet in tag[1:]:
1527         num <<= 7
1528         num |= octet & 0x7F
1529     return (klass, form, num)
1530
1531
1532 def tag_ctxp(num):
1533     """Create CONTEXT PRIMITIVE tag
1534     """
1535     return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
1536
1537
1538 def tag_ctxc(num):
1539     """Create CONTEXT CONSTRUCTED tag
1540     """
1541     return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
1542
1543
1544 def tag_strip(data):
1545     """Take off tag from the data
1546
1547     :returns: (encoded tag, tag length, remaining data)
1548     """
1549     if len(data) == 0:
1550         raise NotEnoughData("no data at all")
1551     if data[0] & 0x1F < 31:
1552         return data[:1], 1, data[1:]
1553     i = 0
1554     while True:
1555         i += 1
1556         if i == len(data):
1557             raise DecodeError("unfinished tag")
1558         if data[i] & 0x80 == 0:
1559             break
1560     if i == 1 and data[1] < 0x1F:
1561         raise DecodeError("unexpected long form")
1562     if i > 1 and data[1] & 0x7F == 0:
1563         raise DecodeError("leading zero byte in tag value")
1564     i += 1
1565     return data[:i], i, data[i:]
1566
1567
1568 def len_encode(l):
1569     if l < 0x80:
1570         return int2byte(l)
1571     octets = bytearray(int_bytes_len(l) + 1)
1572     octets[0] = 0x80 | (len(octets) - 1)
1573     for i in range(len(octets) - 1, 0, -1):
1574         octets[i] = l & 0xFF
1575         l >>= 8
1576     return bytes(octets)
1577
1578
1579 def len_decode(data):
1580     """Decode length
1581
1582     :returns: (decoded length, length's length, remaining data)
1583     :raises LenIndefForm: if indefinite form encoding is met
1584     """
1585     if len(data) == 0:
1586         raise NotEnoughData("no data at all")
1587     first_octet = data[0]
1588     if first_octet & 0x80 == 0:
1589         return first_octet, 1, data[1:]
1590     octets_num = first_octet & 0x7F
1591     if octets_num + 1 > len(data):
1592         raise NotEnoughData("encoded length is longer than data")
1593     if octets_num == 0:
1594         raise LenIndefForm()
1595     if data[1] == 0:
1596         raise DecodeError("leading zeros")
1597     l = 0
1598     for v in data[1:1 + octets_num]:
1599         l = (l << 8) | v
1600     if l <= 127:
1601         raise DecodeError("long form instead of short one")
1602     return l, 1 + octets_num, data[1 + octets_num:]
1603
1604
1605 LEN0 = len_encode(0)
1606 LEN1 = len_encode(1)
1607 LEN1K = len_encode(1000)
1608
1609
1610 def len_size(l):
1611     """How many bytes length field will take
1612     """
1613     if l < 128:
1614         return 1
1615     if l < 256:  # 1 << 8
1616         return 2
1617     if l < 65536:  # 1 << 16
1618         return 3
1619     if l < 16777216:  # 1 << 24
1620         return 4
1621     if l < 4294967296:  # 1 << 32
1622         return 5
1623     if l < 1099511627776:  # 1 << 40
1624         return 6
1625     if l < 281474976710656:  # 1 << 48
1626         return 7
1627     if l < 72057594037927936:  # 1 << 56
1628         return 8
1629     raise OverflowError("too big length")
1630
1631
1632 def write_full(writer, data):
1633     """Fully write provided data
1634
1635     :param writer: must comply with ``io.RawIOBase.write`` behaviour
1636
1637     BytesIO does not guarantee that the whole data will be written at
1638     once. That function write everything provided, raising an error if
1639     ``writer`` returns None.
1640     """
1641     data = memoryview(data)
1642     written = 0
1643     while written != len(data):
1644         n = writer(data[written:])
1645         if n is None:
1646             raise ValueError("can not write to buf")
1647         written += n
1648
1649
1650 # If it is 64-bit system, then use compact 64-bit array of unsigned
1651 # longs. Use an ordinary list with universal integers otherwise, that
1652 # is slower.
1653 if sys_maxsize > 2 ** 32:
1654     def state_2pass_new():
1655         return array("L")
1656 else:
1657     def state_2pass_new():
1658         return []
1659
1660
1661 ########################################################################
1662 # Base class
1663 ########################################################################
1664
1665 class AutoAddSlots(type):
1666     def __new__(cls, name, bases, _dict):
1667         _dict["__slots__"] = _dict.get("__slots__", ())
1668         return super().__new__(cls, name, bases, _dict)
1669
1670
1671 BasicState = namedtuple("BasicState", (
1672     "version",
1673     "tag",
1674     "tag_order",
1675     "expl",
1676     "default",
1677     "optional",
1678     "offset",
1679     "llen",
1680     "vlen",
1681     "expl_lenindef",
1682     "lenindef",
1683     "ber_encoded",
1684 ), **NAMEDTUPLE_KWARGS)
1685
1686
1687 class Obj(metaclass=AutoAddSlots):
1688     """Common ASN.1 object class
1689
1690     All ASN.1 types are inherited from it. It has metaclass that
1691     automatically adds ``__slots__`` to all inherited classes.
1692     """
1693     __slots__ = (
1694         "tag",
1695         "_tag_order",
1696         "_value",
1697         "_expl",
1698         "default",
1699         "optional",
1700         "offset",
1701         "llen",
1702         "vlen",
1703         "expl_lenindef",
1704         "lenindef",
1705         "ber_encoded",
1706     )
1707
1708     def __init__(
1709             self,
1710             impl=None,
1711             expl=None,
1712             default=None,
1713             optional=False,
1714             _decoded=(0, 0, 0),
1715     ):
1716         self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1717         self._expl = getattr(self, "expl", None) if expl is None else expl
1718         if self.tag != self.tag_default and self._expl is not None:
1719             raise ValueError("implicit and explicit tags can not be set simultaneously")
1720         if self.tag is None:
1721             self._tag_order = None
1722         else:
1723             tag_class, _, tag_num = tag_decode(
1724                 self.tag if self._expl is None else self._expl
1725             )
1726             self._tag_order = (tag_class, tag_num)
1727         if default is not None:
1728             optional = True
1729         self.optional = optional
1730         self.offset, self.llen, self.vlen = _decoded
1731         self.default = None
1732         self.expl_lenindef = False
1733         self.lenindef = False
1734         self.ber_encoded = False
1735
1736     @property
1737     def ready(self):  # pragma: no cover
1738         """Is object ready to be encoded?
1739         """
1740         raise NotImplementedError()
1741
1742     def _assert_ready(self):
1743         if not self.ready:
1744             raise ObjNotReady(self.__class__.__name__)
1745
1746     @property
1747     def bered(self):
1748         """Is either object or any elements inside is BER encoded?
1749         """
1750         return self.expl_lenindef or self.lenindef or self.ber_encoded
1751
1752     @property
1753     def decoded(self):
1754         """Is object decoded?
1755         """
1756         return (self.llen + self.vlen) > 0
1757
1758     def __getstate__(self):  # pragma: no cover
1759         """Used for making safe to be mutable pickleable copies
1760         """
1761         raise NotImplementedError()
1762
1763     def __setstate__(self, state):
1764         if state.version != __version__:
1765             raise ValueError("data is pickled by different PyDERASN version")
1766         self.tag = state.tag
1767         self._tag_order = state.tag_order
1768         self._expl = state.expl
1769         self.default = state.default
1770         self.optional = state.optional
1771         self.offset = state.offset
1772         self.llen = state.llen
1773         self.vlen = state.vlen
1774         self.expl_lenindef = state.expl_lenindef
1775         self.lenindef = state.lenindef
1776         self.ber_encoded = state.ber_encoded
1777
1778     @property
1779     def tag_order(self):
1780         """Tag's (class, number) used for DER/CER sorting
1781         """
1782         return self._tag_order
1783
1784     @property
1785     def tag_order_cer(self):
1786         return self.tag_order
1787
1788     @property
1789     def tlen(self):
1790         """.. seealso:: :ref:`decoding`
1791         """
1792         return len(self.tag)
1793
1794     @property
1795     def tlvlen(self):
1796         """.. seealso:: :ref:`decoding`
1797         """
1798         return self.tlen + self.llen + self.vlen
1799
1800     def __str__(self):  # pragma: no cover
1801         return self.__unicode__()
1802
1803     def __ne__(self, their):
1804         return not(self == their)
1805
1806     def __gt__(self, their):  # pragma: no cover
1807         return not(self < their)
1808
1809     def __le__(self, their):  # pragma: no cover
1810         return (self == their) or (self < their)
1811
1812     def __ge__(self, their):  # pragma: no cover
1813         return (self == their) or (self > their)
1814
1815     def _encode(self):  # pragma: no cover
1816         raise NotImplementedError()
1817
1818     def _encode_cer(self, writer):
1819         write_full(writer, self._encode())
1820
1821     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):  # pragma: no cover
1822         yield NotImplemented
1823
1824     def _encode1st(self, state):
1825         raise NotImplementedError()
1826
1827     def _encode2nd(self, writer, state_iter):
1828         raise NotImplementedError()
1829
1830     def encode(self):
1831         """DER encode the structure
1832
1833         :returns: DER representation
1834         """
1835         raw = self._encode()
1836         if self._expl is None:
1837             return raw
1838         return b"".join((self._expl, len_encode(len(raw)), raw))
1839
1840     def encode1st(self, state=None):
1841         """Do the 1st pass of 2-pass encoding
1842
1843         :rtype: (int, array("L"))
1844         :returns: full length of encoded data and precalculated various
1845                   objects lengths
1846         """
1847         if state is None:
1848             state = state_2pass_new()
1849         if self._expl is None:
1850             return self._encode1st(state)
1851         state.append(0)
1852         idx = len(state) - 1
1853         vlen, _ = self._encode1st(state)
1854         state[idx] = vlen
1855         fulllen = len(self._expl) + len_size(vlen) + vlen
1856         return fulllen, state
1857
1858     def encode2nd(self, writer, state_iter):
1859         """Do the 2nd pass of 2-pass encoding
1860
1861         :param writer: must comply with ``io.RawIOBase.write`` behaviour
1862         :param state_iter: iterator over the 1st pass state (``iter(state)``)
1863         """
1864         if self._expl is None:
1865             self._encode2nd(writer, state_iter)
1866         else:
1867             write_full(writer, self._expl + len_encode(next(state_iter)))
1868             self._encode2nd(writer, state_iter)
1869
1870     def encode_cer(self, writer):
1871         """CER encode the structure to specified writer
1872
1873         :param writer: must comply with ``io.RawIOBase.write``
1874                        behaviour. It takes slice to be written and
1875                        returns number of bytes processed. If it returns
1876                        None, then exception will be raised
1877         """
1878         if self._expl is not None:
1879             write_full(writer, self._expl + LENINDEF)
1880         if getattr(self, "der_forced", False):
1881             write_full(writer, self._encode())
1882         else:
1883             self._encode_cer(writer)
1884         if self._expl is not None:
1885             write_full(writer, EOC)
1886
1887     def hexencode(self):
1888         """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
1889         """
1890         return hexenc(self.encode())
1891
1892     def decode(
1893             self,
1894             data,
1895             offset=0,
1896             leavemm=False,
1897             decode_path=(),
1898             ctx=None,
1899             tag_only=False,
1900             _ctx_immutable=True,
1901     ):
1902         """Decode the data
1903
1904         :param data: either binary or memoryview
1905         :param int offset: initial data's offset
1906         :param bool leavemm: do we need to leave memoryview of remaining
1907                     data as is, or convert it to bytes otherwise
1908         :param decode_path: current decode path (tuples of strings,
1909                             possibly with DecodePathDefBy) with will be
1910                             the root for all underlying objects
1911         :param ctx: optional :ref:`context <ctx>` governing decoding process
1912         :param bool tag_only: decode only the tag, without length and
1913                               contents (used only in Choice and Set
1914                               structures, trying to determine if tag satisfies
1915                               the schema)
1916         :param bool _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
1917                                     before using it?
1918         :returns: (Obj, remaining data)
1919
1920         .. seealso:: :ref:`decoding`
1921         """
1922         result = next(self.decode_evgen(
1923             data,
1924             offset,
1925             leavemm,
1926             decode_path,
1927             ctx,
1928             tag_only,
1929             _ctx_immutable,
1930             _evgen_mode=False,
1931         ))
1932         if result is None:
1933             return None
1934         _, obj, tail = result
1935         return obj, tail
1936
1937     def decode_evgen(
1938             self,
1939             data,
1940             offset=0,
1941             leavemm=False,
1942             decode_path=(),
1943             ctx=None,
1944             tag_only=False,
1945             _ctx_immutable=True,
1946             _evgen_mode=True,
1947     ):
1948         """Decode with evgen mode on
1949
1950         That method is identical to :py:meth:`pyderasn.Obj.decode`, but
1951         it returns the generator producing ``(decode_path, obj, tail)``
1952         values.
1953         .. seealso:: :ref:`evgen mode <evgen_mode>`.
1954         """
1955         if ctx is None:
1956             ctx = {}
1957         elif _ctx_immutable:
1958             ctx = copy(ctx)
1959         tlv = memoryview(data)
1960         if (
1961                 _evgen_mode and
1962                 get_def_by_path(ctx.get("evgen_mode_upto", ()), decode_path) is not None
1963         ):
1964             _evgen_mode = False
1965         if self._expl is None:
1966             for result in self._decode(
1967                     tlv,
1968                     offset=offset,
1969                     decode_path=decode_path,
1970                     ctx=ctx,
1971                     tag_only=tag_only,
1972                     evgen_mode=_evgen_mode,
1973             ):
1974                 if tag_only:
1975                     yield None
1976                     return
1977                 _decode_path, obj, tail = result
1978                 if _decode_path is not decode_path:
1979                     yield result
1980         else:
1981             try:
1982                 t, tlen, lv = tag_strip(tlv)
1983             except DecodeError as err:
1984                 raise err.__class__(
1985                     msg=err.msg,
1986                     klass=self.__class__,
1987                     decode_path=decode_path,
1988                     offset=offset,
1989                 )
1990             if t != self._expl:
1991                 raise TagMismatch(
1992                     klass=self.__class__,
1993                     decode_path=decode_path,
1994                     offset=offset,
1995                 )
1996             try:
1997                 l, llen, v = len_decode(lv)
1998             except LenIndefForm as err:
1999                 if not ctx.get("bered", False):
2000                     raise err.__class__(
2001                         msg=err.msg,
2002                         klass=self.__class__,
2003                         decode_path=decode_path,
2004                         offset=offset,
2005                     )
2006                 llen, v = 1, lv[1:]
2007                 offset += tlen + llen
2008                 for result in self._decode(
2009                         v,
2010                         offset=offset,
2011                         decode_path=decode_path,
2012                         ctx=ctx,
2013                         tag_only=tag_only,
2014                         evgen_mode=_evgen_mode,
2015                 ):
2016                     if tag_only:  # pragma: no cover
2017                         yield None
2018                         return
2019                     _decode_path, obj, tail = result
2020                     if _decode_path is not decode_path:
2021                         yield result
2022                 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
2023                 if eoc_expected.tobytes() != EOC:
2024                     raise DecodeError(
2025                         "no EOC",
2026                         klass=self.__class__,
2027                         decode_path=decode_path,
2028                         offset=offset,
2029                     )
2030                 obj.vlen += EOC_LEN
2031                 obj.expl_lenindef = True
2032             except DecodeError as err:
2033                 raise err.__class__(
2034                     msg=err.msg,
2035                     klass=self.__class__,
2036                     decode_path=decode_path,
2037                     offset=offset,
2038                 )
2039             else:
2040                 if l > len(v):
2041                     raise NotEnoughData(
2042                         "encoded length is longer than data",
2043                         klass=self.__class__,
2044                         decode_path=decode_path,
2045                         offset=offset,
2046                     )
2047                 for result in self._decode(
2048                         v,
2049                         offset=offset + tlen + llen,
2050                         decode_path=decode_path,
2051                         ctx=ctx,
2052                         tag_only=tag_only,
2053                         evgen_mode=_evgen_mode,
2054                 ):
2055                     if tag_only:  # pragma: no cover
2056                         yield None
2057                         return
2058                     _decode_path, obj, tail = result
2059                     if _decode_path is not decode_path:
2060                         yield result
2061                 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
2062                     raise DecodeError(
2063                         "explicit tag out-of-bound, longer than data",
2064                         klass=self.__class__,
2065                         decode_path=decode_path,
2066                         offset=offset,
2067                     )
2068         yield decode_path, obj, (tail if leavemm else tail.tobytes())
2069
2070     def decod(self, data, offset=0, decode_path=(), ctx=None):
2071         """Decode the data, check that tail is empty
2072
2073         :raises ExceedingData: if tail is not empty
2074
2075         This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
2076         (decode without tail) that also checks that there is no
2077         trailing data left.
2078         """
2079         obj, tail = self.decode(
2080             data,
2081             offset=offset,
2082             decode_path=decode_path,
2083             ctx=ctx,
2084             leavemm=True,
2085         )
2086         if len(tail) > 0:
2087             raise ExceedingData(len(tail))
2088         return obj
2089
2090     def hexdecode(self, data, *args, **kwargs):
2091         """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
2092         """
2093         return self.decode(hexdec(data), *args, **kwargs)
2094
2095     def hexdecod(self, data, *args, **kwargs):
2096         """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
2097         """
2098         return self.decod(hexdec(data), *args, **kwargs)
2099
2100     @property
2101     def expled(self):
2102         """.. seealso:: :ref:`decoding`
2103         """
2104         return self._expl is not None
2105
2106     @property
2107     def expl_tag(self):
2108         """.. seealso:: :ref:`decoding`
2109         """
2110         return self._expl
2111
2112     @property
2113     def expl_tlen(self):
2114         """.. seealso:: :ref:`decoding`
2115         """
2116         return len(self._expl)
2117
2118     @property
2119     def expl_llen(self):
2120         """.. seealso:: :ref:`decoding`
2121         """
2122         if self.expl_lenindef:
2123             return 1
2124         return len(len_encode(self.tlvlen))
2125
2126     @property
2127     def expl_offset(self):
2128         """.. seealso:: :ref:`decoding`
2129         """
2130         return self.offset - self.expl_tlen - self.expl_llen
2131
2132     @property
2133     def expl_vlen(self):
2134         """.. seealso:: :ref:`decoding`
2135         """
2136         return self.tlvlen
2137
2138     @property
2139     def expl_tlvlen(self):
2140         """.. seealso:: :ref:`decoding`
2141         """
2142         return self.expl_tlen + self.expl_llen + self.expl_vlen
2143
2144     @property
2145     def fulloffset(self):
2146         """.. seealso:: :ref:`decoding`
2147         """
2148         return self.expl_offset if self.expled else self.offset
2149
2150     @property
2151     def fulllen(self):
2152         """.. seealso:: :ref:`decoding`
2153         """
2154         return self.expl_tlvlen if self.expled else self.tlvlen
2155
2156     def pps_lenindef(self, decode_path):
2157         if self.lenindef and not (
2158                 getattr(self, "defined", None) is not None and
2159                 self.defined[1].lenindef
2160         ):
2161             yield _pp(
2162                 asn1_type_name="EOC",
2163                 obj_name="",
2164                 decode_path=decode_path,
2165                 offset=(
2166                     self.offset + self.tlvlen -
2167                     (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
2168                 ),
2169                 tlen=1,
2170                 llen=1,
2171                 vlen=0,
2172                 ber_encoded=True,
2173                 bered=True,
2174             )
2175         if self.expl_lenindef:
2176             yield _pp(
2177                 asn1_type_name="EOC",
2178                 obj_name="EXPLICIT",
2179                 decode_path=decode_path,
2180                 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
2181                 tlen=1,
2182                 llen=1,
2183                 vlen=0,
2184                 ber_encoded=True,
2185                 bered=True,
2186             )
2187
2188
2189 def encode_cer(obj):
2190     """Encode to CER in memory buffer
2191
2192     :returns bytes: memory buffer contents
2193     """
2194     buf = BytesIO()
2195     obj.encode_cer(buf.write)
2196     return buf.getvalue()
2197
2198
2199 def encode2pass(obj):
2200     """Encode (2-pass mode) to DER in memory buffer
2201
2202     :returns bytes: memory buffer contents
2203     """
2204     buf = BytesIO()
2205     _, state = obj.encode1st()
2206     obj.encode2nd(buf.write, iter(state))
2207     return buf.getvalue()
2208
2209
2210 class DecodePathDefBy:
2211     """DEFINED BY representation inside decode path
2212     """
2213     __slots__ = ("defined_by",)
2214
2215     def __init__(self, defined_by):
2216         self.defined_by = defined_by
2217
2218     def __ne__(self, their):
2219         return not(self == their)
2220
2221     def __eq__(self, their):
2222         if not isinstance(their, self.__class__):
2223             return False
2224         return self.defined_by == their.defined_by
2225
2226     def __str__(self):
2227         return "DEFINED BY " + str(self.defined_by)
2228
2229     def __repr__(self):
2230         return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
2231
2232
2233 ########################################################################
2234 # Pretty printing
2235 ########################################################################
2236
2237 PP = namedtuple("PP", (
2238     "obj",
2239     "asn1_type_name",
2240     "obj_name",
2241     "decode_path",
2242     "value",
2243     "blob",
2244     "optional",
2245     "default",
2246     "impl",
2247     "expl",
2248     "offset",
2249     "tlen",
2250     "llen",
2251     "vlen",
2252     "expl_offset",
2253     "expl_tlen",
2254     "expl_llen",
2255     "expl_vlen",
2256     "expl_lenindef",
2257     "lenindef",
2258     "ber_encoded",
2259     "bered",
2260 ), **NAMEDTUPLE_KWARGS)
2261
2262
2263 def _pp(
2264         obj=None,
2265         asn1_type_name="unknown",
2266         obj_name="unknown",
2267         decode_path=(),
2268         value=None,
2269         blob=None,
2270         optional=False,
2271         default=False,
2272         impl=None,
2273         expl=None,
2274         offset=0,
2275         tlen=0,
2276         llen=0,
2277         vlen=0,
2278         expl_offset=None,
2279         expl_tlen=None,
2280         expl_llen=None,
2281         expl_vlen=None,
2282         expl_lenindef=False,
2283         lenindef=False,
2284         ber_encoded=False,
2285         bered=False,
2286 ):
2287     return PP(
2288         obj,
2289         asn1_type_name,
2290         obj_name,
2291         decode_path,
2292         value,
2293         blob,
2294         optional,
2295         default,
2296         impl,
2297         expl,
2298         offset,
2299         tlen,
2300         llen,
2301         vlen,
2302         expl_offset,
2303         expl_tlen,
2304         expl_llen,
2305         expl_vlen,
2306         expl_lenindef,
2307         lenindef,
2308         ber_encoded,
2309         bered,
2310     )
2311
2312
2313 def _colourize(what, colour, with_colours, attrs=("bold",)):
2314     return colored(what, colour, attrs=attrs) if with_colours else what
2315
2316
2317 def colonize_hex(hexed):
2318     """Separate hexadecimal string with colons
2319     """
2320     return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
2321
2322
2323 def find_oid_name(asn1_type_name, oid_maps, value):
2324     if len(oid_maps) > 0 and asn1_type_name == ObjectIdentifier.asn1_type_name:
2325         for oid_map in oid_maps:
2326             oid_name = oid_map.get(value)
2327             if oid_name is not None:
2328                 return oid_name
2329     return None
2330
2331
2332 def pp_console_row(
2333         pp,
2334         oid_maps=(),
2335         with_offsets=False,
2336         with_blob=True,
2337         with_colours=False,
2338         with_decode_path=False,
2339         decode_path_len_decrease=0,
2340 ):
2341     cols = []
2342     if with_offsets:
2343         col = "%5d%s%s" % (
2344             pp.offset,
2345             (
2346                 "  " if pp.expl_offset is None else
2347                 ("-%d" % (pp.offset - pp.expl_offset))
2348             ),
2349             LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
2350         )
2351         col = _colourize(col, "red", with_colours, ())
2352         col += _colourize("B", "red", with_colours) if pp.bered else " "
2353         cols.append(col)
2354         col = "[%d,%d,%4d]%s" % (
2355             pp.tlen, pp.llen, pp.vlen,
2356             LENINDEF_PP_CHAR if pp.lenindef else " "
2357         )
2358         col = _colourize(col, "green", with_colours, ())
2359         cols.append(col)
2360     decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2361     if decode_path_len > 0:
2362         cols.append(" ." * decode_path_len)
2363         ent = pp.decode_path[-1]
2364         if isinstance(ent, DecodePathDefBy):
2365             cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
2366             value = str(ent.defined_by)
2367             oid_name = find_oid_name(ent.defined_by.asn1_type_name, oid_maps, value)
2368             if oid_name is None:
2369                 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
2370             else:
2371                 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
2372         else:
2373             cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
2374     if pp.expl is not None:
2375         klass, _, num = pp.expl
2376         col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
2377         cols.append(_colourize(col, "blue", with_colours))
2378     if pp.impl is not None:
2379         klass, _, num = pp.impl
2380         col = "[%s%d]" % (TagClassReprs[klass], num)
2381         cols.append(_colourize(col, "blue", with_colours))
2382     if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
2383         cols.append(_colourize(pp.obj_name, "magenta", with_colours))
2384     if pp.ber_encoded:
2385         cols.append(_colourize("BER", "red", with_colours))
2386     cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
2387     if pp.value is not None:
2388         value = pp.value
2389         cols.append(_colourize(value, "white", with_colours, ("reverse",)))
2390         oid_name = find_oid_name(pp.asn1_type_name, oid_maps, pp.value)
2391         if oid_name is not None:
2392             cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
2393         if pp.asn1_type_name == Integer.asn1_type_name:
2394             cols.append(_colourize(
2395                 "(%s)" % colonize_hex(pp.obj.tohex()), "green", with_colours,
2396             ))
2397     if with_blob:
2398         if pp.blob.__class__ == bytes:
2399             cols.append(hexenc(pp.blob))
2400         elif pp.blob.__class__ == tuple:
2401             cols.append(", ".join(pp.blob))
2402     if pp.optional:
2403         cols.append(_colourize("OPTIONAL", "red", with_colours))
2404     if pp.default:
2405         cols.append(_colourize("DEFAULT", "red", with_colours))
2406     if with_decode_path:
2407         cols.append(_colourize(
2408             "[%s]" % ":".join(str(p) for p in pp.decode_path),
2409             "grey",
2410             with_colours,
2411         ))
2412     return " ".join(cols)
2413
2414
2415 def pp_console_blob(pp, decode_path_len_decrease=0):
2416     cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
2417     decode_path_len = len(pp.decode_path) - decode_path_len_decrease
2418     if decode_path_len > 0:
2419         cols.append(" ." * (decode_path_len + 1))
2420     if pp.blob.__class__ == bytes:
2421         blob = hexenc(pp.blob).upper()
2422         for i in range(0, len(blob), 32):
2423             chunk = blob[i:i + 32]
2424             yield " ".join(cols + [colonize_hex(chunk)])
2425     elif pp.blob.__class__ == tuple:
2426         yield " ".join(cols + [", ".join(pp.blob)])
2427
2428
2429 def pprint(
2430         obj,
2431         oid_maps=(),
2432         big_blobs=False,
2433         with_colours=False,
2434         with_decode_path=False,
2435         decode_path_only=(),
2436         decode_path=(),
2437 ):
2438     """Pretty print object
2439
2440     :param Obj obj: object you want to pretty print
2441     :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
2442                      Its human readable form is printed when OID is met
2443     :param big_blobs: if large binary objects are met (like OctetString
2444                       values), do we need to print them too, on separate
2445                       lines
2446     :param with_colours: colourize output, if ``termcolor`` library
2447                          is available
2448     :param with_decode_path: print decode path
2449     :param decode_path_only: print only that specified decode path
2450     """
2451     def _pprint_pps(pps):
2452         for pp in pps:
2453             if hasattr(pp, "_fields"):
2454                 if (
2455                         decode_path_only != () and
2456                         tuple(
2457                             str(p) for p in pp.decode_path[:len(decode_path_only)]
2458                         ) != decode_path_only
2459                 ):
2460                     continue
2461                 if big_blobs:
2462                     yield pp_console_row(
2463                         pp,
2464                         oid_maps=oid_maps,
2465                         with_offsets=True,
2466                         with_blob=False,
2467                         with_colours=with_colours,
2468                         with_decode_path=with_decode_path,
2469                         decode_path_len_decrease=len(decode_path_only),
2470                     )
2471                     for row in pp_console_blob(
2472                             pp,
2473                             decode_path_len_decrease=len(decode_path_only),
2474                     ):
2475                         yield row
2476                 else:
2477                     yield pp_console_row(
2478                         pp,
2479                         oid_maps=oid_maps,
2480                         with_offsets=True,
2481                         with_blob=True,
2482                         with_colours=with_colours,
2483                         with_decode_path=with_decode_path,
2484                         decode_path_len_decrease=len(decode_path_only),
2485                     )
2486             else:
2487                 for row in _pprint_pps(pp):
2488                     yield row
2489     return "\n".join(_pprint_pps(obj.pps(decode_path)))
2490
2491
2492 ########################################################################
2493 # ASN.1 primitive types
2494 ########################################################################
2495
2496 BooleanState = namedtuple(
2497     "BooleanState",
2498     BasicState._fields + ("value",),
2499     **NAMEDTUPLE_KWARGS
2500 )
2501
2502
2503 class Boolean(Obj):
2504     """``BOOLEAN`` boolean type
2505
2506     >>> b = Boolean(True)
2507     BOOLEAN True
2508     >>> b == Boolean(True)
2509     True
2510     >>> bool(b)
2511     True
2512     """
2513     __slots__ = ()
2514     tag_default = tag_encode(1)
2515     asn1_type_name = "BOOLEAN"
2516
2517     def __init__(
2518             self,
2519             value=None,
2520             impl=None,
2521             expl=None,
2522             default=None,
2523             optional=False,
2524             _decoded=(0, 0, 0),
2525     ):
2526         """
2527         :param value: set the value. Either boolean type, or
2528                       :py:class:`pyderasn.Boolean` object
2529         :param bytes impl: override default tag with ``IMPLICIT`` one
2530         :param bytes expl: override default tag with ``EXPLICIT`` one
2531         :param default: set default value. Type same as in ``value``
2532         :param bool optional: is object ``OPTIONAL`` in sequence
2533         """
2534         super().__init__(impl, expl, default, optional, _decoded)
2535         self._value = None if value is None else self._value_sanitize(value)
2536         if default is not None:
2537             default = self._value_sanitize(default)
2538             self.default = self.__class__(
2539                 value=default,
2540                 impl=self.tag,
2541                 expl=self._expl,
2542             )
2543             if value is None:
2544                 self._value = default
2545
2546     def _value_sanitize(self, value):
2547         if value.__class__ == bool:
2548             return value
2549         if issubclass(value.__class__, Boolean):
2550             return value._value
2551         raise InvalidValueType((self.__class__, bool))
2552
2553     @property
2554     def ready(self):
2555         return self._value is not None
2556
2557     def __getstate__(self):
2558         return BooleanState(
2559             __version__,
2560             self.tag,
2561             self._tag_order,
2562             self._expl,
2563             self.default,
2564             self.optional,
2565             self.offset,
2566             self.llen,
2567             self.vlen,
2568             self.expl_lenindef,
2569             self.lenindef,
2570             self.ber_encoded,
2571             self._value,
2572         )
2573
2574     def __setstate__(self, state):
2575         super().__setstate__(state)
2576         self._value = state.value
2577
2578     def __nonzero__(self):
2579         self._assert_ready()
2580         return self._value
2581
2582     def __bool__(self):
2583         self._assert_ready()
2584         return self._value
2585
2586     def __eq__(self, their):
2587         if their.__class__ == bool:
2588             return self._value == their
2589         if not issubclass(their.__class__, Boolean):
2590             return False
2591         return (
2592             self._value == their._value and
2593             self.tag == their.tag and
2594             self._expl == their._expl
2595         )
2596
2597     def __call__(
2598             self,
2599             value=None,
2600             impl=None,
2601             expl=None,
2602             default=None,
2603             optional=None,
2604     ):
2605         return self.__class__(
2606             value=value,
2607             impl=self.tag if impl is None else impl,
2608             expl=self._expl if expl is None else expl,
2609             default=self.default if default is None else default,
2610             optional=self.optional if optional is None else optional,
2611         )
2612
2613     def _encode(self):
2614         self._assert_ready()
2615         return b"".join((self.tag, LEN1, (b"\xFF" if self._value else b"\x00")))
2616
2617     def _encode1st(self, state):
2618         return len(self.tag) + 2, state
2619
2620     def _encode2nd(self, writer, state_iter):
2621         self._assert_ready()
2622         write_full(writer, self._encode())
2623
2624     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2625         try:
2626             t, _, lv = tag_strip(tlv)
2627         except DecodeError as err:
2628             raise err.__class__(
2629                 msg=err.msg,
2630                 klass=self.__class__,
2631                 decode_path=decode_path,
2632                 offset=offset,
2633             )
2634         if t != self.tag:
2635             raise TagMismatch(
2636                 klass=self.__class__,
2637                 decode_path=decode_path,
2638                 offset=offset,
2639             )
2640         if tag_only:
2641             yield None
2642             return
2643         try:
2644             l, _, v = len_decode(lv)
2645         except DecodeError as err:
2646             raise err.__class__(
2647                 msg=err.msg,
2648                 klass=self.__class__,
2649                 decode_path=decode_path,
2650                 offset=offset,
2651             )
2652         if l != 1:
2653             raise InvalidLength(
2654                 "Boolean's length must be equal to 1",
2655                 klass=self.__class__,
2656                 decode_path=decode_path,
2657                 offset=offset,
2658             )
2659         if l > len(v):
2660             raise NotEnoughData(
2661                 "encoded length is longer than data",
2662                 klass=self.__class__,
2663                 decode_path=decode_path,
2664                 offset=offset,
2665             )
2666         first_octet = v[0]
2667         ber_encoded = False
2668         if first_octet == 0:
2669             value = False
2670         elif first_octet == 0xFF:
2671             value = True
2672         elif ctx.get("bered", False):
2673             value = True
2674             ber_encoded = True
2675         else:
2676             raise DecodeError(
2677                 "unacceptable Boolean value",
2678                 klass=self.__class__,
2679                 decode_path=decode_path,
2680                 offset=offset,
2681             )
2682         obj = self.__class__(
2683             value=value,
2684             impl=self.tag,
2685             expl=self._expl,
2686             default=self.default,
2687             optional=self.optional,
2688             _decoded=(offset, 1, 1),
2689         )
2690         obj.ber_encoded = ber_encoded
2691         yield decode_path, obj, v[1:]
2692
2693     def __repr__(self):
2694         return pp_console_row(next(self.pps()))
2695
2696     def pps(self, decode_path=()):
2697         yield _pp(
2698             obj=self,
2699             asn1_type_name=self.asn1_type_name,
2700             obj_name=self.__class__.__name__,
2701             decode_path=decode_path,
2702             value=str(self._value) if self.ready else None,
2703             optional=self.optional,
2704             default=self == self.default,
2705             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2706             expl=None if self._expl is None else tag_decode(self._expl),
2707             offset=self.offset,
2708             tlen=self.tlen,
2709             llen=self.llen,
2710             vlen=self.vlen,
2711             expl_offset=self.expl_offset if self.expled else None,
2712             expl_tlen=self.expl_tlen if self.expled else None,
2713             expl_llen=self.expl_llen if self.expled else None,
2714             expl_vlen=self.expl_vlen if self.expled else None,
2715             expl_lenindef=self.expl_lenindef,
2716             ber_encoded=self.ber_encoded,
2717             bered=self.bered,
2718         )
2719         for pp in self.pps_lenindef(decode_path):
2720             yield pp
2721
2722
2723 IntegerState = namedtuple(
2724     "IntegerState",
2725     BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
2726     **NAMEDTUPLE_KWARGS
2727 )
2728
2729
2730 class Integer(Obj):
2731     """``INTEGER`` integer type
2732
2733     >>> b = Integer(-123)
2734     INTEGER -123
2735     >>> b == Integer(-123)
2736     True
2737     >>> int(b)
2738     -123
2739
2740     >>> Integer(2, bounds=(1, 3))
2741     INTEGER 2
2742     >>> Integer(5, bounds=(1, 3))
2743     Traceback (most recent call last):
2744     pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
2745
2746     ::
2747
2748         class Version(Integer):
2749             schema = (
2750                 ("v1", 0),
2751                 ("v2", 1),
2752                 ("v3", 2),
2753             )
2754
2755     >>> v = Version("v1")
2756     Version INTEGER v1
2757     >>> int(v)
2758     0
2759     >>> v.named
2760     'v1'
2761     >>> v.specs
2762     {'v3': 2, 'v1': 0, 'v2': 1}
2763     """
2764     __slots__ = ("specs", "_bound_min", "_bound_max")
2765     tag_default = tag_encode(2)
2766     asn1_type_name = "INTEGER"
2767
2768     def __init__(
2769             self,
2770             value=None,
2771             bounds=None,
2772             impl=None,
2773             expl=None,
2774             default=None,
2775             optional=False,
2776             _specs=None,
2777             _decoded=(0, 0, 0),
2778     ):
2779         """
2780         :param value: set the value. Either integer type, named value
2781                       (if ``schema`` is specified in the class), or
2782                       :py:class:`pyderasn.Integer` object
2783         :param bounds: set ``(MIN, MAX)`` value constraint.
2784                        (-inf, +inf) by default
2785         :param bytes impl: override default tag with ``IMPLICIT`` one
2786         :param bytes expl: override default tag with ``EXPLICIT`` one
2787         :param default: set default value. Type same as in ``value``
2788         :param bool optional: is object ``OPTIONAL`` in sequence
2789         """
2790         super().__init__(impl, expl, default, optional, _decoded)
2791         self._value = value
2792         specs = getattr(self, "schema", {}) if _specs is None else _specs
2793         self.specs = specs if specs.__class__ == dict else dict(specs)
2794         self._bound_min, self._bound_max = getattr(
2795             self,
2796             "bounds",
2797             (float("-inf"), float("+inf")),
2798         ) if bounds is None else bounds
2799         if value is not None:
2800             self._value = self._value_sanitize(value)
2801         if default is not None:
2802             default = self._value_sanitize(default)
2803             self.default = self.__class__(
2804                 value=default,
2805                 impl=self.tag,
2806                 expl=self._expl,
2807                 _specs=self.specs,
2808             )
2809             if self._value is None:
2810                 self._value = default
2811
2812     def _value_sanitize(self, value):
2813         if isinstance(value, int):
2814             pass
2815         elif issubclass(value.__class__, Integer):
2816             value = value._value
2817         elif value.__class__ == str:
2818             value = self.specs.get(value)
2819             if value is None:
2820                 raise ObjUnknown("integer value: %s" % value)
2821         else:
2822             raise InvalidValueType((self.__class__, int, str))
2823         if not self._bound_min <= value <= self._bound_max:
2824             raise BoundsError(self._bound_min, value, self._bound_max)
2825         return value
2826
2827     @property
2828     def ready(self):
2829         return self._value is not None
2830
2831     def __getstate__(self):
2832         return IntegerState(
2833             __version__,
2834             self.tag,
2835             self._tag_order,
2836             self._expl,
2837             self.default,
2838             self.optional,
2839             self.offset,
2840             self.llen,
2841             self.vlen,
2842             self.expl_lenindef,
2843             self.lenindef,
2844             self.ber_encoded,
2845             self.specs,
2846             self._value,
2847             self._bound_min,
2848             self._bound_max,
2849         )
2850
2851     def __setstate__(self, state):
2852         super().__setstate__(state)
2853         self.specs = state.specs
2854         self._value = state.value
2855         self._bound_min = state.bound_min
2856         self._bound_max = state.bound_max
2857
2858     def __int__(self):
2859         self._assert_ready()
2860         return int(self._value)
2861
2862     def tohex(self):
2863         """Hexadecimal representation
2864
2865         Use :py:func:`pyderasn.colonize_hex` for colonizing it.
2866         """
2867         hex_repr = hex(int(self))[2:].upper()
2868         if len(hex_repr) % 2 != 0:
2869             hex_repr = "0" + hex_repr
2870         return hex_repr
2871
2872     def __hash__(self):
2873         self._assert_ready()
2874         return hash(b"".join((
2875             self.tag,
2876             bytes(self._expl or b""),
2877             str(self._value).encode("ascii"),
2878         )))
2879
2880     def __eq__(self, their):
2881         if isinstance(their, int):
2882             return self._value == their
2883         if not issubclass(their.__class__, Integer):
2884             return False
2885         return (
2886             self._value == their._value and
2887             self.tag == their.tag and
2888             self._expl == their._expl
2889         )
2890
2891     def __lt__(self, their):
2892         return self._value < their._value
2893
2894     @property
2895     def named(self):
2896         """Return named representation (if exists) of the value
2897         """
2898         for name, value in self.specs.items():
2899             if value == self._value:
2900                 return name
2901         return None
2902
2903     def __call__(
2904             self,
2905             value=None,
2906             bounds=None,
2907             impl=None,
2908             expl=None,
2909             default=None,
2910             optional=None,
2911     ):
2912         return self.__class__(
2913             value=value,
2914             bounds=(
2915                 (self._bound_min, self._bound_max)
2916                 if bounds is None else bounds
2917             ),
2918             impl=self.tag if impl is None else impl,
2919             expl=self._expl if expl is None else expl,
2920             default=self.default if default is None else default,
2921             optional=self.optional if optional is None else optional,
2922             _specs=self.specs,
2923         )
2924
2925     def _encode_payload(self):
2926         self._assert_ready()
2927         value = self._value
2928         bytes_len = ceil(value.bit_length() / 8) or 1
2929         while True:
2930             try:
2931                 octets = value.to_bytes(bytes_len, byteorder="big", signed=True)
2932             except OverflowError:
2933                 bytes_len += 1
2934             else:
2935                 break
2936         return octets
2937
2938     def _encode(self):
2939         octets = self._encode_payload()
2940         return b"".join((self.tag, len_encode(len(octets)), octets))
2941
2942     def _encode1st(self, state):
2943         l = len(self._encode_payload())
2944         return len(self.tag) + len_size(l) + l, state
2945
2946     def _encode2nd(self, writer, state_iter):
2947         write_full(writer, self._encode())
2948
2949     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
2950         try:
2951             t, _, lv = tag_strip(tlv)
2952         except DecodeError as err:
2953             raise err.__class__(
2954                 msg=err.msg,
2955                 klass=self.__class__,
2956                 decode_path=decode_path,
2957                 offset=offset,
2958             )
2959         if t != self.tag:
2960             raise TagMismatch(
2961                 klass=self.__class__,
2962                 decode_path=decode_path,
2963                 offset=offset,
2964             )
2965         if tag_only:
2966             yield None
2967             return
2968         try:
2969             l, llen, v = len_decode(lv)
2970         except DecodeError as err:
2971             raise err.__class__(
2972                 msg=err.msg,
2973                 klass=self.__class__,
2974                 decode_path=decode_path,
2975                 offset=offset,
2976             )
2977         if l > len(v):
2978             raise NotEnoughData(
2979                 "encoded length is longer than data",
2980                 klass=self.__class__,
2981                 decode_path=decode_path,
2982                 offset=offset,
2983             )
2984         if l == 0:
2985             raise NotEnoughData(
2986                 "zero length",
2987                 klass=self.__class__,
2988                 decode_path=decode_path,
2989                 offset=offset,
2990             )
2991         v, tail = v[:l], v[l:]
2992         first_octet = v[0]
2993         if l > 1:
2994             second_octet = v[1]
2995             if (
2996                     ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2997                     ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2998             ):
2999                 raise DecodeError(
3000                     "non normalized integer",
3001                     klass=self.__class__,
3002                     decode_path=decode_path,
3003                     offset=offset,
3004                 )
3005         value = int.from_bytes(v, byteorder="big", signed=True)
3006         try:
3007             obj = self.__class__(
3008                 value=value,
3009                 bounds=(self._bound_min, self._bound_max),
3010                 impl=self.tag,
3011                 expl=self._expl,
3012                 default=self.default,
3013                 optional=self.optional,
3014                 _specs=self.specs,
3015                 _decoded=(offset, llen, l),
3016             )
3017         except BoundsError as err:
3018             raise DecodeError(
3019                 msg=str(err),
3020                 klass=self.__class__,
3021                 decode_path=decode_path,
3022                 offset=offset,
3023             )
3024         yield decode_path, obj, tail
3025
3026     def __repr__(self):
3027         return pp_console_row(next(self.pps()))
3028
3029     def pps(self, decode_path=()):
3030         yield _pp(
3031             obj=self,
3032             asn1_type_name=self.asn1_type_name,
3033             obj_name=self.__class__.__name__,
3034             decode_path=decode_path,
3035             value=(self.named or str(self._value)) if self.ready else None,
3036             optional=self.optional,
3037             default=self == self.default,
3038             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3039             expl=None if self._expl is None else tag_decode(self._expl),
3040             offset=self.offset,
3041             tlen=self.tlen,
3042             llen=self.llen,
3043             vlen=self.vlen,
3044             expl_offset=self.expl_offset if self.expled else None,
3045             expl_tlen=self.expl_tlen if self.expled else None,
3046             expl_llen=self.expl_llen if self.expled else None,
3047             expl_vlen=self.expl_vlen if self.expled else None,
3048             expl_lenindef=self.expl_lenindef,
3049             bered=self.bered,
3050         )
3051         for pp in self.pps_lenindef(decode_path):
3052             yield pp
3053
3054
3055 BitStringState = namedtuple(
3056     "BitStringState",
3057     BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
3058     **NAMEDTUPLE_KWARGS
3059 )
3060
3061
3062 class BitString(Obj):
3063     """``BIT STRING`` bit string type
3064
3065     >>> BitString(b"hello world")
3066     BIT STRING 88 bits 68656c6c6f20776f726c64
3067     >>> bytes(b)
3068     b'hello world'
3069     >>> b == b"hello world"
3070     True
3071     >>> b.bit_len
3072     88
3073
3074     >>> BitString("'0A3B5F291CD'H")
3075     BIT STRING 44 bits 0a3b5f291cd0
3076     >>> b = BitString("'010110000000'B")
3077     BIT STRING 12 bits 5800
3078     >>> b.bit_len
3079     12
3080     >>> b[0], b[1], b[2], b[3]
3081     (False, True, False, True)
3082     >>> b[1000]
3083     False
3084     >>> [v for v in b]
3085     [False, True, False, True, True, False, False, False, False, False, False, False]
3086
3087     ::
3088
3089         class KeyUsage(BitString):
3090             schema = (
3091                 ("digitalSignature", 0),
3092                 ("nonRepudiation", 1),
3093                 ("keyEncipherment", 2),
3094             )
3095
3096     >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
3097     KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
3098     >>> b.named
3099     ['nonRepudiation', 'keyEncipherment']
3100     >>> b.specs
3101     {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
3102
3103     .. note::
3104
3105        Pay attention that BIT STRING can be encoded both in primitive
3106        and constructed forms. Decoder always checks constructed form tag
3107        additionally to specified primitive one. If BER decoding is
3108        :ref:`not enabled <bered_ctx>`, then decoder will fail, because
3109        of DER restrictions.
3110     """
3111     __slots__ = ("tag_constructed", "specs", "defined")
3112     tag_default = tag_encode(3)
3113     asn1_type_name = "BIT STRING"
3114
3115     def __init__(
3116             self,
3117             value=None,
3118             impl=None,
3119             expl=None,
3120             default=None,
3121             optional=False,
3122             _specs=None,
3123             _decoded=(0, 0, 0),
3124     ):
3125         """
3126         :param value: set the value. Either binary type, tuple of named
3127                       values (if ``schema`` is specified in the class),
3128                       string in ``'XXX...'B`` form, or
3129                       :py:class:`pyderasn.BitString` object
3130         :param bytes impl: override default tag with ``IMPLICIT`` one
3131         :param bytes expl: override default tag with ``EXPLICIT`` one
3132         :param default: set default value. Type same as in ``value``
3133         :param bool optional: is object ``OPTIONAL`` in sequence
3134         """
3135         super().__init__(impl, expl, default, optional, _decoded)
3136         specs = getattr(self, "schema", {}) if _specs is None else _specs
3137         self.specs = specs if specs.__class__ == dict else dict(specs)
3138         self._value = None if value is None else self._value_sanitize(value)
3139         if default is not None:
3140             default = self._value_sanitize(default)
3141             self.default = self.__class__(
3142                 value=default,
3143                 impl=self.tag,
3144                 expl=self._expl,
3145             )
3146             if value is None:
3147                 self._value = default
3148         self.defined = None
3149         tag_klass, _, tag_num = tag_decode(self.tag)
3150         self.tag_constructed = tag_encode(
3151             klass=tag_klass,
3152             form=TagFormConstructed,
3153             num=tag_num,
3154         )
3155
3156     def _bits2octets(self, bits):
3157         if len(self.specs) > 0:
3158             bits = bits.rstrip("0")
3159         bit_len = len(bits)
3160         bits += "0" * ((8 - (bit_len % 8)) % 8)
3161         octets = bytearray(len(bits) // 8)
3162         for i in range(len(octets)):
3163             octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
3164         return bit_len, bytes(octets)
3165
3166     def _value_sanitize(self, value):
3167         if isinstance(value, (str, bytes)):
3168             if (
3169                     isinstance(value, str) and
3170                     value.startswith("'")
3171             ):
3172                 if value.endswith("'B"):
3173                     value = value[1:-2]
3174                     if not frozenset(value) <= SET01:
3175                         raise ValueError("B's coding contains unacceptable chars")
3176                     return self._bits2octets(value)
3177                 if value.endswith("'H"):
3178                     value = value[1:-2]
3179                     return (
3180                         len(value) * 4,
3181                         hexdec(value + ("" if len(value) % 2 == 0 else "0")),
3182                     )
3183             if value.__class__ == bytes:
3184                 return (len(value) * 8, value)
3185             raise InvalidValueType((self.__class__, str, bytes))
3186         if value.__class__ == tuple:
3187             if (
3188                     len(value) == 2 and
3189                     isinstance(value[0], int) and
3190                     value[1].__class__ == bytes
3191             ):
3192                 return value
3193             bits = []
3194             for name in value:
3195                 bit = self.specs.get(name)
3196                 if bit is None:
3197                     raise ObjUnknown("BitString value: %s" % name)
3198                 bits.append(bit)
3199             if len(bits) == 0:
3200                 return self._bits2octets("")
3201             bits = frozenset(bits)
3202             return self._bits2octets("".join(
3203                 ("1" if bit in bits else "0")
3204                 for bit in range(max(bits) + 1)
3205             ))
3206         if issubclass(value.__class__, BitString):
3207             return value._value
3208         raise InvalidValueType((self.__class__, bytes, str))
3209
3210     @property
3211     def ready(self):
3212         return self._value is not None
3213
3214     def __getstate__(self):
3215         return BitStringState(
3216             __version__,
3217             self.tag,
3218             self._tag_order,
3219             self._expl,
3220             self.default,
3221             self.optional,
3222             self.offset,
3223             self.llen,
3224             self.vlen,
3225             self.expl_lenindef,
3226             self.lenindef,
3227             self.ber_encoded,
3228             self.specs,
3229             self._value,
3230             self.tag_constructed,
3231             self.defined,
3232         )
3233
3234     def __setstate__(self, state):
3235         super().__setstate__(state)
3236         self.specs = state.specs
3237         self._value = state.value
3238         self.tag_constructed = state.tag_constructed
3239         self.defined = state.defined
3240
3241     def __iter__(self):
3242         self._assert_ready()
3243         for i in range(self._value[0]):
3244             yield self[i]
3245
3246     @property
3247     def bit_len(self):
3248         """Returns number of bits in the string
3249         """
3250         self._assert_ready()
3251         return self._value[0]
3252
3253     def __bytes__(self):
3254         self._assert_ready()
3255         return self._value[1]
3256
3257     def __eq__(self, their):
3258         if their.__class__ == bytes:
3259             return self._value[1] == their
3260         if not issubclass(their.__class__, BitString):
3261             return False
3262         return (
3263             self._value == their._value and
3264             self.tag == their.tag and
3265             self._expl == their._expl
3266         )
3267
3268     @property
3269     def named(self):
3270         """Named representation (if exists) of the bits
3271
3272         :returns: [str(name), ...]
3273         """
3274         return [name for name, bit in self.specs.items() if self[bit]]
3275
3276     def __call__(
3277             self,
3278             value=None,
3279             impl=None,
3280             expl=None,
3281             default=None,
3282             optional=None,
3283     ):
3284         return self.__class__(
3285             value=value,
3286             impl=self.tag if impl is None else impl,
3287             expl=self._expl if expl is None else expl,
3288             default=self.default if default is None else default,
3289             optional=self.optional if optional is None else optional,
3290             _specs=self.specs,
3291         )
3292
3293     def __getitem__(self, key):
3294         if key.__class__ == int:
3295             bit_len, octets = self._value
3296             if key >= bit_len:
3297                 return False
3298             return memoryview(octets)[key // 8] >> (7 - (key % 8)) & 1 == 1
3299         if isinstance(key, str):
3300             value = self.specs.get(key)
3301             if value is None:
3302                 raise ObjUnknown("BitString value: %s" % key)
3303             return self[value]
3304         raise InvalidValueType((int, str))
3305
3306     def _encode(self):
3307         self._assert_ready()
3308         bit_len, octets = self._value
3309         return b"".join((
3310             self.tag,
3311             len_encode(len(octets) + 1),
3312             int2byte((8 - bit_len % 8) % 8),
3313             octets,
3314         ))
3315
3316     def _encode1st(self, state):
3317         self._assert_ready()
3318         _, octets = self._value
3319         l = len(octets) + 1
3320         return len(self.tag) + len_size(l) + l, state
3321
3322     def _encode2nd(self, writer, state_iter):
3323         bit_len, octets = self._value
3324         write_full(writer, b"".join((
3325             self.tag,
3326             len_encode(len(octets) + 1),
3327             int2byte((8 - bit_len % 8) % 8),
3328         )))
3329         write_full(writer, octets)
3330
3331     def _encode_cer(self, writer):
3332         bit_len, octets = self._value
3333         if len(octets) + 1 <= 1000:
3334             write_full(writer, self._encode())
3335             return
3336         write_full(writer, self.tag_constructed)
3337         write_full(writer, LENINDEF)
3338         for offset in range(0, (len(octets) // 999) * 999, 999):
3339             write_full(writer, b"".join((
3340                 BitString.tag_default,
3341                 LEN1K,
3342                 b"\x00",
3343                 octets[offset:offset + 999],
3344             )))
3345         tail = octets[offset + 999:]
3346         if len(tail) > 0:
3347             tail = int2byte((8 - bit_len % 8) % 8) + tail
3348             write_full(writer, b"".join((
3349                 BitString.tag_default,
3350                 len_encode(len(tail)),
3351                 tail,
3352             )))
3353         write_full(writer, EOC)
3354
3355     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3356         try:
3357             t, tlen, lv = tag_strip(tlv)
3358         except DecodeError as err:
3359             raise err.__class__(
3360                 msg=err.msg,
3361                 klass=self.__class__,
3362                 decode_path=decode_path,
3363                 offset=offset,
3364             )
3365         if t == self.tag:
3366             if tag_only:  # pragma: no cover
3367                 yield None
3368                 return
3369             try:
3370                 l, llen, v = len_decode(lv)
3371             except DecodeError as err:
3372                 raise err.__class__(
3373                     msg=err.msg,
3374                     klass=self.__class__,
3375                     decode_path=decode_path,
3376                     offset=offset,
3377                 )
3378             if l > len(v):
3379                 raise NotEnoughData(
3380                     "encoded length is longer than data",
3381                     klass=self.__class__,
3382                     decode_path=decode_path,
3383                     offset=offset,
3384                 )
3385             if l == 0:
3386                 raise NotEnoughData(
3387                     "zero length",
3388                     klass=self.__class__,
3389                     decode_path=decode_path,
3390                     offset=offset,
3391                 )
3392             pad_size = v[0]
3393             if l == 1 and pad_size != 0:
3394                 raise DecodeError(
3395                     "invalid empty value",
3396                     klass=self.__class__,
3397                     decode_path=decode_path,
3398                     offset=offset,
3399                 )
3400             if pad_size > 7:
3401                 raise DecodeError(
3402                     "too big pad",
3403                     klass=self.__class__,
3404                     decode_path=decode_path,
3405                     offset=offset,
3406                 )
3407             if v[l - 1] & ((1 << pad_size) - 1) != 0:
3408                 raise DecodeError(
3409                     "invalid pad",
3410                     klass=self.__class__,
3411                     decode_path=decode_path,
3412                     offset=offset,
3413                 )
3414             v, tail = v[:l], v[l:]
3415             bit_len = (len(v) - 1) * 8 - pad_size
3416             obj = self.__class__(
3417                 value=None if evgen_mode else (bit_len, v[1:].tobytes()),
3418                 impl=self.tag,
3419                 expl=self._expl,
3420                 default=self.default,
3421                 optional=self.optional,
3422                 _specs=self.specs,
3423                 _decoded=(offset, llen, l),
3424             )
3425             if evgen_mode:
3426                 obj._value = (bit_len, None)
3427             yield decode_path, obj, tail
3428             return
3429         if t != self.tag_constructed:
3430             raise TagMismatch(
3431                 klass=self.__class__,
3432                 decode_path=decode_path,
3433                 offset=offset,
3434             )
3435         if not ctx.get("bered", False):
3436             raise DecodeError(
3437                 "unallowed BER constructed encoding",
3438                 klass=self.__class__,
3439                 decode_path=decode_path,
3440                 offset=offset,
3441             )
3442         if tag_only:  # pragma: no cover
3443             yield None
3444             return
3445         lenindef = False
3446         try:
3447             l, llen, v = len_decode(lv)
3448         except LenIndefForm:
3449             llen, l, v = 1, 0, lv[1:]
3450             lenindef = True
3451         except DecodeError as err:
3452             raise err.__class__(
3453                 msg=err.msg,
3454                 klass=self.__class__,
3455                 decode_path=decode_path,
3456                 offset=offset,
3457             )
3458         if l > len(v):
3459             raise NotEnoughData(
3460                 "encoded length is longer than data",
3461                 klass=self.__class__,
3462                 decode_path=decode_path,
3463                 offset=offset,
3464             )
3465         if not lenindef and l == 0:
3466             raise NotEnoughData(
3467                 "zero length",
3468                 klass=self.__class__,
3469                 decode_path=decode_path,
3470                 offset=offset,
3471             )
3472         chunks = []
3473         sub_offset = offset + tlen + llen
3474         vlen = 0
3475         while True:
3476             if lenindef:
3477                 if v[:EOC_LEN].tobytes() == EOC:
3478                     break
3479             else:
3480                 if vlen == l:
3481                     break
3482                 if vlen > l:
3483                     raise DecodeError(
3484                         "chunk out of bounds",
3485                         klass=self.__class__,
3486                         decode_path=decode_path + (str(len(chunks) - 1),),
3487                         offset=chunks[-1].offset,
3488                     )
3489             sub_decode_path = decode_path + (str(len(chunks)),)
3490             try:
3491                 if evgen_mode:
3492                     for _decode_path, chunk, v_tail in BitString().decode_evgen(
3493                             v,
3494                             offset=sub_offset,
3495                             decode_path=sub_decode_path,
3496                             leavemm=True,
3497                             ctx=ctx,
3498                             _ctx_immutable=False,
3499                     ):
3500                         yield _decode_path, chunk, v_tail
3501                 else:
3502                     _, chunk, v_tail = next(BitString().decode_evgen(
3503                         v,
3504                         offset=sub_offset,
3505                         decode_path=sub_decode_path,
3506                         leavemm=True,
3507                         ctx=ctx,
3508                         _ctx_immutable=False,
3509                         _evgen_mode=False,
3510                     ))
3511             except TagMismatch:
3512                 raise DecodeError(
3513                     "expected BitString encoded chunk",
3514                     klass=self.__class__,
3515                     decode_path=sub_decode_path,
3516                     offset=sub_offset,
3517                 )
3518             chunks.append(chunk)
3519             sub_offset += chunk.tlvlen
3520             vlen += chunk.tlvlen
3521             v = v_tail
3522         if len(chunks) == 0:
3523             raise DecodeError(
3524                 "no chunks",
3525                 klass=self.__class__,
3526                 decode_path=decode_path,
3527                 offset=offset,
3528             )
3529         values = []
3530         bit_len = 0
3531         for chunk_i, chunk in enumerate(chunks[:-1]):
3532             if chunk.bit_len % 8 != 0:
3533                 raise DecodeError(
3534                     "BitString chunk is not multiple of 8 bits",
3535                     klass=self.__class__,
3536                     decode_path=decode_path + (str(chunk_i),),
3537                     offset=chunk.offset,
3538                 )
3539             if not evgen_mode:
3540                 values.append(bytes(chunk))
3541             bit_len += chunk.bit_len
3542         chunk_last = chunks[-1]
3543         if not evgen_mode:
3544             values.append(bytes(chunk_last))
3545         bit_len += chunk_last.bit_len
3546         obj = self.__class__(
3547             value=None if evgen_mode else (bit_len, b"".join(values)),
3548             impl=self.tag,
3549             expl=self._expl,
3550             default=self.default,
3551             optional=self.optional,
3552             _specs=self.specs,
3553             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
3554         )
3555         if evgen_mode:
3556             obj._value = (bit_len, None)
3557         obj.lenindef = lenindef
3558         obj.ber_encoded = True
3559         yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
3560
3561     def __repr__(self):
3562         return pp_console_row(next(self.pps()))
3563
3564     def pps(self, decode_path=()):
3565         value = None
3566         blob = None
3567         if self.ready:
3568             bit_len, blob = self._value
3569             value = "%d bits" % bit_len
3570             if len(self.specs) > 0 and blob is not None:
3571                 blob = tuple(self.named)
3572         yield _pp(
3573             obj=self,
3574             asn1_type_name=self.asn1_type_name,
3575             obj_name=self.__class__.__name__,
3576             decode_path=decode_path,
3577             value=value,
3578             blob=blob,
3579             optional=self.optional,
3580             default=self == self.default,
3581             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3582             expl=None if self._expl is None else tag_decode(self._expl),
3583             offset=self.offset,
3584             tlen=self.tlen,
3585             llen=self.llen,
3586             vlen=self.vlen,
3587             expl_offset=self.expl_offset if self.expled else None,
3588             expl_tlen=self.expl_tlen if self.expled else None,
3589             expl_llen=self.expl_llen if self.expled else None,
3590             expl_vlen=self.expl_vlen if self.expled else None,
3591             expl_lenindef=self.expl_lenindef,
3592             lenindef=self.lenindef,
3593             ber_encoded=self.ber_encoded,
3594             bered=self.bered,
3595         )
3596         defined_by, defined = self.defined or (None, None)
3597         if defined_by is not None:
3598             yield defined.pps(
3599                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3600             )
3601         for pp in self.pps_lenindef(decode_path):
3602             yield pp
3603
3604
3605 OctetStringState = namedtuple(
3606     "OctetStringState",
3607     BasicState._fields + (
3608         "value",
3609         "bound_min",
3610         "bound_max",
3611         "tag_constructed",
3612         "defined",
3613     ),
3614     **NAMEDTUPLE_KWARGS
3615 )
3616
3617
3618 class OctetString(Obj):
3619     """``OCTET STRING`` binary string type
3620
3621     >>> s = OctetString(b"hello world")
3622     OCTET STRING 11 bytes 68656c6c6f20776f726c64
3623     >>> s == OctetString(b"hello world")
3624     True
3625     >>> bytes(s)
3626     b'hello world'
3627
3628     >>> OctetString(b"hello", bounds=(4, 4))
3629     Traceback (most recent call last):
3630     pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
3631     >>> OctetString(b"hell", bounds=(4, 4))
3632     OCTET STRING 4 bytes 68656c6c
3633
3634     Memoryviews can be used as a values. If memoryview is made on
3635     mmap-ed file, then it does not take storage inside OctetString
3636     itself. In CER encoding mode it will be streamed to the specified
3637     writer, copying 1 KB chunks.
3638     """
3639     __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
3640     tag_default = tag_encode(4)
3641     asn1_type_name = "OCTET STRING"
3642     evgen_mode_skip_value = True
3643     memoryview_safe = True
3644
3645     def __init__(
3646             self,
3647             value=None,
3648             bounds=None,
3649             impl=None,
3650             expl=None,
3651             default=None,
3652             optional=False,
3653             _decoded=(0, 0, 0),
3654             ctx=None,
3655     ):
3656         """
3657         :param value: set the value. Either binary type, or
3658                       :py:class:`pyderasn.OctetString` object
3659         :param bounds: set ``(MIN, MAX)`` value size constraint.
3660                        (-inf, +inf) by default
3661         :param bytes impl: override default tag with ``IMPLICIT`` one
3662         :param bytes expl: override default tag with ``EXPLICIT`` one
3663         :param default: set default value. Type same as in ``value``
3664         :param bool optional: is object ``OPTIONAL`` in sequence
3665         """
3666         super().__init__(impl, expl, default, optional, _decoded)
3667         self._value = value
3668         self._bound_min, self._bound_max = getattr(
3669             self,
3670             "bounds",
3671             (0, float("+inf")),
3672         ) if bounds is None else bounds
3673         if value is not None:
3674             self._value = self._value_sanitize(value)
3675         if default is not None:
3676             default = self._value_sanitize(default)
3677             self.default = self.__class__(
3678                 value=default,
3679                 impl=self.tag,
3680                 expl=self._expl,
3681             )
3682             if self._value is None:
3683                 self._value = default
3684         self.defined = None
3685         tag_klass, _, tag_num = tag_decode(self.tag)
3686         self.tag_constructed = tag_encode(
3687             klass=tag_klass,
3688             form=TagFormConstructed,
3689             num=tag_num,
3690         )
3691
3692     def _value_sanitize(self, value):
3693         if value.__class__ == bytes or value.__class__ == memoryview:
3694             pass
3695         elif issubclass(value.__class__, OctetString):
3696             value = value._value
3697         else:
3698             raise InvalidValueType((self.__class__, bytes, memoryview))
3699         if not self._bound_min <= len(value) <= self._bound_max:
3700             raise BoundsError(self._bound_min, len(value), self._bound_max)
3701         return value
3702
3703     @property
3704     def ready(self):
3705         return self._value is not None
3706
3707     def __getstate__(self):
3708         return OctetStringState(
3709             __version__,
3710             self.tag,
3711             self._tag_order,
3712             self._expl,
3713             self.default,
3714             self.optional,
3715             self.offset,
3716             self.llen,
3717             self.vlen,
3718             self.expl_lenindef,
3719             self.lenindef,
3720             self.ber_encoded,
3721             self._value,
3722             self._bound_min,
3723             self._bound_max,
3724             self.tag_constructed,
3725             self.defined,
3726         )
3727
3728     def __setstate__(self, state):
3729         super().__setstate__(state)
3730         self._value = state.value
3731         self._bound_min = state.bound_min
3732         self._bound_max = state.bound_max
3733         self.tag_constructed = state.tag_constructed
3734         self.defined = state.defined
3735
3736     def __bytes__(self):
3737         self._assert_ready()
3738         return bytes(self._value)
3739
3740     def memoryview(self):
3741         self._assert_ready()
3742         return memoryview(self._value)
3743
3744     def __eq__(self, their):
3745         if their.__class__ == bytes:
3746             return self._value == their
3747         if not issubclass(their.__class__, OctetString):
3748             return False
3749         return (
3750             self._value == their._value and
3751             self.tag == their.tag and
3752             self._expl == their._expl
3753         )
3754
3755     def __lt__(self, their):
3756         return self._value < their._value
3757
3758     def __call__(
3759             self,
3760             value=None,
3761             bounds=None,
3762             impl=None,
3763             expl=None,
3764             default=None,
3765             optional=None,
3766     ):
3767         return self.__class__(
3768             value=value,
3769             bounds=(
3770                 (self._bound_min, self._bound_max)
3771                 if bounds is None else bounds
3772             ),
3773             impl=self.tag if impl is None else impl,
3774             expl=self._expl if expl is None else expl,
3775             default=self.default if default is None else default,
3776             optional=self.optional if optional is None else optional,
3777         )
3778
3779     def _encode(self):
3780         self._assert_ready()
3781         return b"".join((
3782             self.tag,
3783             len_encode(len(self._value)),
3784             self._value,
3785         ))
3786
3787     def _encode1st(self, state):
3788         self._assert_ready()
3789         l = len(self._value)
3790         return len(self.tag) + len_size(l) + l, state
3791
3792     def _encode2nd(self, writer, state_iter):
3793         value = self._value
3794         write_full(writer, self.tag + len_encode(len(value)))
3795         write_full(writer, value)
3796
3797     def _encode_cer(self, writer):
3798         octets = self._value
3799         if len(octets) <= 1000:
3800             write_full(writer, self._encode())
3801             return
3802         write_full(writer, self.tag_constructed)
3803         write_full(writer, LENINDEF)
3804         for offset in range(0, (len(octets) // 1000) * 1000, 1000):
3805             write_full(writer, b"".join((
3806                 OctetString.tag_default,
3807                 LEN1K,
3808                 octets[offset:offset + 1000],
3809             )))
3810         tail = octets[offset + 1000:]
3811         if len(tail) > 0:
3812             write_full(writer, b"".join((
3813                 OctetString.tag_default,
3814                 len_encode(len(tail)),
3815                 tail,
3816             )))
3817         write_full(writer, EOC)
3818
3819     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
3820         try:
3821             t, tlen, lv = tag_strip(tlv)
3822         except DecodeError as err:
3823             raise err.__class__(
3824                 msg=err.msg,
3825                 klass=self.__class__,
3826                 decode_path=decode_path,
3827                 offset=offset,
3828             )
3829         if t == self.tag:
3830             if tag_only:
3831                 yield None
3832                 return
3833             try:
3834                 l, llen, v = len_decode(lv)
3835             except DecodeError as err:
3836                 raise err.__class__(
3837                     msg=err.msg,
3838                     klass=self.__class__,
3839                     decode_path=decode_path,
3840                     offset=offset,
3841                 )
3842             if l > len(v):
3843                 raise NotEnoughData(
3844                     "encoded length is longer than data",
3845                     klass=self.__class__,
3846                     decode_path=decode_path,
3847                     offset=offset,
3848                 )
3849             v, tail = v[:l], v[l:]
3850             if evgen_mode and not self._bound_min <= len(v) <= self._bound_max:
3851                 raise DecodeError(
3852                     msg=str(BoundsError(self._bound_min, len(v), self._bound_max)),
3853                     klass=self.__class__,
3854                     decode_path=decode_path,
3855                     offset=offset,
3856                 )
3857             if evgen_mode and self.evgen_mode_skip_value:
3858                 value = None
3859             elif self.memoryview_safe and ctx.get("keep_memoryview", False):
3860                 value = v
3861             else:
3862                 value = v.tobytes()
3863             try:
3864                 obj = self.__class__(
3865                     value=value,
3866                     bounds=(self._bound_min, self._bound_max),
3867                     impl=self.tag,
3868                     expl=self._expl,
3869                     default=self.default,
3870                     optional=self.optional,
3871                     _decoded=(offset, llen, l),
3872                     ctx=ctx,
3873                 )
3874             except DecodeError as err:
3875                 raise DecodeError(
3876                     msg=err.msg,
3877                     klass=self.__class__,
3878                     decode_path=decode_path,
3879                     offset=offset,
3880                 )
3881             except BoundsError as err:
3882                 raise DecodeError(
3883                     msg=str(err),
3884                     klass=self.__class__,
3885                     decode_path=decode_path,
3886                     offset=offset,
3887                 )
3888             yield decode_path, obj, tail
3889             return
3890         if t != self.tag_constructed:
3891             raise TagMismatch(
3892                 klass=self.__class__,
3893                 decode_path=decode_path,
3894                 offset=offset,
3895             )
3896         if not ctx.get("bered", False):
3897             raise DecodeError(
3898                 "unallowed BER constructed encoding",
3899                 klass=self.__class__,
3900                 decode_path=decode_path,
3901                 offset=offset,
3902             )
3903         if tag_only:
3904             yield None
3905             return
3906         lenindef = False
3907         try:
3908             l, llen, v = len_decode(lv)
3909         except LenIndefForm:
3910             llen, l, v = 1, 0, lv[1:]
3911             lenindef = True
3912         except DecodeError as err:
3913             raise err.__class__(
3914                 msg=err.msg,
3915                 klass=self.__class__,
3916                 decode_path=decode_path,
3917                 offset=offset,
3918             )
3919         if l > len(v):
3920             raise NotEnoughData(
3921                 "encoded length is longer than data",
3922                 klass=self.__class__,
3923                 decode_path=decode_path,
3924                 offset=offset,
3925             )
3926         chunks = []
3927         chunks_count = 0
3928         sub_offset = offset + tlen + llen
3929         vlen = 0
3930         payload_len = 0
3931         while True:
3932             if lenindef:
3933                 if v[:EOC_LEN].tobytes() == EOC:
3934                     break
3935             else:
3936                 if vlen == l:
3937                     break
3938                 if vlen > l:
3939                     raise DecodeError(
3940                         "chunk out of bounds",
3941                         klass=self.__class__,
3942                         decode_path=decode_path + (str(len(chunks) - 1),),
3943                         offset=chunks[-1].offset,
3944                     )
3945             try:
3946                 if evgen_mode:
3947                     sub_decode_path = decode_path + (str(chunks_count),)
3948                     for _decode_path, chunk, v_tail in OctetString().decode_evgen(
3949                             v,
3950                             offset=sub_offset,
3951                             decode_path=sub_decode_path,
3952                             leavemm=True,
3953                             ctx=ctx,
3954                             _ctx_immutable=False,
3955                     ):
3956                         yield _decode_path, chunk, v_tail
3957                         if not chunk.ber_encoded:
3958                             payload_len += chunk.vlen
3959                     chunks_count += 1
3960                 else:
3961                     sub_decode_path = decode_path + (str(len(chunks)),)
3962                     _, chunk, v_tail = next(OctetString().decode_evgen(
3963                         v,
3964                         offset=sub_offset,
3965                         decode_path=sub_decode_path,
3966                         leavemm=True,
3967                         ctx=ctx,
3968                         _ctx_immutable=False,
3969                         _evgen_mode=False,
3970                     ))
3971                     chunks.append(chunk)
3972             except TagMismatch:
3973                 raise DecodeError(
3974                     "expected OctetString encoded chunk",
3975                     klass=self.__class__,
3976                     decode_path=sub_decode_path,
3977                     offset=sub_offset,
3978                 )
3979             sub_offset += chunk.tlvlen
3980             vlen += chunk.tlvlen
3981             v = v_tail
3982         if evgen_mode and not self._bound_min <= payload_len <= self._bound_max:
3983             raise DecodeError(
3984                 msg=str(BoundsError(self._bound_min, payload_len, self._bound_max)),
3985                 klass=self.__class__,
3986                 decode_path=decode_path,
3987                 offset=offset,
3988             )
3989         try:
3990             obj = self.__class__(
3991                 value=(
3992                     None if evgen_mode else
3993                     b"".join(bytes(chunk) for chunk in chunks)
3994                 ),
3995                 bounds=(self._bound_min, self._bound_max),
3996                 impl=self.tag,
3997                 expl=self._expl,
3998                 default=self.default,
3999                 optional=self.optional,
4000                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4001                 ctx=ctx,
4002             )
4003         except DecodeError as err:
4004             raise DecodeError(
4005                 msg=err.msg,
4006                 klass=self.__class__,
4007                 decode_path=decode_path,
4008                 offset=offset,
4009             )
4010         except BoundsError as err:
4011             raise DecodeError(
4012                 msg=str(err),
4013                 klass=self.__class__,
4014                 decode_path=decode_path,
4015                 offset=offset,
4016             )
4017         obj.lenindef = lenindef
4018         obj.ber_encoded = True
4019         yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
4020
4021     def __repr__(self):
4022         return pp_console_row(next(self.pps()))
4023
4024     def pps(self, decode_path=()):
4025         yield _pp(
4026             obj=self,
4027             asn1_type_name=self.asn1_type_name,
4028             obj_name=self.__class__.__name__,
4029             decode_path=decode_path,
4030             value=("%d bytes" % len(self._value)) if self.ready else None,
4031             blob=self._value if self.ready else None,
4032             optional=self.optional,
4033             default=self == self.default,
4034             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4035             expl=None if self._expl is None else tag_decode(self._expl),
4036             offset=self.offset,
4037             tlen=self.tlen,
4038             llen=self.llen,
4039             vlen=self.vlen,
4040             expl_offset=self.expl_offset if self.expled else None,
4041             expl_tlen=self.expl_tlen if self.expled else None,
4042             expl_llen=self.expl_llen if self.expled else None,
4043             expl_vlen=self.expl_vlen if self.expled else None,
4044             expl_lenindef=self.expl_lenindef,
4045             lenindef=self.lenindef,
4046             ber_encoded=self.ber_encoded,
4047             bered=self.bered,
4048         )
4049         defined_by, defined = self.defined or (None, None)
4050         if defined_by is not None:
4051             yield defined.pps(
4052                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4053             )
4054         for pp in self.pps_lenindef(decode_path):
4055             yield pp
4056
4057
4058 def agg_octet_string(evgens, decode_path, raw, writer):
4059     """Aggregate constructed string (OctetString and its derivatives)
4060
4061     :param evgens: iterator of generated events
4062     :param decode_path: points to the string we want to decode
4063     :param raw: slicebable (memoryview, bytearray, etc) with
4064                 the data evgens are generated on
4065     :param writer: buffer.write where string is going to be saved
4066     :param writer: where string is going to be saved. Must comply
4067                    with ``io.RawIOBase.write`` behaviour
4068
4069     .. seealso:: :ref:`agg_octet_string`
4070     """
4071     decode_path_len = len(decode_path)
4072     for dp, obj, _ in evgens:
4073         if dp[:decode_path_len] != decode_path:
4074             continue
4075         if not obj.ber_encoded:
4076             write_full(writer, raw[
4077                 obj.offset + obj.tlen + obj.llen:
4078                 obj.offset + obj.tlen + obj.llen + obj.vlen -
4079                 (EOC_LEN if obj.expl_lenindef else 0)
4080             ])
4081         if len(dp) == decode_path_len:
4082             break
4083
4084
4085 NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
4086
4087
4088 class Null(Obj):
4089     """``NULL`` null object
4090
4091     >>> n = Null()
4092     NULL
4093     >>> n.ready
4094     True
4095     """
4096     __slots__ = ()
4097     tag_default = tag_encode(5)
4098     asn1_type_name = "NULL"
4099
4100     def __init__(
4101             self,
4102             value=None,  # unused, but Sequence passes it
4103             impl=None,
4104             expl=None,
4105             optional=False,
4106             _decoded=(0, 0, 0),
4107     ):
4108         """
4109         :param bytes impl: override default tag with ``IMPLICIT`` one
4110         :param bytes expl: override default tag with ``EXPLICIT`` one
4111         :param bool optional: is object ``OPTIONAL`` in sequence
4112         """
4113         super().__init__(impl, expl, None, optional, _decoded)
4114         self.default = None
4115
4116     @property
4117     def ready(self):
4118         return True
4119
4120     def __getstate__(self):
4121         return NullState(
4122             __version__,
4123             self.tag,
4124             self._tag_order,
4125             self._expl,
4126             self.default,
4127             self.optional,
4128             self.offset,
4129             self.llen,
4130             self.vlen,
4131             self.expl_lenindef,
4132             self.lenindef,
4133             self.ber_encoded,
4134         )
4135
4136     def __eq__(self, their):
4137         if not issubclass(their.__class__, Null):
4138             return False
4139         return (
4140             self.tag == their.tag and
4141             self._expl == their._expl
4142         )
4143
4144     def __call__(
4145             self,
4146             value=None,
4147             impl=None,
4148             expl=None,
4149             optional=None,
4150     ):
4151         return self.__class__(
4152             impl=self.tag if impl is None else impl,
4153             expl=self._expl if expl is None else expl,
4154             optional=self.optional if optional is None else optional,
4155         )
4156
4157     def _encode(self):
4158         return self.tag + LEN0
4159
4160     def _encode1st(self, state):
4161         return len(self.tag) + 1, state
4162
4163     def _encode2nd(self, writer, state_iter):
4164         write_full(writer, self.tag + LEN0)
4165
4166     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4167         try:
4168             t, _, lv = tag_strip(tlv)
4169         except DecodeError as err:
4170             raise err.__class__(
4171                 msg=err.msg,
4172                 klass=self.__class__,
4173                 decode_path=decode_path,
4174                 offset=offset,
4175             )
4176         if t != self.tag:
4177             raise TagMismatch(
4178                 klass=self.__class__,
4179                 decode_path=decode_path,
4180                 offset=offset,
4181             )
4182         if tag_only:  # pragma: no cover
4183             yield None
4184             return
4185         try:
4186             l, _, v = len_decode(lv)
4187         except DecodeError as err:
4188             raise err.__class__(
4189                 msg=err.msg,
4190                 klass=self.__class__,
4191                 decode_path=decode_path,
4192                 offset=offset,
4193             )
4194         if l != 0:
4195             raise InvalidLength(
4196                 "Null must have zero length",
4197                 klass=self.__class__,
4198                 decode_path=decode_path,
4199                 offset=offset,
4200             )
4201         obj = self.__class__(
4202             impl=self.tag,
4203             expl=self._expl,
4204             optional=self.optional,
4205             _decoded=(offset, 1, 0),
4206         )
4207         yield decode_path, obj, v
4208
4209     def __repr__(self):
4210         return pp_console_row(next(self.pps()))
4211
4212     def pps(self, decode_path=()):
4213         yield _pp(
4214             obj=self,
4215             asn1_type_name=self.asn1_type_name,
4216             obj_name=self.__class__.__name__,
4217             decode_path=decode_path,
4218             optional=self.optional,
4219             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4220             expl=None if self._expl is None else tag_decode(self._expl),
4221             offset=self.offset,
4222             tlen=self.tlen,
4223             llen=self.llen,
4224             vlen=self.vlen,
4225             expl_offset=self.expl_offset if self.expled else None,
4226             expl_tlen=self.expl_tlen if self.expled else None,
4227             expl_llen=self.expl_llen if self.expled else None,
4228             expl_vlen=self.expl_vlen if self.expled else None,
4229             expl_lenindef=self.expl_lenindef,
4230             bered=self.bered,
4231         )
4232         for pp in self.pps_lenindef(decode_path):
4233             yield pp
4234
4235
4236 ObjectIdentifierState = namedtuple(
4237     "ObjectIdentifierState",
4238     BasicState._fields + ("value", "defines"),
4239     **NAMEDTUPLE_KWARGS
4240 )
4241
4242
4243 class ObjectIdentifier(Obj):
4244     """``OBJECT IDENTIFIER`` OID type
4245
4246     >>> oid = ObjectIdentifier((1, 2, 3))
4247     OBJECT IDENTIFIER 1.2.3
4248     >>> oid == ObjectIdentifier("1.2.3")
4249     True
4250     >>> tuple(oid)
4251     (1, 2, 3)
4252     >>> str(oid)
4253     '1.2.3'
4254     >>> oid + (4, 5) + ObjectIdentifier("1.7")
4255     OBJECT IDENTIFIER 1.2.3.4.5.1.7
4256
4257     >>> str(ObjectIdentifier((3, 1)))
4258     Traceback (most recent call last):
4259     pyderasn.InvalidOID: unacceptable first arc value
4260     """
4261     __slots__ = ("defines",)
4262     tag_default = tag_encode(6)
4263     asn1_type_name = "OBJECT IDENTIFIER"
4264
4265     def __init__(
4266             self,
4267             value=None,
4268             defines=(),
4269             impl=None,
4270             expl=None,
4271             default=None,
4272             optional=False,
4273             _decoded=(0, 0, 0),
4274     ):
4275         """
4276         :param value: set the value. Either tuples of integers,
4277                       string of "."-concatenated integers, or
4278                       :py:class:`pyderasn.ObjectIdentifier` object
4279         :param defines: sequence of tuples. Each tuple has two elements.
4280                         First one is relative to current one decode
4281                         path, aiming to the field defined by that OID.
4282                         Read about relative path in
4283                         :py:func:`pyderasn.abs_decode_path`. Second
4284                         tuple element is ``{OID: pyderasn.Obj()}``
4285                         dictionary, mapping between current OID value
4286                         and structure applied to defined field.
4287
4288                         .. seealso:: :ref:`definedby`
4289
4290         :param bytes impl: override default tag with ``IMPLICIT`` one
4291         :param bytes expl: override default tag with ``EXPLICIT`` one
4292         :param default: set default value. Type same as in ``value``
4293         :param bool optional: is object ``OPTIONAL`` in sequence
4294         """
4295         super().__init__(impl, expl, default, optional, _decoded)
4296         self._value = value
4297         if value is not None:
4298             self._value = self._value_sanitize(value)
4299         if default is not None:
4300             default = self._value_sanitize(default)
4301             self.default = self.__class__(
4302                 value=default,
4303                 impl=self.tag,
4304                 expl=self._expl,
4305             )
4306             if self._value is None:
4307                 self._value = default
4308         self.defines = defines
4309
4310     def __add__(self, their):
4311         if their.__class__ == tuple:
4312             return self.__class__(self._value + array("L", their))
4313         if isinstance(their, self.__class__):
4314             return self.__class__(self._value + their._value)
4315         raise InvalidValueType((self.__class__, tuple))
4316
4317     def _value_sanitize(self, value):
4318         if issubclass(value.__class__, ObjectIdentifier):
4319             return value._value
4320         if isinstance(value, str):
4321             try:
4322                 value = array("L", (pureint(arc) for arc in value.split(".")))
4323             except ValueError:
4324                 raise InvalidOID("unacceptable arcs values")
4325         if value.__class__ == tuple:
4326             try:
4327                 value = array("L", value)
4328             except OverflowError as err:
4329                 raise InvalidOID(repr(err))
4330         if value.__class__ is array:
4331             if len(value) < 2:
4332                 raise InvalidOID("less than 2 arcs")
4333             first_arc = value[0]
4334             if first_arc in (0, 1):
4335                 if not (0 <= value[1] <= 39):
4336                     raise InvalidOID("second arc is too wide")
4337             elif first_arc == 2:
4338                 pass
4339             else:
4340                 raise InvalidOID("unacceptable first arc value")
4341             if not all(arc >= 0 for arc in value):
4342                 raise InvalidOID("negative arc value")
4343             return value
4344         raise InvalidValueType((self.__class__, str, tuple))
4345
4346     @property
4347     def ready(self):
4348         return self._value is not None
4349
4350     def __getstate__(self):
4351         return ObjectIdentifierState(
4352             __version__,
4353             self.tag,
4354             self._tag_order,
4355             self._expl,
4356             self.default,
4357             self.optional,
4358             self.offset,
4359             self.llen,
4360             self.vlen,
4361             self.expl_lenindef,
4362             self.lenindef,
4363             self.ber_encoded,
4364             self._value,
4365             self.defines,
4366         )
4367
4368     def __setstate__(self, state):
4369         super().__setstate__(state)
4370         self._value = state.value
4371         self.defines = state.defines
4372
4373     def __iter__(self):
4374         self._assert_ready()
4375         return iter(self._value)
4376
4377     def __str__(self):
4378         return ".".join(str(arc) for arc in self._value or ())
4379
4380     def __hash__(self):
4381         self._assert_ready()
4382         return hash(b"".join((
4383             self.tag,
4384             bytes(self._expl or b""),
4385             str(self._value).encode("ascii"),
4386         )))
4387
4388     def __eq__(self, their):
4389         if their.__class__ == tuple:
4390             return self._value == array("L", their)
4391         if not issubclass(their.__class__, ObjectIdentifier):
4392             return False
4393         return (
4394             self.tag == their.tag and
4395             self._expl == their._expl and
4396             self._value == their._value
4397         )
4398
4399     def __lt__(self, their):
4400         return self._value < their._value
4401
4402     def __call__(
4403             self,
4404             value=None,
4405             defines=None,
4406             impl=None,
4407             expl=None,
4408             default=None,
4409             optional=None,
4410     ):
4411         return self.__class__(
4412             value=value,
4413             defines=self.defines if defines is None else defines,
4414             impl=self.tag if impl is None else impl,
4415             expl=self._expl if expl is None else expl,
4416             default=self.default if default is None else default,
4417             optional=self.optional if optional is None else optional,
4418         )
4419
4420     def _encode_octets(self):
4421         self._assert_ready()
4422         value = self._value
4423         first_value = value[1]
4424         first_arc = value[0]
4425         if first_arc == 0:
4426             pass
4427         elif first_arc == 1:
4428             first_value += 40
4429         elif first_arc == 2:
4430             first_value += 80
4431         else:  # pragma: no cover
4432             raise RuntimeError("invalid arc is stored")
4433         octets = [zero_ended_encode(first_value)]
4434         for arc in value[2:]:
4435             octets.append(zero_ended_encode(arc))
4436         return b"".join(octets)
4437
4438     def _encode(self):
4439         v = self._encode_octets()
4440         return b"".join((self.tag, len_encode(len(v)), v))
4441
4442     def _encode1st(self, state):
4443         l = len(self._encode_octets())
4444         return len(self.tag) + len_size(l) + l, state
4445
4446     def _encode2nd(self, writer, state_iter):
4447         write_full(writer, self._encode())
4448
4449     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
4450         try:
4451             t, _, lv = tag_strip(tlv)
4452         except DecodeError as err:
4453             raise err.__class__(
4454                 msg=err.msg,
4455                 klass=self.__class__,
4456                 decode_path=decode_path,
4457                 offset=offset,
4458             )
4459         if t != self.tag:
4460             raise TagMismatch(
4461                 klass=self.__class__,
4462                 decode_path=decode_path,
4463                 offset=offset,
4464             )
4465         if tag_only:  # pragma: no cover
4466             yield None
4467             return
4468         try:
4469             l, llen, v = len_decode(lv)
4470         except DecodeError as err:
4471             raise err.__class__(
4472                 msg=err.msg,
4473                 klass=self.__class__,
4474                 decode_path=decode_path,
4475                 offset=offset,
4476             )
4477         if l > len(v):
4478             raise NotEnoughData(
4479                 "encoded length is longer than data",
4480                 klass=self.__class__,
4481                 decode_path=decode_path,
4482                 offset=offset,
4483             )
4484         if l == 0:
4485             raise NotEnoughData(
4486                 "zero length",
4487                 klass=self.__class__,
4488                 decode_path=decode_path,
4489                 offset=offset,
4490             )
4491         v, tail = v[:l], v[l:]
4492         arcs = array("L")
4493         ber_encoded = False
4494         while len(v) > 0:
4495             i = 0
4496             arc = 0
4497             while True:
4498                 octet = v[i]
4499                 if i == 0 and octet == 0x80:
4500                     if ctx.get("bered", False):
4501                         ber_encoded = True
4502                     else:
4503                         raise DecodeError(
4504                             "non normalized arc encoding",
4505                             klass=self.__class__,
4506                             decode_path=decode_path,
4507                             offset=offset,
4508                         )
4509                 arc = (arc << 7) | (octet & 0x7F)
4510                 if octet & 0x80 == 0:
4511                     try:
4512                         arcs.append(arc)
4513                     except OverflowError:
4514                         raise DecodeError(
4515                             "too huge value for local unsigned long",
4516                             klass=self.__class__,
4517                             decode_path=decode_path,
4518                             offset=offset,
4519                         )
4520                     v = v[i + 1:]
4521                     break
4522                 i += 1
4523                 if i == len(v):
4524                     raise DecodeError(
4525                         "unfinished OID",
4526                         klass=self.__class__,
4527                         decode_path=decode_path,
4528                         offset=offset,
4529                     )
4530         first_arc = 0
4531         second_arc = arcs[0]
4532         if 0 <= second_arc <= 39:
4533             first_arc = 0
4534         elif 40 <= second_arc <= 79:
4535             first_arc = 1
4536             second_arc -= 40
4537         else:
4538             first_arc = 2
4539             second_arc -= 80
4540         obj = self.__class__(
4541             value=array("L", (first_arc, second_arc)) + arcs[1:],
4542             impl=self.tag,
4543             expl=self._expl,
4544             default=self.default,
4545             optional=self.optional,
4546             _decoded=(offset, llen, l),
4547         )
4548         if ber_encoded:
4549             obj.ber_encoded = True
4550         yield decode_path, obj, tail
4551
4552     def __repr__(self):
4553         return pp_console_row(next(self.pps()))
4554
4555     def pps(self, decode_path=()):
4556         yield _pp(
4557             obj=self,
4558             asn1_type_name=self.asn1_type_name,
4559             obj_name=self.__class__.__name__,
4560             decode_path=decode_path,
4561             value=str(self) if self.ready else None,
4562             optional=self.optional,
4563             default=self == self.default,
4564             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4565             expl=None if self._expl is None else tag_decode(self._expl),
4566             offset=self.offset,
4567             tlen=self.tlen,
4568             llen=self.llen,
4569             vlen=self.vlen,
4570             expl_offset=self.expl_offset if self.expled else None,
4571             expl_tlen=self.expl_tlen if self.expled else None,
4572             expl_llen=self.expl_llen if self.expled else None,
4573             expl_vlen=self.expl_vlen if self.expled else None,
4574             expl_lenindef=self.expl_lenindef,
4575             ber_encoded=self.ber_encoded,
4576             bered=self.bered,
4577         )
4578         for pp in self.pps_lenindef(decode_path):
4579             yield pp
4580
4581
4582 class Enumerated(Integer):
4583     """``ENUMERATED`` integer type
4584
4585     This type is identical to :py:class:`pyderasn.Integer`, but requires
4586     schema to be specified and does not accept values missing from it.
4587     """
4588     __slots__ = ()
4589     tag_default = tag_encode(10)
4590     asn1_type_name = "ENUMERATED"
4591
4592     def __init__(
4593             self,
4594             value=None,
4595             impl=None,
4596             expl=None,
4597             default=None,
4598             optional=False,
4599             _specs=None,
4600             _decoded=(0, 0, 0),
4601             bounds=None,  # dummy argument, workability for Integer.decode
4602     ):
4603         super().__init__(
4604             value, bounds, impl, expl, default, optional, _specs, _decoded,
4605         )
4606         if len(self.specs) == 0:
4607             raise ValueError("schema must be specified")
4608
4609     def _value_sanitize(self, value):
4610         if isinstance(value, self.__class__):
4611             value = value._value
4612         elif isinstance(value, int):
4613             for _value in self.specs.values():
4614                 if _value == value:
4615                     break
4616             else:
4617                 raise DecodeError(
4618                     "unknown integer value: %s" % value,
4619                     klass=self.__class__,
4620                 )
4621         elif isinstance(value, str):
4622             value = self.specs.get(value)
4623             if value is None:
4624                 raise ObjUnknown("integer value: %s" % value)
4625         else:
4626             raise InvalidValueType((self.__class__, int, str))
4627         return value
4628
4629     def __call__(
4630             self,
4631             value=None,
4632             impl=None,
4633             expl=None,
4634             default=None,
4635             optional=None,
4636             _specs=None,
4637     ):
4638         return self.__class__(
4639             value=value,
4640             impl=self.tag if impl is None else impl,
4641             expl=self._expl if expl is None else expl,
4642             default=self.default if default is None else default,
4643             optional=self.optional if optional is None else optional,
4644             _specs=self.specs,
4645         )
4646
4647
4648 def escape_control_unicode(c):
4649     if unicat(c)[0] == "C":
4650         c = repr(c).lstrip("u").strip("'")
4651     return c
4652
4653
4654 class CommonString(OctetString):
4655     """Common class for all strings
4656
4657     Everything resembles :py:class:`pyderasn.OctetString`, except
4658     ability to deal with unicode text strings.
4659
4660     >>> hexenc("привет Ð¼Ð¸Ñ€".encode("utf-8"))
4661     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4662     >>> UTF8String("привет Ð¼Ð¸Ñ€") == UTF8String(hexdec("d0...80"))
4663     True
4664     >>> s = UTF8String("привет Ð¼Ð¸Ñ€")
4665     UTF8String UTF8String Ð¿Ñ€Ð¸Ð²ÐµÑ‚ Ð¼Ð¸Ñ€
4666     >>> str(s)
4667     'привет Ð¼Ð¸Ñ€'
4668     >>> hexenc(bytes(s))
4669     'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
4670
4671     >>> PrintableString("привет Ð¼Ð¸Ñ€")
4672     Traceback (most recent call last):
4673     pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
4674
4675     >>> BMPString("ада", bounds=(2, 2))
4676     Traceback (most recent call last):
4677     pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
4678     >>> s = BMPString("ад", bounds=(2, 2))
4679     >>> s.encoding
4680     'utf-16-be'
4681     >>> hexenc(bytes(s))
4682     '04300434'
4683
4684     .. list-table::
4685        :header-rows: 1
4686
4687        * - Class
4688          - Text Encoding, validation
4689        * - :py:class:`pyderasn.UTF8String`
4690          - utf-8
4691        * - :py:class:`pyderasn.NumericString`
4692          - proper alphabet validation
4693        * - :py:class:`pyderasn.PrintableString`
4694          - proper alphabet validation
4695        * - :py:class:`pyderasn.TeletexString`
4696          - iso-8859-1
4697        * - :py:class:`pyderasn.T61String`
4698          - iso-8859-1
4699        * - :py:class:`pyderasn.VideotexString`
4700          - iso-8859-1
4701        * - :py:class:`pyderasn.IA5String`
4702          - proper alphabet validation
4703        * - :py:class:`pyderasn.GraphicString`
4704          - iso-8859-1
4705        * - :py:class:`pyderasn.VisibleString`, :py:class:`pyderasn.ISO646String`
4706          - proper alphabet validation
4707        * - :py:class:`pyderasn.GeneralString`
4708          - iso-8859-1
4709        * - :py:class:`pyderasn.UniversalString`
4710          - utf-32-be
4711        * - :py:class:`pyderasn.BMPString`
4712          - utf-16-be
4713     """
4714     __slots__ = ()
4715     memoryview_safe = False
4716
4717     def _value_sanitize(self, value):
4718         value_raw = None
4719         value_decoded = None
4720         if isinstance(value, self.__class__):
4721             value_raw = value._value
4722         elif value.__class__ == str:
4723             value_decoded = value
4724         elif value.__class__ == bytes:
4725             value_raw = value
4726         else:
4727             raise InvalidValueType((self.__class__, str, bytes))
4728         try:
4729             value_raw = (
4730                 value_decoded.encode(self.encoding)
4731                 if value_raw is None else value_raw
4732             )
4733             value_decoded = (
4734                 value_raw.decode(self.encoding)
4735                 if value_decoded is None else value_decoded
4736             )
4737         except (UnicodeEncodeError, UnicodeDecodeError) as err:
4738             raise DecodeError(str(err))
4739         if not self._bound_min <= len(value_decoded) <= self._bound_max:
4740             raise BoundsError(
4741                 self._bound_min,
4742                 len(value_decoded),
4743                 self._bound_max,
4744             )
4745         return value_raw
4746
4747     def __eq__(self, their):
4748         if their.__class__ == bytes:
4749             return self._value == their
4750         if their.__class__ == str:
4751             return self._value == their.encode(self.encoding)
4752         if not isinstance(their, self.__class__):
4753             return False
4754         return (
4755             self._value == their._value and
4756             self.tag == their.tag and
4757             self._expl == their._expl
4758         )
4759
4760     def __unicode__(self):
4761         if self.ready:
4762             return self._value.decode(self.encoding)
4763         return str(self._value)
4764
4765     def memoryview(self):
4766         raise NotImplementedError()
4767
4768     def __repr__(self):
4769         return pp_console_row(next(self.pps()))
4770
4771     def pps(self, decode_path=()):
4772         value = None
4773         if self.ready:
4774             value = "".join(escape_control_unicode(c) for c in self.__unicode__())
4775         yield _pp(
4776             obj=self,
4777             asn1_type_name=self.asn1_type_name,
4778             obj_name=self.__class__.__name__,
4779             decode_path=decode_path,
4780             value=value,
4781             optional=self.optional,
4782             default=self == self.default,
4783             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4784             expl=None if self._expl is None else tag_decode(self._expl),
4785             offset=self.offset,
4786             tlen=self.tlen,
4787             llen=self.llen,
4788             vlen=self.vlen,
4789             expl_offset=self.expl_offset if self.expled else None,
4790             expl_tlen=self.expl_tlen if self.expled else None,
4791             expl_llen=self.expl_llen if self.expled else None,
4792             expl_vlen=self.expl_vlen if self.expled else None,
4793             expl_lenindef=self.expl_lenindef,
4794             ber_encoded=self.ber_encoded,
4795             bered=self.bered,
4796         )
4797         for pp in self.pps_lenindef(decode_path):
4798             yield pp
4799
4800
4801 class UTF8String(CommonString):
4802     __slots__ = ()
4803     tag_default = tag_encode(12)
4804     encoding = "utf-8"
4805     asn1_type_name = "UTF8String"
4806
4807
4808 class AllowableCharsMixin:
4809     __slots__ = ()
4810
4811     @property
4812     def allowable_chars(self):
4813         return frozenset(chr(c) for c in self._allowable_chars)
4814
4815     def _value_sanitize(self, value):
4816         value = super()._value_sanitize(value)
4817         if not frozenset(value) <= self._allowable_chars:
4818             raise DecodeError("non satisfying alphabet value")
4819         return value
4820
4821
4822 NUMERIC_ALLOWABLE_CHARS = frozenset(digits.encode("ascii") + b" ")
4823
4824
4825 class NumericString(AllowableCharsMixin, CommonString):
4826     """Numeric string
4827
4828     Its value is properly sanitized: only ASCII digits with spaces can
4829     be stored.
4830
4831     >>> NumericString().allowable_chars
4832     frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
4833     """
4834     __slots__ = ("_allowable_chars",)
4835     tag_default = tag_encode(18)
4836     encoding = "ascii"
4837     asn1_type_name = "NumericString"
4838
4839     def __init__(self, *args, **kwargs):
4840         self._allowable_chars = NUMERIC_ALLOWABLE_CHARS
4841         super().__init__(*args, **kwargs)
4842
4843
4844 PrintableStringState = namedtuple(
4845     "PrintableStringState",
4846     OctetStringState._fields + ("allowable_chars",),
4847     **NAMEDTUPLE_KWARGS
4848 )
4849
4850
4851 PRINTABLE_ALLOWABLE_CHARS = frozenset(
4852     (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
4853 )
4854
4855
4856 class PrintableString(AllowableCharsMixin, CommonString):
4857     """Printable string
4858
4859     Its value is properly sanitized: see X.680 41.4 table 10.
4860
4861     >>> PrintableString().allowable_chars
4862     frozenset([' ', "'", ..., 'z'])
4863     >>> obj = PrintableString("foo*bar", allow_asterisk=True)
4864     PrintableString PrintableString foo*bar
4865     >>> obj.allow_asterisk, obj.allow_ampersand
4866     (True, False)
4867     """
4868     __slots__ = ("_allowable_chars",)
4869     tag_default = tag_encode(19)
4870     encoding = "ascii"
4871     asn1_type_name = "PrintableString"
4872     _asterisk = frozenset("*".encode("ascii"))
4873     _ampersand = frozenset("&".encode("ascii"))
4874
4875     def __init__(
4876             self,
4877             value=None,
4878             bounds=None,
4879             impl=None,
4880             expl=None,
4881             default=None,
4882             optional=False,
4883             _decoded=(0, 0, 0),
4884             ctx=None,
4885             allow_asterisk=False,
4886             allow_ampersand=False,
4887     ):
4888         """
4889         :param allow_asterisk: allow asterisk character
4890         :param allow_ampersand: allow ampersand character
4891         """
4892         allowable_chars = PRINTABLE_ALLOWABLE_CHARS
4893         if allow_asterisk:
4894             allowable_chars |= self._asterisk
4895         if allow_ampersand:
4896             allowable_chars |= self._ampersand
4897         self._allowable_chars = allowable_chars
4898         super().__init__(
4899             value, bounds, impl, expl, default, optional, _decoded, ctx,
4900         )
4901
4902     @property
4903     def allow_asterisk(self):
4904         """Is asterisk character allowed?
4905         """
4906         return self._asterisk <= self._allowable_chars
4907
4908     @property
4909     def allow_ampersand(self):
4910         """Is ampersand character allowed?
4911         """
4912         return self._ampersand <= self._allowable_chars
4913
4914     def __getstate__(self):
4915         return PrintableStringState(
4916             *super().__getstate__(),
4917             **{"allowable_chars": self._allowable_chars}
4918         )
4919
4920     def __setstate__(self, state):
4921         super().__setstate__(state)
4922         self._allowable_chars = state.allowable_chars
4923
4924     def __call__(
4925             self,
4926             value=None,
4927             bounds=None,
4928             impl=None,
4929             expl=None,
4930             default=None,
4931             optional=None,
4932     ):
4933         return self.__class__(
4934             value=value,
4935             bounds=(
4936                 (self._bound_min, self._bound_max)
4937                 if bounds is None else bounds
4938             ),
4939             impl=self.tag if impl is None else impl,
4940             expl=self._expl if expl is None else expl,
4941             default=self.default if default is None else default,
4942             optional=self.optional if optional is None else optional,
4943             allow_asterisk=self.allow_asterisk,
4944             allow_ampersand=self.allow_ampersand,
4945         )
4946
4947
4948 class TeletexString(CommonString):
4949     __slots__ = ()
4950     tag_default = tag_encode(20)
4951     encoding = "iso-8859-1"
4952     asn1_type_name = "TeletexString"
4953
4954
4955 class T61String(TeletexString):
4956     __slots__ = ()
4957     asn1_type_name = "T61String"
4958
4959
4960 class VideotexString(CommonString):
4961     __slots__ = ()
4962     tag_default = tag_encode(21)
4963     encoding = "iso-8859-1"
4964     asn1_type_name = "VideotexString"
4965
4966
4967 IA5_ALLOWABLE_CHARS = frozenset(b"".join(
4968     chr(c).encode("ascii") for c in range(128)
4969 ))
4970
4971
4972 class IA5String(AllowableCharsMixin, CommonString):
4973     """IA5 string
4974
4975     Its value is properly sanitized: it is a mix of
4976
4977     * http://www.itscj.ipsj.or.jp/iso-ir/006.pdf (G)
4978     * http://www.itscj.ipsj.or.jp/iso-ir/001.pdf (C0)
4979     * DEL character (0x7F)
4980
4981     It is just 7-bit ASCII.
4982
4983     >>> IA5String().allowable_chars
4984     frozenset(["NUL", ... "DEL"])
4985     """
4986     __slots__ = ("_allowable_chars",)
4987     tag_default = tag_encode(22)
4988     encoding = "ascii"
4989     asn1_type_name = "IA5"
4990
4991     def __init__(self, *args, **kwargs):
4992         self._allowable_chars = IA5_ALLOWABLE_CHARS
4993         super().__init__(*args, **kwargs)
4994
4995
4996 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
4997 LEN_LEN_YYMMDDHHMMSSZ = len_encode(LEN_YYMMDDHHMMSSZ)
4998 LEN_YYMMDDHHMMSSZ_WITH_LEN = len(LEN_LEN_YYMMDDHHMMSSZ) + LEN_YYMMDDHHMMSSZ
4999 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
5000 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
5001 LEN_LEN_YYYYMMDDHHMMSSZ = len_encode(LEN_YYYYMMDDHHMMSSZ)
5002
5003
5004 VISIBLE_ALLOWABLE_CHARS = frozenset(b"".join(
5005     chr(c).encode("ascii") for c in range(ord(" "), ord("~") + 1)
5006 ))
5007
5008
5009 class VisibleString(AllowableCharsMixin, CommonString):
5010     """Visible string
5011
5012     Its value is properly sanitized. ASCII subset from space to tilde is
5013     allowed: http://www.itscj.ipsj.or.jp/iso-ir/006.pdf
5014
5015     >>> VisibleString().allowable_chars
5016     frozenset([" ", ... "~"])
5017     """
5018     __slots__ = ("_allowable_chars",)
5019     tag_default = tag_encode(26)
5020     encoding = "ascii"
5021     asn1_type_name = "VisibleString"
5022
5023     def __init__(self, *args, **kwargs):
5024         self._allowable_chars = VISIBLE_ALLOWABLE_CHARS
5025         super().__init__(*args, **kwargs)
5026
5027
5028 class ISO646String(VisibleString):
5029     __slots__ = ()
5030     asn1_type_name = "ISO646String"
5031
5032
5033 UTCTimeState = namedtuple(
5034     "UTCTimeState",
5035     OctetStringState._fields + ("ber_raw",),
5036     **NAMEDTUPLE_KWARGS
5037 )
5038
5039
5040 def str_to_time_fractions(value):
5041     v = pureint(value)
5042     year, v = (v // 10**10), (v % 10**10)
5043     month, v = (v // 10**8), (v % 10**8)
5044     day, v = (v // 10**6), (v % 10**6)
5045     hour, v = (v // 10**4), (v % 10**4)
5046     minute, second = (v // 100), (v % 100)
5047     return year, month, day, hour, minute, second
5048
5049
5050 class UTCTime(VisibleString):
5051     """``UTCTime`` datetime type
5052
5053     >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5054     UTCTime UTCTime 2017-09-30T22:07:50
5055     >>> str(t)
5056     '170930220750Z'
5057     >>> bytes(t)
5058     b'170930220750Z'
5059     >>> t.todatetime()
5060     datetime.datetime(2017, 9, 30, 22, 7, 50)
5061     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
5062     datetime.datetime(1957, 9, 30, 22, 7, 50)
5063     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).totzdatetime()
5064     datetime.datetime(1957, 9, 30, 22, 7, 50, tzinfo=tzutc())
5065
5066     If BER encoded value was met, then ``ber_raw`` attribute will hold
5067     its raw representation.
5068
5069     .. warning::
5070
5071        Only **naive** ``datetime`` objects are supported.
5072        Library assumes that all work is done in UTC.
5073
5074     .. warning::
5075
5076        Pay attention that ``UTCTime`` can not hold full year, so all years
5077        having < 50 years are treated as 20xx, 19xx otherwise, according to
5078        X.509 recommendation. Use ``GeneralizedTime`` instead for
5079        removing ambiguity.
5080
5081     .. warning::
5082
5083        No strict validation of UTC offsets are made (only applicable to
5084        **BER**), but very crude:
5085
5086        * minutes are not exceeding 60
5087        * offset value is not exceeding 14 hours
5088     """
5089     __slots__ = ("ber_raw",)
5090     tag_default = tag_encode(23)
5091     encoding = "ascii"
5092     asn1_type_name = "UTCTime"
5093     evgen_mode_skip_value = False
5094
5095     def __init__(
5096             self,
5097             value=None,
5098             impl=None,
5099             expl=None,
5100             default=None,
5101             optional=False,
5102             _decoded=(0, 0, 0),
5103             bounds=None,  # dummy argument, workability for OctetString.decode
5104             ctx=None,
5105     ):
5106         """
5107         :param value: set the value. Either datetime type, or
5108                       :py:class:`pyderasn.UTCTime` object
5109         :param bytes impl: override default tag with ``IMPLICIT`` one
5110         :param bytes expl: override default tag with ``EXPLICIT`` one
5111         :param default: set default value. Type same as in ``value``
5112         :param bool optional: is object ``OPTIONAL`` in sequence
5113         """
5114         super().__init__(None, None, impl, expl, None, optional, _decoded, ctx)
5115         self._value = value
5116         self.ber_raw = None
5117         if value is not None:
5118             self._value, self.ber_raw = self._value_sanitize(value, ctx)
5119             self.ber_encoded = self.ber_raw is not None
5120         if default is not None:
5121             default, _ = self._value_sanitize(default)
5122             self.default = self.__class__(
5123                 value=default,
5124                 impl=self.tag,
5125                 expl=self._expl,
5126             )
5127             if self._value is None:
5128                 self._value = default
5129             optional = True
5130         self.optional = optional
5131
5132     def _strptime_bered(self, value):
5133         year, month, day, hour, minute, _ = str_to_time_fractions(value[:10] + "00")
5134         value = value[10:]
5135         if len(value) == 0:
5136             raise ValueError("no timezone")
5137         year += 2000 if year < 50 else 1900
5138         decoded = datetime(year, month, day, hour, minute)
5139         offset = 0
5140         if value[-1] == "Z":
5141             value = value[:-1]
5142         else:
5143             if len(value) < 5:
5144                 raise ValueError("invalid UTC offset")
5145             if value[-5] == "-":
5146                 sign = -1
5147             elif value[-5] == "+":
5148                 sign = 1
5149             else:
5150                 raise ValueError("invalid UTC offset")
5151             v = pureint(value[-4:])
5152             offset, v = (60 * (v % 100)), v // 100
5153             if offset >= 3600:
5154                 raise ValueError("invalid UTC offset minutes")
5155             offset += 3600 * v
5156             if offset > 14 * 3600:
5157                 raise ValueError("too big UTC offset")
5158             offset *= sign
5159             value = value[:-5]
5160         if len(value) == 0:
5161             return offset, decoded
5162         if len(value) != 2:
5163             raise ValueError("invalid UTC offset seconds")
5164         seconds = pureint(value)
5165         if seconds >= 60:
5166             raise ValueError("invalid seconds value")
5167         return offset, decoded + timedelta(seconds=seconds)
5168
5169     def _strptime(self, value):
5170         # datetime.strptime's format: %y%m%d%H%M%SZ
5171         if len(value) != LEN_YYMMDDHHMMSSZ:
5172             raise ValueError("invalid UTCTime length")
5173         if value[-1] != "Z":
5174             raise ValueError("non UTC timezone")
5175         year, month, day, hour, minute, second = str_to_time_fractions(value[:-1])
5176         year += 2000 if year < 50 else 1900
5177         return datetime(year, month, day, hour, minute, second)
5178
5179     def _dt_sanitize(self, value):
5180         if value.year < 1950 or value.year > 2049:
5181             raise ValueError("UTCTime can hold only 1950-2049 years")
5182         return value.replace(microsecond=0)
5183
5184     def _value_sanitize(self, value, ctx=None):
5185         if value.__class__ == bytes:
5186             try:
5187                 value_decoded = value.decode("ascii")
5188             except (UnicodeEncodeError, UnicodeDecodeError) as err:
5189                 raise DecodeError("invalid UTCTime encoding: %r" % err)
5190             err = None
5191             try:
5192                 return self._strptime(value_decoded), None
5193             except (TypeError, ValueError) as _err:
5194                 err = _err
5195                 if (ctx is not None) and ctx.get("bered", False):
5196                     try:
5197                         offset, _value = self._strptime_bered(value_decoded)
5198                         _value = _value - timedelta(seconds=offset)
5199                         return self._dt_sanitize(_value), value
5200                     except (TypeError, ValueError, OverflowError) as _err:
5201                         err = _err
5202             raise DecodeError(
5203                 "invalid %s format: %r" % (self.asn1_type_name, err),
5204                 klass=self.__class__,
5205             )
5206         if isinstance(value, self.__class__):
5207             return value._value, None
5208         if value.__class__ == datetime:
5209             if value.tzinfo is not None:
5210                 raise ValueError("only naive datetime supported")
5211             return self._dt_sanitize(value), None
5212         raise InvalidValueType((self.__class__, datetime))
5213
5214     def _pp_value(self):
5215         if self.ready:
5216             value = self._value.isoformat()
5217             if self.ber_encoded:
5218                 value += " (%s)" % self.ber_raw
5219             return value
5220         return None
5221
5222     def __unicode__(self):
5223         if self.ready:
5224             value = self._value.isoformat()
5225             if self.ber_encoded:
5226                 value += " (%s)" % self.ber_raw
5227             return value
5228         return str(self._pp_value())
5229
5230     def __getstate__(self):
5231         return UTCTimeState(*super().__getstate__(), **{"ber_raw": self.ber_raw})
5232
5233     def __setstate__(self, state):
5234         super().__setstate__(state)
5235         self.ber_raw = state.ber_raw
5236
5237     def __bytes__(self):
5238         self._assert_ready()
5239         return self._encode_time()
5240
5241     def __eq__(self, their):
5242         if their.__class__ == bytes:
5243             return self._encode_time() == their
5244         if their.__class__ == datetime:
5245             return self.todatetime() == their
5246         if not isinstance(their, self.__class__):
5247             return False
5248         return (
5249             self._value == their._value and
5250             self.tag == their.tag and
5251             self._expl == their._expl
5252         )
5253
5254     def _encode_time(self):
5255         return self._value.strftime("%y%m%d%H%M%SZ").encode("ascii")
5256
5257     def _encode(self):
5258         self._assert_ready()
5259         return b"".join((self.tag, LEN_LEN_YYMMDDHHMMSSZ, self._encode_time()))
5260
5261     def _encode1st(self, state):
5262         return len(self.tag) + LEN_YYMMDDHHMMSSZ_WITH_LEN, state
5263
5264     def _encode2nd(self, writer, state_iter):
5265         self._assert_ready()
5266         write_full(writer, self._encode())
5267
5268     def _encode_cer(self, writer):
5269         write_full(writer, self._encode())
5270
5271     def todatetime(self):
5272         return self._value
5273
5274     def totzdatetime(self):
5275         try:
5276             return self._value.replace(tzinfo=tzUTC)
5277         except TypeError as err:
5278             raise NotImplementedError("Missing dateutil.tz") from err
5279
5280     def __repr__(self):
5281         return pp_console_row(next(self.pps()))
5282
5283     def pps(self, decode_path=()):
5284         yield _pp(
5285             obj=self,
5286             asn1_type_name=self.asn1_type_name,
5287             obj_name=self.__class__.__name__,
5288             decode_path=decode_path,
5289             value=self._pp_value(),
5290             optional=self.optional,
5291             default=self == self.default,
5292             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5293             expl=None if self._expl is None else tag_decode(self._expl),
5294             offset=self.offset,
5295             tlen=self.tlen,
5296             llen=self.llen,
5297             vlen=self.vlen,
5298             expl_offset=self.expl_offset if self.expled else None,
5299             expl_tlen=self.expl_tlen if self.expled else None,
5300             expl_llen=self.expl_llen if self.expled else None,
5301             expl_vlen=self.expl_vlen if self.expled else None,
5302             expl_lenindef=self.expl_lenindef,
5303             ber_encoded=self.ber_encoded,
5304             bered=self.bered,
5305         )
5306         for pp in self.pps_lenindef(decode_path):
5307             yield pp
5308
5309
5310 class GeneralizedTime(UTCTime):
5311     """``GeneralizedTime`` datetime type
5312
5313     This type is similar to :py:class:`pyderasn.UTCTime`.
5314
5315     >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
5316     GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
5317     >>> str(t)
5318     '20170930220750.000123Z'
5319     >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
5320     GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
5321
5322     .. warning::
5323
5324        Only **naive** datetime objects are supported.
5325        Library assumes that all work is done in UTC.
5326
5327     .. warning::
5328
5329        Only **microsecond** fractions are supported in DER encoding.
5330        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
5331        higher precision values.
5332
5333     .. warning::
5334
5335        **BER** encoded data can loss information (accuracy) during
5336        decoding because of float transformations.
5337
5338     .. warning::
5339
5340        **Zero** year is unsupported.
5341     """
5342     __slots__ = ()
5343     tag_default = tag_encode(24)
5344     asn1_type_name = "GeneralizedTime"
5345
5346     def _dt_sanitize(self, value):
5347         return value
5348
5349     def _strptime_bered(self, value):
5350         if len(value) < 4 + 3 * 2:
5351             raise ValueError("invalid GeneralizedTime")
5352         year, month, day, hour, _, _ = str_to_time_fractions(value[:10] + "0000")
5353         decoded = datetime(year, month, day, hour)
5354         offset, value = 0, value[10:]
5355         if len(value) == 0:
5356             return offset, decoded
5357         if value[-1] == "Z":
5358             value = value[:-1]
5359         else:
5360             for char, sign in (("-", -1), ("+", 1)):
5361                 idx = value.rfind(char)
5362                 if idx == -1:
5363                     continue
5364                 offset_raw, value = value[idx + 1:].replace(":", ""), value[:idx]
5365                 v = pureint(offset_raw)
5366                 if len(offset_raw) == 4:
5367                     offset, v = (60 * (v % 100)), v // 100
5368                     if offset >= 3600:
5369                         raise ValueError("invalid UTC offset minutes")
5370                 elif len(offset_raw) == 2:
5371                     pass
5372                 else:
5373                     raise ValueError("invalid UTC offset")
5374                 offset += 3600 * v
5375                 if offset > 14 * 3600:
5376                     raise ValueError("too big UTC offset")
5377                 offset *= sign
5378                 break
5379         if len(value) == 0:
5380             return offset, decoded
5381         if value[0] in DECIMAL_SIGNS:
5382             return offset, (
5383                 decoded + timedelta(seconds=3600 * fractions2float(value[1:]))
5384             )
5385         if len(value) < 2:
5386             raise ValueError("stripped minutes")
5387         decoded += timedelta(seconds=60 * pureint(value[:2]))
5388         value = value[2:]
5389         if len(value) == 0:
5390             return offset, decoded
5391         if value[0] in DECIMAL_SIGNS:
5392             return offset, (
5393                 decoded + timedelta(seconds=60 * fractions2float(value[1:]))
5394             )
5395         if len(value) < 2:
5396             raise ValueError("stripped seconds")
5397         decoded += timedelta(seconds=pureint(value[:2]))
5398         value = value[2:]
5399         if len(value) == 0:
5400             return offset, decoded
5401         if value[0] not in DECIMAL_SIGNS:
5402             raise ValueError("invalid format after seconds")
5403         return offset, (
5404             decoded + timedelta(microseconds=10**6 * fractions2float(value[1:]))
5405         )
5406
5407     def _strptime(self, value):
5408         l = len(value)
5409         if l == LEN_YYYYMMDDHHMMSSZ:
5410             # datetime.strptime's format: %Y%m%d%H%M%SZ
5411             if value[-1] != "Z":
5412                 raise ValueError("non UTC timezone")
5413             return datetime(*str_to_time_fractions(value[:-1]))
5414         if l >= LEN_YYYYMMDDHHMMSSDMZ:
5415             # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
5416             if value[-1] != "Z":
5417                 raise ValueError("non UTC timezone")
5418             if value[14] != ".":
5419                 raise ValueError("no fractions separator")
5420             us = value[15:-1]
5421             if us[-1] == "0":
5422                 raise ValueError("trailing zero")
5423             us_len = len(us)
5424             if us_len > 6:
5425                 raise ValueError("only microsecond fractions are supported")
5426             us = pureint(us + ("0" * (6 - us_len)))
5427             year, month, day, hour, minute, second = str_to_time_fractions(value[:14])
5428             return datetime(year, month, day, hour, minute, second, us)
5429         raise ValueError("invalid GeneralizedTime length")
5430
5431     def _encode_time(self):
5432         value = self._value
5433         encoded = value.strftime("%Y%m%d%H%M%S")
5434         if value.microsecond > 0:
5435             encoded += (".%06d" % value.microsecond).rstrip("0")
5436         return (encoded + "Z").encode("ascii")
5437
5438     def _encode(self):
5439         self._assert_ready()
5440         value = self._value
5441         if value.microsecond > 0:
5442             encoded = self._encode_time()
5443             return b"".join((self.tag, len_encode(len(encoded)), encoded))
5444         return b"".join((self.tag, LEN_LEN_YYYYMMDDHHMMSSZ, self._encode_time()))
5445
5446     def _encode1st(self, state):
5447         self._assert_ready()
5448         vlen = len(self._encode_time())
5449         return len(self.tag) + len_size(vlen) + vlen, state
5450
5451     def _encode2nd(self, writer, state_iter):
5452         write_full(writer, self._encode())
5453
5454
5455 class GraphicString(CommonString):
5456     __slots__ = ()
5457     tag_default = tag_encode(25)
5458     encoding = "iso-8859-1"
5459     asn1_type_name = "GraphicString"
5460
5461
5462 class GeneralString(CommonString):
5463     __slots__ = ()
5464     tag_default = tag_encode(27)
5465     encoding = "iso-8859-1"
5466     asn1_type_name = "GeneralString"
5467
5468
5469 class UniversalString(CommonString):
5470     __slots__ = ()
5471     tag_default = tag_encode(28)
5472     encoding = "utf-32-be"
5473     asn1_type_name = "UniversalString"
5474
5475
5476 class BMPString(CommonString):
5477     __slots__ = ()
5478     tag_default = tag_encode(30)
5479     encoding = "utf-16-be"
5480     asn1_type_name = "BMPString"
5481
5482
5483 ChoiceState = namedtuple(
5484     "ChoiceState",
5485     BasicState._fields + ("specs", "value",),
5486     **NAMEDTUPLE_KWARGS
5487 )
5488
5489
5490 class Choice(Obj):
5491     """``CHOICE`` special type
5492
5493     ::
5494
5495         class GeneralName(Choice):
5496             schema = (
5497                 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
5498                 ("dNSName", IA5String(impl=tag_ctxp(2))),
5499             )
5500
5501     >>> gn = GeneralName()
5502     GeneralName CHOICE
5503     >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
5504     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5505     >>> gn["dNSName"] = IA5String("bar.baz")
5506     GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
5507     >>> gn["rfc822Name"]
5508     None
5509     >>> gn["dNSName"]
5510     [2] IA5String IA5 bar.baz
5511     >>> gn.choice
5512     'dNSName'
5513     >>> gn.value == gn["dNSName"]
5514     True
5515     >>> gn.specs
5516     OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
5517
5518     >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
5519     GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
5520     """
5521     __slots__ = ("specs",)
5522     tag_default = None
5523     asn1_type_name = "CHOICE"
5524
5525     def __init__(
5526             self,
5527             value=None,
5528             schema=None,
5529             impl=None,
5530             expl=None,
5531             default=None,
5532             optional=False,
5533             _decoded=(0, 0, 0),
5534     ):
5535         """
5536         :param value: set the value. Either ``(choice, value)`` tuple, or
5537                       :py:class:`pyderasn.Choice` object
5538         :param bytes impl: can not be set, do **not** use it
5539         :param bytes expl: override default tag with ``EXPLICIT`` one
5540         :param default: set default value. Type same as in ``value``
5541         :param bool optional: is object ``OPTIONAL`` in sequence
5542         """
5543         if impl is not None:
5544             raise ValueError("no implicit tag allowed for CHOICE")
5545         super().__init__(None, expl, default, optional, _decoded)
5546         if schema is None:
5547             schema = getattr(self, "schema", ())
5548         if len(schema) == 0:
5549             raise ValueError("schema must be specified")
5550         self.specs = (
5551             schema if schema.__class__ == OrderedDict else OrderedDict(schema)
5552         )
5553         self._value = None
5554         if value is not None:
5555             self._value = self._value_sanitize(value)
5556         if default is not None:
5557             default_value = self._value_sanitize(default)
5558             default_obj = self.__class__(impl=self.tag, expl=self._expl)
5559             default_obj.specs = self.specs
5560             default_obj._value = default_value
5561             self.default = default_obj
5562             if value is None:
5563                 self._value = copy(default_obj._value)
5564         if self._expl is not None:
5565             tag_class, _, tag_num = tag_decode(self._expl)
5566             self._tag_order = (tag_class, tag_num)
5567
5568     def _value_sanitize(self, value):
5569         if (value.__class__ == tuple) and len(value) == 2:
5570             choice, obj = value
5571             spec = self.specs.get(choice)
5572             if spec is None:
5573                 raise ObjUnknown(choice)
5574             if not isinstance(obj, spec.__class__):
5575                 raise InvalidValueType((spec,))
5576             return (choice, spec(obj))
5577         if isinstance(value, self.__class__):
5578             return value._value
5579         raise InvalidValueType((self.__class__, tuple))
5580
5581     @property
5582     def ready(self):
5583         return self._value is not None and self._value[1].ready
5584
5585     @property
5586     def bered(self):
5587         return self.expl_lenindef or (
5588             (self._value is not None) and
5589             self._value[1].bered
5590         )
5591
5592     def __getstate__(self):
5593         return ChoiceState(
5594             __version__,
5595             self.tag,
5596             self._tag_order,
5597             self._expl,
5598             self.default,
5599             self.optional,
5600             self.offset,
5601             self.llen,
5602             self.vlen,
5603             self.expl_lenindef,
5604             self.lenindef,
5605             self.ber_encoded,
5606             self.specs,
5607             copy(self._value),
5608         )
5609
5610     def __setstate__(self, state):
5611         super().__setstate__(state)
5612         self.specs = state.specs
5613         self._value = state.value
5614
5615     def __eq__(self, their):
5616         if (their.__class__ == tuple) and len(their) == 2:
5617             return self._value == their
5618         if not isinstance(their, self.__class__):
5619             return False
5620         return (
5621             self.specs == their.specs and
5622             self._value == their._value
5623         )
5624
5625     def __call__(
5626             self,
5627             value=None,
5628             expl=None,
5629             default=None,
5630             optional=None,
5631     ):
5632         return self.__class__(
5633             value=value,
5634             schema=self.specs,
5635             expl=self._expl if expl is None else expl,
5636             default=self.default if default is None else default,
5637             optional=self.optional if optional is None else optional,
5638         )
5639
5640     @property
5641     def choice(self):
5642         """Name of the choice
5643         """
5644         self._assert_ready()
5645         return self._value[0]
5646
5647     @property
5648     def value(self):
5649         """Value of underlying choice
5650         """
5651         self._assert_ready()
5652         return self._value[1]
5653
5654     @property
5655     def tag_order(self):
5656         self._assert_ready()
5657         return self._value[1].tag_order if self._tag_order is None else self._tag_order
5658
5659     @property
5660     def tag_order_cer(self):
5661         return min(v.tag_order_cer for v in self.specs.values())
5662
5663     def __getitem__(self, key):
5664         if key not in self.specs:
5665             raise ObjUnknown(key)
5666         if self._value is None:
5667             return None
5668         choice, value = self._value
5669         if choice != key:
5670             return None
5671         return value
5672
5673     def __setitem__(self, key, value):
5674         spec = self.specs.get(key)
5675         if spec is None:
5676             raise ObjUnknown(key)
5677         if not isinstance(value, spec.__class__):
5678             raise InvalidValueType((spec.__class__,))
5679         self._value = (key, spec(value))
5680
5681     @property
5682     def tlen(self):
5683         return 0
5684
5685     @property
5686     def decoded(self):
5687         return self._value[1].decoded if self.ready else False
5688
5689     def _encode(self):
5690         self._assert_ready()
5691         return self._value[1].encode()
5692
5693     def _encode1st(self, state):
5694         self._assert_ready()
5695         return self._value[1].encode1st(state)
5696
5697     def _encode2nd(self, writer, state_iter):
5698         self._value[1].encode2nd(writer, state_iter)
5699
5700     def _encode_cer(self, writer):
5701         self._assert_ready()
5702         self._value[1].encode_cer(writer)
5703
5704     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
5705         for choice, spec in self.specs.items():
5706             sub_decode_path = decode_path + (choice,)
5707             try:
5708                 spec.decode(
5709                     tlv,
5710                     offset=offset,
5711                     leavemm=True,
5712                     decode_path=sub_decode_path,
5713                     ctx=ctx,
5714                     tag_only=True,
5715                     _ctx_immutable=False,
5716                 )
5717             except TagMismatch:
5718                 continue
5719             break
5720         else:
5721             raise TagMismatch(
5722                 klass=self.__class__,
5723                 decode_path=decode_path,
5724                 offset=offset,
5725             )
5726         if tag_only:  # pragma: no cover
5727             yield None
5728             return
5729         if evgen_mode:
5730             for _decode_path, value, tail in spec.decode_evgen(
5731                     tlv,
5732                     offset=offset,
5733                     leavemm=True,
5734                     decode_path=sub_decode_path,
5735                     ctx=ctx,
5736                     _ctx_immutable=False,
5737             ):
5738                 yield _decode_path, value, tail
5739         else:
5740             _, value, tail = next(spec.decode_evgen(
5741                 tlv,
5742                 offset=offset,
5743                 leavemm=True,
5744                 decode_path=sub_decode_path,
5745                 ctx=ctx,
5746                 _ctx_immutable=False,
5747                 _evgen_mode=False,
5748             ))
5749         obj = self.__class__(
5750             schema=self.specs,
5751             expl=self._expl,
5752             default=self.default,
5753             optional=self.optional,
5754             _decoded=(offset, 0, value.fulllen),
5755         )
5756         obj._value = (choice, value)
5757         yield decode_path, obj, tail
5758
5759     def __repr__(self):
5760         value = pp_console_row(next(self.pps()))
5761         if self.ready:
5762             value = "%s[%r]" % (value, self.value)
5763         return value
5764
5765     def pps(self, decode_path=()):
5766         yield _pp(
5767             obj=self,
5768             asn1_type_name=self.asn1_type_name,
5769             obj_name=self.__class__.__name__,
5770             decode_path=decode_path,
5771             value=self.choice if self.ready else None,
5772             optional=self.optional,
5773             default=self == self.default,
5774             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5775             expl=None if self._expl is None else tag_decode(self._expl),
5776             offset=self.offset,
5777             tlen=self.tlen,
5778             llen=self.llen,
5779             vlen=self.vlen,
5780             expl_lenindef=self.expl_lenindef,
5781             bered=self.bered,
5782         )
5783         if self.ready:
5784             yield self.value.pps(decode_path=decode_path + (self.choice,))
5785         for pp in self.pps_lenindef(decode_path):
5786             yield pp
5787
5788
5789 class PrimitiveTypes(Choice):
5790     """Predefined ``CHOICE`` for all generic primitive types
5791
5792     It could be useful for general decoding of some unspecified values:
5793
5794     >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
5795     OCTET STRING 3 bytes 666f6f
5796     >>> PrimitiveTypes().decod(hexdec("0203123456")).value
5797     INTEGER 1193046
5798     """
5799     __slots__ = ()
5800     schema = tuple((klass.__name__, klass()) for klass in (
5801         Boolean,
5802         Integer,
5803         BitString,
5804         OctetString,
5805         Null,
5806         ObjectIdentifier,
5807         UTF8String,
5808         NumericString,
5809         PrintableString,
5810         TeletexString,
5811         VideotexString,
5812         IA5String,
5813         UTCTime,
5814         GeneralizedTime,
5815         GraphicString,
5816         VisibleString,
5817         ISO646String,
5818         GeneralString,
5819         UniversalString,
5820         BMPString,
5821     ))
5822
5823
5824 AnyState = namedtuple(
5825     "AnyState",
5826     BasicState._fields + ("value", "defined"),
5827     **NAMEDTUPLE_KWARGS
5828 )
5829
5830
5831 class Any(Obj):
5832     """``ANY`` special type
5833
5834     >>> Any(Integer(-123))
5835     ANY INTEGER -123 (0X:7B)
5836     >>> a = Any(OctetString(b"hello world").encode())
5837     ANY 040b68656c6c6f20776f726c64
5838     >>> hexenc(bytes(a))
5839     b'0x040x0bhello world'
5840     """
5841     __slots__ = ("defined",)
5842     tag_default = tag_encode(0)
5843     asn1_type_name = "ANY"
5844
5845     def __init__(
5846             self,
5847             value=None,
5848             expl=None,
5849             optional=False,
5850             _decoded=(0, 0, 0),
5851     ):
5852         """
5853         :param value: set the value. Either any kind of pyderasn's
5854                       **ready** object, or bytes. Pay attention that
5855                       **no** validation is performed if raw binary value
5856                       is valid TLV, except just tag decoding
5857         :param bytes expl: override default tag with ``EXPLICIT`` one
5858         :param bool optional: is object ``OPTIONAL`` in sequence
5859         """
5860         super().__init__(None, expl, None, optional, _decoded)
5861         if value is None:
5862             self._value = None
5863         else:
5864             value = self._value_sanitize(value)
5865             self._value = value
5866             if self._expl is None:
5867                 if value.__class__ == bytes or value.__class__ == memoryview:
5868                     tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5869                 else:
5870                     tag_class, tag_num = value.tag_order
5871             else:
5872                 tag_class, _, tag_num = tag_decode(self._expl)
5873             self._tag_order = (tag_class, tag_num)
5874         self.defined = None
5875
5876     def _value_sanitize(self, value):
5877         if value.__class__ == bytes or value.__class__ == memoryview:
5878             if len(value) == 0:
5879                 raise ValueError("%s value can not be empty" % self.__class__.__name__)
5880             return value
5881         if isinstance(value, self.__class__):
5882             return value._value
5883         if not isinstance(value, Obj):
5884             raise InvalidValueType((self.__class__, Obj, bytes))
5885         return value
5886
5887     @property
5888     def ready(self):
5889         return self._value is not None
5890
5891     @property
5892     def tag_order(self):
5893         self._assert_ready()
5894         return self._tag_order
5895
5896     @property
5897     def bered(self):
5898         if self.expl_lenindef or self.lenindef:
5899             return True
5900         if self.defined is None:
5901             return False
5902         return self.defined[1].bered
5903
5904     def __getstate__(self):
5905         return AnyState(
5906             __version__,
5907             self.tag,
5908             self._tag_order,
5909             self._expl,
5910             None,
5911             self.optional,
5912             self.offset,
5913             self.llen,
5914             self.vlen,
5915             self.expl_lenindef,
5916             self.lenindef,
5917             self.ber_encoded,
5918             self._value,
5919             self.defined,
5920         )
5921
5922     def __setstate__(self, state):
5923         super().__setstate__(state)
5924         self._value = state.value
5925         self.defined = state.defined
5926
5927     def __eq__(self, their):
5928         if their.__class__ == bytes or their.__class__ == memoryview:
5929             if self._value.__class__ == bytes or their.__class__ == memoryview:
5930                 return self._value == their
5931             return self._value.encode() == their
5932         if issubclass(their.__class__, Any):
5933             if self.ready and their.ready:
5934                 return self.memoryview() == their.memoryview()
5935             return self.ready == their.ready
5936         return False
5937
5938     def __call__(
5939             self,
5940             value=None,
5941             expl=None,
5942             optional=None,
5943     ):
5944         return self.__class__(
5945             value=value,
5946             expl=self._expl if expl is None else expl,
5947             optional=self.optional if optional is None else optional,
5948         )
5949
5950     def __bytes__(self):
5951         self._assert_ready()
5952         value = self._value
5953         if value.__class__ == bytes:
5954             return value
5955         if value.__class__ == memoryview:
5956             return bytes(value)
5957         return self._value.encode()
5958
5959     def memoryview(self):
5960         self._assert_ready()
5961         value = self._value
5962         if value.__class__ == memoryview:
5963             return memoryview(value)
5964         return memoryview(bytes(self))
5965
5966     @property
5967     def tlen(self):
5968         return 0
5969
5970     def _encode(self):
5971         self._assert_ready()
5972         value = self._value
5973         if value.__class__ == bytes or value.__class__ == memoryview:
5974             return bytes(self)
5975         return value.encode()
5976
5977     def _encode1st(self, state):
5978         self._assert_ready()
5979         value = self._value
5980         if value.__class__ == bytes or value.__class__ == memoryview:
5981             return len(value), state
5982         return value.encode1st(state)
5983
5984     def _encode2nd(self, writer, state_iter):
5985         value = self._value
5986         if value.__class__ == bytes or value.__class__ == memoryview:
5987             write_full(writer, value)
5988         else:
5989             value.encode2nd(writer, state_iter)
5990
5991     def _encode_cer(self, writer):
5992         self._assert_ready()
5993         value = self._value
5994         if value.__class__ == bytes or value.__class__ == memoryview:
5995             write_full(writer, value)
5996         else:
5997             value.encode_cer(writer)
5998
5999     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6000         try:
6001             t, tlen, lv = tag_strip(tlv)
6002         except DecodeError as err:
6003             raise err.__class__(
6004                 msg=err.msg,
6005                 klass=self.__class__,
6006                 decode_path=decode_path,
6007                 offset=offset,
6008             )
6009         try:
6010             l, llen, v = len_decode(lv)
6011         except LenIndefForm as err:
6012             if not ctx.get("bered", False):
6013                 raise err.__class__(
6014                     msg=err.msg,
6015                     klass=self.__class__,
6016                     decode_path=decode_path,
6017                     offset=offset,
6018                 )
6019             llen, vlen, v = 1, 0, lv[1:]
6020             sub_offset = offset + tlen + llen
6021             chunk_i = 0
6022             while v[:EOC_LEN].tobytes() != EOC:
6023                 chunk, v = Any().decode(
6024                     v,
6025                     offset=sub_offset,
6026                     decode_path=decode_path + (str(chunk_i),),
6027                     leavemm=True,
6028                     ctx=ctx,
6029                     _ctx_immutable=False,
6030                 )
6031                 vlen += chunk.tlvlen
6032                 sub_offset += chunk.tlvlen
6033                 chunk_i += 1
6034             tlvlen = tlen + llen + vlen + EOC_LEN
6035             obj = self.__class__(
6036                 value=None if evgen_mode else tlv[:tlvlen].tobytes(),
6037                 expl=self._expl,
6038                 optional=self.optional,
6039                 _decoded=(offset, 0, tlvlen),
6040             )
6041             obj.lenindef = True
6042             obj.tag = t.tobytes()
6043             yield decode_path, obj, v[EOC_LEN:]
6044             return
6045         except DecodeError as err:
6046             raise err.__class__(
6047                 msg=err.msg,
6048                 klass=self.__class__,
6049                 decode_path=decode_path,
6050                 offset=offset,
6051             )
6052         if l > len(v):
6053             raise NotEnoughData(
6054                 "encoded length is longer than data",
6055                 klass=self.__class__,
6056                 decode_path=decode_path,
6057                 offset=offset,
6058             )
6059         tlvlen = tlen + llen + l
6060         v, tail = tlv[:tlvlen], v[l:]
6061         if evgen_mode:
6062             value = None
6063         elif ctx.get("keep_memoryview", False):
6064             value = v
6065         else:
6066             value = v.tobytes()
6067         obj = self.__class__(
6068             value=value,
6069             expl=self._expl,
6070             optional=self.optional,
6071             _decoded=(offset, 0, tlvlen),
6072         )
6073         obj.tag = t.tobytes()
6074         yield decode_path, obj, tail
6075
6076     def __repr__(self):
6077         return pp_console_row(next(self.pps()))
6078
6079     def pps(self, decode_path=()):
6080         value = self._value
6081         if value is None:
6082             pass
6083         elif value.__class__ == bytes or value.__class__ == memoryview:
6084             value = None
6085         else:
6086             value = repr(value)
6087         yield _pp(
6088             obj=self,
6089             asn1_type_name=self.asn1_type_name,
6090             obj_name=self.__class__.__name__,
6091             decode_path=decode_path,
6092             value=value,
6093             blob=self._value if (
6094                 self._value.__class__ == bytes or
6095                 value.__class__ == memoryview
6096             ) else None,
6097             optional=self.optional,
6098             default=self == self.default,
6099             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6100             expl=None if self._expl is None else tag_decode(self._expl),
6101             offset=self.offset,
6102             tlen=self.tlen,
6103             llen=self.llen,
6104             vlen=self.vlen,
6105             expl_offset=self.expl_offset if self.expled else None,
6106             expl_tlen=self.expl_tlen if self.expled else None,
6107             expl_llen=self.expl_llen if self.expled else None,
6108             expl_vlen=self.expl_vlen if self.expled else None,
6109             expl_lenindef=self.expl_lenindef,
6110             lenindef=self.lenindef,
6111             bered=self.bered,
6112         )
6113         defined_by, defined = self.defined or (None, None)
6114         if defined_by is not None:
6115             yield defined.pps(
6116                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
6117             )
6118         for pp in self.pps_lenindef(decode_path):
6119             yield pp
6120
6121
6122 ########################################################################
6123 # ASN.1 constructed types
6124 ########################################################################
6125
6126 def abs_decode_path(decode_path, rel_path):
6127     """Create an absolute decode path from current and relative ones
6128
6129     :param decode_path: current decode path, starting point. Tuple of strings
6130     :param rel_path: relative path to ``decode_path``. Tuple of strings.
6131                      If first tuple's element is "/", then treat it as
6132                      an absolute path, ignoring ``decode_path`` as
6133                      starting point. Also this tuple can contain ".."
6134                      elements, stripping the leading element from
6135                      ``decode_path``
6136
6137     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
6138     ("foo", "bar", "baz", "whatever")
6139     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
6140     ("foo", "whatever")
6141     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
6142     ("baz", "whatever")
6143     """
6144     if rel_path[0] == "/":
6145         return rel_path[1:]
6146     if rel_path[0] == "..":
6147         return abs_decode_path(decode_path[:-1], rel_path[1:])
6148     return decode_path + rel_path
6149
6150
6151 SequenceState = namedtuple(
6152     "SequenceState",
6153     BasicState._fields + ("specs", "value",),
6154     **NAMEDTUPLE_KWARGS
6155 )
6156
6157
6158 class SequenceEncode1stMixin:
6159     __slots__ = ()
6160
6161     def _encode1st(self, state):
6162         state.append(0)
6163         idx = len(state) - 1
6164         vlen = 0
6165         for v in self._values_for_encoding():
6166             l, _ = v.encode1st(state)
6167             vlen += l
6168         state[idx] = vlen
6169         return len(self.tag) + len_size(vlen) + vlen, state
6170
6171
6172 class Sequence(SequenceEncode1stMixin, Obj):
6173     """``SEQUENCE`` structure type
6174
6175     You have to make specification of sequence::
6176
6177         class Extension(Sequence):
6178             schema = (
6179                 ("extnID", ObjectIdentifier()),
6180                 ("critical", Boolean(default=False)),
6181                 ("extnValue", OctetString()),
6182             )
6183
6184     Then, you can work with it as with dictionary.
6185
6186     >>> ext = Extension()
6187     >>> Extension().specs
6188     OrderedDict([
6189         ('extnID', OBJECT IDENTIFIER),
6190         ('critical', BOOLEAN False OPTIONAL DEFAULT),
6191         ('extnValue', OCTET STRING),
6192     ])
6193     >>> ext["extnID"] = "1.2.3"
6194     Traceback (most recent call last):
6195     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
6196     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
6197
6198     You can determine if sequence is ready to be encoded:
6199
6200     >>> ext.ready
6201     False
6202     >>> ext.encode()
6203     Traceback (most recent call last):
6204     pyderasn.ObjNotReady: object is not ready: extnValue
6205     >>> ext["extnValue"] = OctetString(b"foobar")
6206     >>> ext.ready
6207     True
6208
6209     Value you want to assign, must have the same **type** as in
6210     corresponding specification, but it can have different tags,
6211     optional/default attributes -- they will be taken from specification
6212     automatically::
6213
6214         class TBSCertificate(Sequence):
6215             schema = (
6216                 ("version", Version(expl=tag_ctxc(0), default="v1")),
6217             [...]
6218
6219     >>> tbs = TBSCertificate()
6220     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
6221
6222     Assign ``None`` to remove value from sequence.
6223
6224     You can set values in Sequence during its initialization:
6225
6226     >>> AlgorithmIdentifier((
6227         ("algorithm", ObjectIdentifier("1.2.3")),
6228         ("parameters", Any(Null()))
6229     ))
6230     AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
6231
6232     You can determine if value exists/set in the sequence and take its value:
6233
6234     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
6235     (True, True, False)
6236     >>> ext["extnID"]
6237     OBJECT IDENTIFIER 1.2.3
6238
6239     But pay attention that if value has default, then it won't be (not
6240     in) in the sequence (because ``DEFAULT`` must not be encoded in
6241     DER), but you can read its value:
6242
6243     >>> "critical" in ext, ext["critical"]
6244     (False, BOOLEAN False)
6245     >>> ext["critical"] = Boolean(True)
6246     >>> "critical" in ext, ext["critical"]
6247     (True, BOOLEAN True)
6248
6249     All defaulted values are always optional.
6250
6251     .. _allow_default_values_ctx:
6252
6253     DER prohibits default value encoding and will raise an error if
6254     default value is unexpectedly met during decode.
6255     If :ref:`bered <bered_ctx>` context option is set, then no error
6256     will be raised, but ``bered`` attribute set. You can disable strict
6257     defaulted values existence validation by setting
6258     ``"allow_default_values": True`` :ref:`context <ctx>` option.
6259
6260     All values with DEFAULT specified are decoded atomically in
6261     :ref:`evgen mode <evgen_mode>`. If DEFAULT value is some kind of
6262     SEQUENCE, then it will be yielded as a single element, not
6263     disassembled. That is required for DEFAULT existence check.
6264
6265     Two sequences are equal if they have equal specification (schema),
6266     implicit/explicit tagging and the same values.
6267     """
6268     __slots__ = ("specs",)
6269     tag_default = tag_encode(form=TagFormConstructed, num=16)
6270     asn1_type_name = "SEQUENCE"
6271
6272     def __init__(
6273             self,
6274             value=None,
6275             schema=None,
6276             impl=None,
6277             expl=None,
6278             default=None,
6279             optional=False,
6280             _decoded=(0, 0, 0),
6281     ):
6282         super().__init__(impl, expl, default, optional, _decoded)
6283         if schema is None:
6284             schema = getattr(self, "schema", ())
6285         self.specs = (
6286             schema if schema.__class__ == OrderedDict else OrderedDict(schema)
6287         )
6288         self._value = {}
6289         if value is not None:
6290             if issubclass(value.__class__, Sequence):
6291                 self._value = value._value
6292             elif hasattr(value, "__iter__"):
6293                 for seq_key, seq_value in value:
6294                     self[seq_key] = seq_value
6295             else:
6296                 raise InvalidValueType((Sequence,))
6297         if default is not None:
6298             if not issubclass(default.__class__, Sequence):
6299                 raise InvalidValueType((Sequence,))
6300             default_value = default._value
6301             default_obj = self.__class__(impl=self.tag, expl=self._expl)
6302             default_obj.specs = self.specs
6303             default_obj._value = default_value
6304             self.default = default_obj
6305             if value is None:
6306                 self._value = copy(default_obj._value)
6307
6308     @property
6309     def ready(self):
6310         for name, spec in self.specs.items():
6311             value = self._value.get(name)
6312             if value is None:
6313                 if spec.optional:
6314                     continue
6315                 return False
6316             if not value.ready:
6317                 return False
6318         return True
6319
6320     @property
6321     def bered(self):
6322         if self.expl_lenindef or self.lenindef or self.ber_encoded:
6323             return True
6324         return any(value.bered for value in self._value.values())
6325
6326     def __getstate__(self):
6327         return SequenceState(
6328             __version__,
6329             self.tag,
6330             self._tag_order,
6331             self._expl,
6332             self.default,
6333             self.optional,
6334             self.offset,
6335             self.llen,
6336             self.vlen,
6337             self.expl_lenindef,
6338             self.lenindef,
6339             self.ber_encoded,
6340             self.specs,
6341             {k: copy(v) for k, v in self._value.items()},
6342         )
6343
6344     def __setstate__(self, state):
6345         super().__setstate__(state)
6346         self.specs = state.specs
6347         self._value = state.value
6348
6349     def __eq__(self, their):
6350         if not isinstance(their, self.__class__):
6351             return False
6352         return (
6353             self.specs == their.specs and
6354             self.tag == their.tag and
6355             self._expl == their._expl and
6356             self._value == their._value
6357         )
6358
6359     def __call__(
6360             self,
6361             value=None,
6362             impl=None,
6363             expl=None,
6364             default=None,
6365             optional=None,
6366     ):
6367         return self.__class__(
6368             value=value,
6369             schema=self.specs,
6370             impl=self.tag if impl is None else impl,
6371             expl=self._expl if expl is None else expl,
6372             default=self.default if default is None else default,
6373             optional=self.optional if optional is None else optional,
6374         )
6375
6376     def __contains__(self, key):
6377         return key in self._value
6378
6379     def __setitem__(self, key, value):
6380         spec = self.specs.get(key)
6381         if spec is None:
6382             raise ObjUnknown(key)
6383         if value is None:
6384             self._value.pop(key, None)
6385             return
6386         if not isinstance(value, spec.__class__):
6387             raise InvalidValueType((spec.__class__,))
6388         value = spec(value=value)
6389         if spec.default is not None and value == spec.default:
6390             self._value.pop(key, None)
6391             return
6392         self._value[key] = value
6393
6394     def __getitem__(self, key):
6395         value = self._value.get(key)
6396         if value is not None:
6397             return value
6398         spec = self.specs.get(key)
6399         if spec is None:
6400             raise ObjUnknown(key)
6401         if spec.default is not None:
6402             return spec.default
6403         return None
6404
6405     def _values_for_encoding(self):
6406         for name, spec in self.specs.items():
6407             value = self._value.get(name)
6408             if value is None:
6409                 if spec.optional:
6410                     continue
6411                 raise ObjNotReady(name)
6412             yield value
6413
6414     def _encode(self):
6415         v = b"".join(v.encode() for v in self._values_for_encoding())
6416         return b"".join((self.tag, len_encode(len(v)), v))
6417
6418     def _encode2nd(self, writer, state_iter):
6419         write_full(writer, self.tag + len_encode(next(state_iter)))
6420         for v in self._values_for_encoding():
6421             v.encode2nd(writer, state_iter)
6422
6423     def _encode_cer(self, writer):
6424         write_full(writer, self.tag + LENINDEF)
6425         for v in self._values_for_encoding():
6426             v.encode_cer(writer)
6427         write_full(writer, EOC)
6428
6429     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6430         try:
6431             t, tlen, lv = tag_strip(tlv)
6432         except DecodeError as err:
6433             raise err.__class__(
6434                 msg=err.msg,
6435                 klass=self.__class__,
6436                 decode_path=decode_path,
6437                 offset=offset,
6438             )
6439         if t != self.tag:
6440             raise TagMismatch(
6441                 klass=self.__class__,
6442                 decode_path=decode_path,
6443                 offset=offset,
6444             )
6445         if tag_only:  # pragma: no cover
6446             yield None
6447             return
6448         lenindef = False
6449         ctx_bered = ctx.get("bered", False)
6450         try:
6451             l, llen, v = len_decode(lv)
6452         except LenIndefForm as err:
6453             if not ctx_bered:
6454                 raise err.__class__(
6455                     msg=err.msg,
6456                     klass=self.__class__,
6457                     decode_path=decode_path,
6458                     offset=offset,
6459                 )
6460             l, llen, v = 0, 1, lv[1:]
6461             lenindef = True
6462         except DecodeError as err:
6463             raise err.__class__(
6464                 msg=err.msg,
6465                 klass=self.__class__,
6466                 decode_path=decode_path,
6467                 offset=offset,
6468             )
6469         if l > len(v):
6470             raise NotEnoughData(
6471                 "encoded length is longer than data",
6472                 klass=self.__class__,
6473                 decode_path=decode_path,
6474                 offset=offset,
6475             )
6476         if not lenindef:
6477             v, tail = v[:l], v[l:]
6478         vlen = 0
6479         sub_offset = offset + tlen + llen
6480         values = {}
6481         ber_encoded = False
6482         ctx_allow_default_values = ctx.get("allow_default_values", False)
6483         for name, spec in self.specs.items():
6484             if spec.optional and (
6485                     (lenindef and v[:EOC_LEN].tobytes() == EOC) or
6486                     len(v) == 0
6487             ):
6488                 continue
6489             spec_defaulted = spec.default is not None
6490             sub_decode_path = decode_path + (name,)
6491             try:
6492                 if evgen_mode and not spec_defaulted:
6493                     for _decode_path, value, v_tail in spec.decode_evgen(
6494                             v,
6495                             sub_offset,
6496                             leavemm=True,
6497                             decode_path=sub_decode_path,
6498                             ctx=ctx,
6499                             _ctx_immutable=False,
6500                     ):
6501                         yield _decode_path, value, v_tail
6502                 else:
6503                     _, value, v_tail = next(spec.decode_evgen(
6504                         v,
6505                         sub_offset,
6506                         leavemm=True,
6507                         decode_path=sub_decode_path,
6508                         ctx=ctx,
6509                         _ctx_immutable=False,
6510                         _evgen_mode=False,
6511                     ))
6512             except TagMismatch as err:
6513                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
6514                     continue
6515                 raise
6516
6517             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
6518             if not evgen_mode and defined is not None:
6519                 defined_by, defined_spec = defined
6520                 if issubclass(value.__class__, SequenceOf):
6521                     for i, _value in enumerate(value):
6522                         sub_sub_decode_path = sub_decode_path + (
6523                             str(i),
6524                             DecodePathDefBy(defined_by),
6525                         )
6526                         defined_value, defined_tail = defined_spec.decode(
6527                             memoryview(bytes(_value)),
6528                             sub_offset + (
6529                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6530                                 if value.expled else (value.tlen + value.llen)
6531                             ),
6532                             leavemm=True,
6533                             decode_path=sub_sub_decode_path,
6534                             ctx=ctx,
6535                             _ctx_immutable=False,
6536                         )
6537                         if len(defined_tail) > 0:
6538                             raise DecodeError(
6539                                 "remaining data",
6540                                 klass=self.__class__,
6541                                 decode_path=sub_sub_decode_path,
6542                                 offset=offset,
6543                             )
6544                         _value.defined = (defined_by, defined_value)
6545                 else:
6546                     defined_value, defined_tail = defined_spec.decode(
6547                         memoryview(bytes(value)),
6548                         sub_offset + (
6549                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
6550                             if value.expled else (value.tlen + value.llen)
6551                         ),
6552                         leavemm=True,
6553                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6554                         ctx=ctx,
6555                         _ctx_immutable=False,
6556                     )
6557                     if len(defined_tail) > 0:
6558                         raise DecodeError(
6559                             "remaining data",
6560                             klass=self.__class__,
6561                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
6562                             offset=offset,
6563                         )
6564                     value.defined = (defined_by, defined_value)
6565
6566             value_len = value.fulllen
6567             vlen += value_len
6568             sub_offset += value_len
6569             v = v_tail
6570             if spec_defaulted:
6571                 if evgen_mode:
6572                     yield sub_decode_path, value, v_tail
6573                 if value == spec.default:
6574                     if ctx_bered or ctx_allow_default_values:
6575                         ber_encoded = True
6576                     else:
6577                         raise DecodeError(
6578                             "DEFAULT value met",
6579                             klass=self.__class__,
6580                             decode_path=sub_decode_path,
6581                             offset=sub_offset,
6582                         )
6583             if not evgen_mode:
6584                 values[name] = value
6585                 spec_defines = getattr(spec, "defines", ())
6586                 if len(spec_defines) == 0:
6587                     defines_by_path = ctx.get("defines_by_path", ())
6588                     if len(defines_by_path) > 0:
6589                         spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
6590                 if spec_defines is not None and len(spec_defines) > 0:
6591                     for rel_path, schema in spec_defines:
6592                         defined = schema.get(value, None)
6593                         if defined is not None:
6594                             ctx.setdefault("_defines", []).append((
6595                                 abs_decode_path(sub_decode_path[:-1], rel_path),
6596                                 (value, defined),
6597                             ))
6598         if lenindef:
6599             if v[:EOC_LEN].tobytes() != EOC:
6600                 raise DecodeError(
6601                     "no EOC",
6602                     klass=self.__class__,
6603                     decode_path=decode_path,
6604                     offset=offset,
6605                 )
6606             tail = v[EOC_LEN:]
6607             vlen += EOC_LEN
6608         elif len(v) > 0:
6609             raise DecodeError(
6610                 "remaining data",
6611                 klass=self.__class__,
6612                 decode_path=decode_path,
6613                 offset=offset,
6614             )
6615         obj = self.__class__(
6616             schema=self.specs,
6617             impl=self.tag,
6618             expl=self._expl,
6619             default=self.default,
6620             optional=self.optional,
6621             _decoded=(offset, llen, vlen),
6622         )
6623         obj._value = values
6624         obj.lenindef = lenindef
6625         obj.ber_encoded = ber_encoded
6626         yield decode_path, obj, tail
6627
6628     def __repr__(self):
6629         value = pp_console_row(next(self.pps()))
6630         cols = []
6631         for name in self.specs:
6632             _value = self._value.get(name)
6633             if _value is None:
6634                 continue
6635             cols.append("%s: %s" % (name, repr(_value)))
6636         return "%s[%s]" % (value, "; ".join(cols))
6637
6638     def pps(self, decode_path=()):
6639         yield _pp(
6640             obj=self,
6641             asn1_type_name=self.asn1_type_name,
6642             obj_name=self.__class__.__name__,
6643             decode_path=decode_path,
6644             optional=self.optional,
6645             default=self == self.default,
6646             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
6647             expl=None if self._expl is None else tag_decode(self._expl),
6648             offset=self.offset,
6649             tlen=self.tlen,
6650             llen=self.llen,
6651             vlen=self.vlen,
6652             expl_offset=self.expl_offset if self.expled else None,
6653             expl_tlen=self.expl_tlen if self.expled else None,
6654             expl_llen=self.expl_llen if self.expled else None,
6655             expl_vlen=self.expl_vlen if self.expled else None,
6656             expl_lenindef=self.expl_lenindef,
6657             lenindef=self.lenindef,
6658             ber_encoded=self.ber_encoded,
6659             bered=self.bered,
6660         )
6661         for name in self.specs:
6662             value = self._value.get(name)
6663             if value is None:
6664                 continue
6665             yield value.pps(decode_path=decode_path + (name,))
6666         for pp in self.pps_lenindef(decode_path):
6667             yield pp
6668
6669
6670 class Set(Sequence, SequenceEncode1stMixin):
6671     """``SET`` structure type
6672
6673     Its usage is identical to :py:class:`pyderasn.Sequence`.
6674
6675     .. _allow_unordered_set_ctx:
6676
6677     DER prohibits unordered values encoding and will raise an error
6678     during decode. If :ref:`bered <bered_ctx>` context option is set,
6679     then no error will occur. Also you can disable strict values
6680     ordering check by setting ``"allow_unordered_set": True``
6681     :ref:`context <ctx>` option.
6682     """
6683     __slots__ = ()
6684     tag_default = tag_encode(form=TagFormConstructed, num=17)
6685     asn1_type_name = "SET"
6686
6687     def _values_for_encoding(self):
6688         return sorted(super()._values_for_encoding(), key=attrgetter("tag_order"))
6689
6690     def _encode_cer(self, writer):
6691         write_full(writer, self.tag + LENINDEF)
6692         for v in sorted(
6693                 super()._values_for_encoding(),
6694                 key=attrgetter("tag_order_cer"),
6695         ):
6696             v.encode_cer(writer)
6697         write_full(writer, EOC)
6698
6699     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
6700         try:
6701             t, tlen, lv = tag_strip(tlv)
6702         except DecodeError as err:
6703             raise err.__class__(
6704                 msg=err.msg,
6705                 klass=self.__class__,
6706                 decode_path=decode_path,
6707                 offset=offset,
6708             )
6709         if t != self.tag:
6710             raise TagMismatch(
6711                 klass=self.__class__,
6712                 decode_path=decode_path,
6713                 offset=offset,
6714             )
6715         if tag_only:
6716             yield None
6717             return
6718         lenindef = False
6719         ctx_bered = ctx.get("bered", False)
6720         try:
6721             l, llen, v = len_decode(lv)
6722         except LenIndefForm as err:
6723             if not ctx_bered:
6724                 raise err.__class__(
6725                     msg=err.msg,
6726                     klass=self.__class__,
6727                     decode_path=decode_path,
6728                     offset=offset,
6729                 )
6730             l, llen, v = 0, 1, lv[1:]
6731             lenindef = True
6732         except DecodeError as err:
6733             raise err.__class__(
6734                 msg=err.msg,
6735                 klass=self.__class__,
6736                 decode_path=decode_path,
6737                 offset=offset,
6738             )
6739         if l > len(v):
6740             raise NotEnoughData(
6741                 "encoded length is longer than data",
6742                 klass=self.__class__,
6743                 offset=offset,
6744             )
6745         if not lenindef:
6746             v, tail = v[:l], v[l:]
6747         vlen = 0
6748         sub_offset = offset + tlen + llen
6749         values = {}
6750         ber_encoded = False
6751         ctx_allow_default_values = ctx.get("allow_default_values", False)
6752         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
6753         tag_order_prev = (0, 0)
6754         _specs_items = copy(self.specs)
6755
6756         while len(v) > 0:
6757             if lenindef and v[:EOC_LEN].tobytes() == EOC:
6758                 break
6759             for name, spec in _specs_items.items():
6760                 sub_decode_path = decode_path + (name,)
6761                 try:
6762                     spec.decode(
6763                         v,
6764                         sub_offset,
6765                         leavemm=True,
6766                         decode_path=sub_decode_path,
6767                         ctx=ctx,
6768                         tag_only=True,
6769                         _ctx_immutable=False,
6770                     )
6771                 except TagMismatch:
6772                     continue
6773                 break
6774             else:
6775                 raise TagMismatch(
6776                     klass=self.__class__,
6777                     decode_path=decode_path,
6778                     offset=offset,
6779                 )
6780             spec_defaulted = spec.default is not None
6781             if evgen_mode and not spec_defaulted:
6782                 for _decode_path, value, v_tail in spec.decode_evgen(
6783                         v,
6784                         sub_offset,
6785                         leavemm=True,
6786                         decode_path=sub_decode_path,
6787                         ctx=ctx,
6788                         _ctx_immutable=False,
6789                 ):
6790                     yield _decode_path, value, v_tail
6791             else:
6792                 _, value, v_tail = next(spec.decode_evgen(
6793                     v,
6794                     sub_offset,
6795                     leavemm=True,
6796                     decode_path=sub_decode_path,
6797                     ctx=ctx,
6798                     _ctx_immutable=False,
6799                     _evgen_mode=False,
6800                 ))
6801             value_tag_order = value.tag_order
6802             value_len = value.fulllen
6803             if tag_order_prev >= value_tag_order:
6804                 if ctx_bered or ctx_allow_unordered_set:
6805                     ber_encoded = True
6806                 else:
6807                     raise DecodeError(
6808                         "unordered " + self.asn1_type_name,
6809                         klass=self.__class__,
6810                         decode_path=sub_decode_path,
6811                         offset=sub_offset,
6812                     )
6813             if spec_defaulted:
6814                 if evgen_mode:
6815                     yield sub_decode_path, value, v_tail
6816                 if value != spec.default:
6817                     pass
6818                 elif ctx_bered or ctx_allow_default_values:
6819                     ber_encoded = True
6820                 else:
6821                     raise DecodeError(
6822                         "DEFAULT value met",
6823                         klass=self.__class__,
6824                         decode_path=sub_decode_path,
6825                         offset=sub_offset,
6826                     )
6827             values[name] = value
6828             del _specs_items[name]
6829             tag_order_prev = value_tag_order
6830             sub_offset += value_len
6831             vlen += value_len
6832             v = v_tail
6833
6834         obj = self.__class__(
6835             schema=self.specs,
6836             impl=self.tag,
6837             expl=self._expl,
6838             default=self.default,
6839             optional=self.optional,
6840             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
6841         )
6842         if lenindef:
6843             if v[:EOC_LEN].tobytes() != EOC:
6844                 raise DecodeError(
6845                     "no EOC",
6846                     klass=self.__class__,
6847                     decode_path=decode_path,
6848                     offset=offset,
6849                 )
6850             tail = v[EOC_LEN:]
6851             obj.lenindef = True
6852         for name, spec in self.specs.items():
6853             if name not in values and not spec.optional:
6854                 raise DecodeError(
6855                     "%s value is not ready" % name,
6856                     klass=self.__class__,
6857                     decode_path=decode_path,
6858                     offset=offset,
6859                 )
6860         if not evgen_mode:
6861             obj._value = values
6862         obj.ber_encoded = ber_encoded
6863         yield decode_path, obj, tail
6864
6865
6866 SequenceOfState = namedtuple(
6867     "SequenceOfState",
6868     BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
6869     **NAMEDTUPLE_KWARGS
6870 )
6871
6872
6873 class SequenceOf(SequenceEncode1stMixin, Obj):
6874     """``SEQUENCE OF`` sequence type
6875
6876     For that kind of type you must specify the object it will carry on
6877     (bounds are for example here, not required)::
6878
6879         class Ints(SequenceOf):
6880             schema = Integer()
6881             bounds = (0, 2)
6882
6883     >>> ints = Ints()
6884     >>> ints.append(Integer(123))
6885     >>> ints.append(Integer(234))
6886     >>> ints
6887     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
6888     >>> [int(i) for i in ints]
6889     [123, 234]
6890     >>> ints.append(Integer(345))
6891     Traceback (most recent call last):
6892     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
6893     >>> ints[1]
6894     INTEGER 234
6895     >>> ints[1] = Integer(345)
6896     >>> ints
6897     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
6898
6899     You can initialize sequence with preinitialized values:
6900
6901     >>> ints = Ints([Integer(123), Integer(234)])
6902
6903     Also you can use iterator as a value:
6904
6905     >>> ints = Ints(iter(Integer(i) for i in range(1000000)))
6906
6907     And it won't be iterated until encoding process. Pay attention that
6908     bounds and required schema checks are done only during the encoding
6909     process in that case! After encode was called, then value is zeroed
6910     back to empty list and you have to set it again. That mode is useful
6911     mainly with CER encoding mode, where all objects from the iterable
6912     will be streamed to the buffer, without copying all of them to
6913     memory first.
6914     """
6915     __slots__ = ("spec", "_bound_min", "_bound_max")
6916     tag_default = tag_encode(form=TagFormConstructed, num=16)
6917     asn1_type_name = "SEQUENCE OF"
6918
6919     def __init__(
6920             self,
6921             value=None,
6922             schema=None,
6923             bounds=None,
6924             impl=None,
6925             expl=None,
6926             default=None,
6927             optional=False,
6928             _decoded=(0, 0, 0),
6929     ):
6930         super().__init__(impl, expl, default, optional, _decoded)
6931         if schema is None:
6932             schema = getattr(self, "schema", None)
6933         if schema is None:
6934             raise ValueError("schema must be specified")
6935         self.spec = schema
6936         self._bound_min, self._bound_max = getattr(
6937             self,
6938             "bounds",
6939             (0, float("+inf")),
6940         ) if bounds is None else bounds
6941         self._value = []
6942         if value is not None:
6943             self._value = self._value_sanitize(value)
6944         if default is not None:
6945             default_value = self._value_sanitize(default)
6946             default_obj = self.__class__(
6947                 schema=schema,
6948                 impl=self.tag,
6949                 expl=self._expl,
6950             )
6951             default_obj._value = default_value
6952             self.default = default_obj
6953             if value is None:
6954                 self._value = copy(default_obj._value)
6955
6956     def _value_sanitize(self, value):
6957         iterator = False
6958         if issubclass(value.__class__, SequenceOf):
6959             value = value._value
6960         elif hasattr(value, NEXT_ATTR_NAME):
6961             iterator = True
6962         elif hasattr(value, "__iter__"):
6963             value = list(value)
6964         else:
6965             raise InvalidValueType((self.__class__, iter, "iterator"))
6966         if not iterator:
6967             if not self._bound_min <= len(value) <= self._bound_max:
6968                 raise BoundsError(self._bound_min, len(value), self._bound_max)
6969             class_expected = self.spec.__class__
6970             for v in value:
6971                 if not isinstance(v, class_expected):
6972                     raise InvalidValueType((class_expected,))
6973         return value
6974
6975     @property
6976     def ready(self):
6977         if hasattr(self._value, NEXT_ATTR_NAME):
6978             return True
6979         if self._bound_min > 0 and len(self._value) == 0:
6980             return False
6981         return all(v.ready for v in self._value)
6982
6983     @property
6984     def bered(self):
6985         if self.expl_lenindef or self.lenindef or self.ber_encoded:
6986             return True
6987         return any(v.bered for v in self._value)
6988
6989     def __getstate__(self):
6990         if hasattr(self._value, NEXT_ATTR_NAME):
6991             raise ValueError("can not pickle SequenceOf with iterator")
6992         return SequenceOfState(
6993             __version__,
6994             self.tag,
6995             self._tag_order,
6996             self._expl,
6997             self.default,
6998             self.optional,
6999             self.offset,
7000             self.llen,
7001             self.vlen,
7002             self.expl_lenindef,
7003             self.lenindef,
7004             self.ber_encoded,
7005             self.spec,
7006             [copy(v) for v in self._value],
7007             self._bound_min,
7008             self._bound_max,
7009         )
7010
7011     def __setstate__(self, state):
7012         super().__setstate__(state)
7013         self.spec = state.spec
7014         self._value = state.value
7015         self._bound_min = state.bound_min
7016         self._bound_max = state.bound_max
7017
7018     def __eq__(self, their):
7019         if isinstance(their, self.__class__):
7020             return (
7021                 self.spec == their.spec and
7022                 self.tag == their.tag and
7023                 self._expl == their._expl and
7024                 self._value == their._value
7025             )
7026         if hasattr(their, "__iter__"):
7027             return self._value == list(their)
7028         return False
7029
7030     def __call__(
7031             self,
7032             value=None,
7033             bounds=None,
7034             impl=None,
7035             expl=None,
7036             default=None,
7037             optional=None,
7038     ):
7039         return self.__class__(
7040             value=value,
7041             schema=self.spec,
7042             bounds=(
7043                 (self._bound_min, self._bound_max)
7044                 if bounds is None else bounds
7045             ),
7046             impl=self.tag if impl is None else impl,
7047             expl=self._expl if expl is None else expl,
7048             default=self.default if default is None else default,
7049             optional=self.optional if optional is None else optional,
7050         )
7051
7052     def __contains__(self, key):
7053         return key in self._value
7054
7055     def append(self, value):
7056         if not isinstance(value, self.spec.__class__):
7057             raise InvalidValueType((self.spec.__class__,))
7058         if len(self._value) + 1 > self._bound_max:
7059             raise BoundsError(
7060                 self._bound_min,
7061                 len(self._value) + 1,
7062                 self._bound_max,
7063             )
7064         self._value.append(value)
7065
7066     def __iter__(self):
7067         return iter(self._value)
7068
7069     def __len__(self):
7070         return len(self._value)
7071
7072     def __setitem__(self, key, value):
7073         if not isinstance(value, self.spec.__class__):
7074             raise InvalidValueType((self.spec.__class__,))
7075         self._value[key] = self.spec(value=value)
7076
7077     def __getitem__(self, key):
7078         return self._value[key]
7079
7080     def _values_for_encoding(self):
7081         return iter(self._value)
7082
7083     def _encode(self):
7084         iterator = hasattr(self._value, NEXT_ATTR_NAME)
7085         if iterator:
7086             values = []
7087             values_append = values.append
7088             class_expected = self.spec.__class__
7089             values_for_encoding = self._values_for_encoding()
7090             self._value = []
7091             for v in values_for_encoding:
7092                 if not isinstance(v, class_expected):
7093                     raise InvalidValueType((class_expected,))
7094                 values_append(v.encode())
7095             if not self._bound_min <= len(values) <= self._bound_max:
7096                 raise BoundsError(self._bound_min, len(values), self._bound_max)
7097             value = b"".join(values)
7098         else:
7099             value = b"".join(v.encode() for v in self._values_for_encoding())
7100         return b"".join((self.tag, len_encode(len(value)), value))
7101
7102     def _encode1st(self, state):
7103         state = super()._encode1st(state)
7104         if hasattr(self._value, NEXT_ATTR_NAME):
7105             self._value = []
7106         return state
7107
7108     def _encode2nd(self, writer, state_iter):
7109         write_full(writer, self.tag + len_encode(next(state_iter)))
7110         iterator = hasattr(self._value, NEXT_ATTR_NAME)
7111         if iterator:
7112             values_count = 0
7113             class_expected = self.spec.__class__
7114             values_for_encoding = self._values_for_encoding()
7115             self._value = []
7116             for v in values_for_encoding:
7117                 if not isinstance(v, class_expected):
7118                     raise InvalidValueType((class_expected,))
7119                 v.encode2nd(writer, state_iter)
7120                 values_count += 1
7121             if not self._bound_min <= values_count <= self._bound_max:
7122                 raise BoundsError(self._bound_min, values_count, self._bound_max)
7123         else:
7124             for v in self._values_for_encoding():
7125                 v.encode2nd(writer, state_iter)
7126
7127     def _encode_cer(self, writer):
7128         write_full(writer, self.tag + LENINDEF)
7129         iterator = hasattr(self._value, NEXT_ATTR_NAME)
7130         if iterator:
7131             class_expected = self.spec.__class__
7132             values_count = 0
7133             values_for_encoding = self._values_for_encoding()
7134             self._value = []
7135             for v in values_for_encoding:
7136                 if not isinstance(v, class_expected):
7137                     raise InvalidValueType((class_expected,))
7138                 v.encode_cer(writer)
7139                 values_count += 1
7140             if not self._bound_min <= values_count <= self._bound_max:
7141                 raise BoundsError(self._bound_min, values_count, self._bound_max)
7142         else:
7143             for v in self._values_for_encoding():
7144                 v.encode_cer(writer)
7145         write_full(writer, EOC)
7146
7147     def _decode(
7148             self,
7149             tlv,
7150             offset,
7151             decode_path,
7152             ctx,
7153             tag_only,
7154             evgen_mode,
7155             ordering_check=False,
7156     ):
7157         try:
7158             t, tlen, lv = tag_strip(tlv)
7159         except DecodeError as err:
7160             raise err.__class__(
7161                 msg=err.msg,
7162                 klass=self.__class__,
7163                 decode_path=decode_path,
7164                 offset=offset,
7165             )
7166         if t != self.tag:
7167             raise TagMismatch(
7168                 klass=self.__class__,
7169                 decode_path=decode_path,
7170                 offset=offset,
7171             )
7172         if tag_only:
7173             yield None
7174             return
7175         lenindef = False
7176         ctx_bered = ctx.get("bered", False)
7177         try:
7178             l, llen, v = len_decode(lv)
7179         except LenIndefForm as err:
7180             if not ctx_bered:
7181                 raise err.__class__(
7182                     msg=err.msg,
7183                     klass=self.__class__,
7184                     decode_path=decode_path,
7185                     offset=offset,
7186                 )
7187             l, llen, v = 0, 1, lv[1:]
7188             lenindef = True
7189         except DecodeError as err:
7190             raise err.__class__(
7191                 msg=err.msg,
7192                 klass=self.__class__,
7193                 decode_path=decode_path,
7194                 offset=offset,
7195             )
7196         if l > len(v):
7197             raise NotEnoughData(
7198                 "encoded length is longer than data",
7199                 klass=self.__class__,
7200                 decode_path=decode_path,
7201                 offset=offset,
7202             )
7203         if not lenindef:
7204             v, tail = v[:l], v[l:]
7205         vlen = 0
7206         sub_offset = offset + tlen + llen
7207         _value = []
7208         _value_count = 0
7209         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
7210         value_prev = memoryview(v[:0])
7211         ber_encoded = False
7212         spec = self.spec
7213         while len(v) > 0:
7214             if lenindef and v[:EOC_LEN].tobytes() == EOC:
7215                 break
7216             sub_decode_path = decode_path + (str(_value_count),)
7217             if evgen_mode:
7218                 for _decode_path, value, v_tail in spec.decode_evgen(
7219                         v,
7220                         sub_offset,
7221                         leavemm=True,
7222                         decode_path=sub_decode_path,
7223                         ctx=ctx,
7224                         _ctx_immutable=False,
7225                 ):
7226                     yield _decode_path, value, v_tail
7227             else:
7228                 _, value, v_tail = next(spec.decode_evgen(
7229                     v,
7230                     sub_offset,
7231                     leavemm=True,
7232                     decode_path=sub_decode_path,
7233                     ctx=ctx,
7234                     _ctx_immutable=False,
7235                     _evgen_mode=False,
7236                 ))
7237             value_len = value.fulllen
7238             if ordering_check:
7239                 if value_prev.tobytes() > v[:value_len].tobytes():
7240                     if ctx_bered or ctx_allow_unordered_set:
7241                         ber_encoded = True
7242                     else:
7243                         raise DecodeError(
7244                             "unordered " + self.asn1_type_name,
7245                             klass=self.__class__,
7246                             decode_path=sub_decode_path,
7247                             offset=sub_offset,
7248                         )
7249                 value_prev = v[:value_len]
7250             _value_count += 1
7251             if not evgen_mode:
7252                 _value.append(value)
7253             sub_offset += value_len
7254             vlen += value_len
7255             v = v_tail
7256         if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
7257             raise DecodeError(
7258                 msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
7259                 klass=self.__class__,
7260                 decode_path=decode_path,
7261                 offset=offset,
7262             )
7263         try:
7264             obj = self.__class__(
7265                 value=None if evgen_mode else _value,
7266                 schema=spec,
7267                 bounds=(self._bound_min, self._bound_max),
7268                 impl=self.tag,
7269                 expl=self._expl,
7270                 default=self.default,
7271                 optional=self.optional,
7272                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
7273             )
7274         except BoundsError as err:
7275             raise DecodeError(
7276                 msg=str(err),
7277                 klass=self.__class__,
7278                 decode_path=decode_path,
7279                 offset=offset,
7280             )
7281         if lenindef:
7282             if v[:EOC_LEN].tobytes() != EOC:
7283                 raise DecodeError(
7284                     "no EOC",
7285                     klass=self.__class__,
7286                     decode_path=decode_path,
7287                     offset=offset,
7288                 )
7289             obj.lenindef = True
7290             tail = v[EOC_LEN:]
7291         obj.ber_encoded = ber_encoded
7292         yield decode_path, obj, tail
7293
7294     def __repr__(self):
7295         return "%s[%s]" % (
7296             pp_console_row(next(self.pps())),
7297             ", ".join(repr(v) for v in self._value),
7298         )
7299
7300     def pps(self, decode_path=()):
7301         yield _pp(
7302             obj=self,
7303             asn1_type_name=self.asn1_type_name,
7304             obj_name=self.__class__.__name__,
7305             decode_path=decode_path,
7306             optional=self.optional,
7307             default=self == self.default,
7308             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
7309             expl=None if self._expl is None else tag_decode(self._expl),
7310             offset=self.offset,
7311             tlen=self.tlen,
7312             llen=self.llen,
7313             vlen=self.vlen,
7314             expl_offset=self.expl_offset if self.expled else None,
7315             expl_tlen=self.expl_tlen if self.expled else None,
7316             expl_llen=self.expl_llen if self.expled else None,
7317             expl_vlen=self.expl_vlen if self.expled else None,
7318             expl_lenindef=self.expl_lenindef,
7319             lenindef=self.lenindef,
7320             ber_encoded=self.ber_encoded,
7321             bered=self.bered,
7322         )
7323         for i, value in enumerate(self._value):
7324             yield value.pps(decode_path=decode_path + (str(i),))
7325         for pp in self.pps_lenindef(decode_path):
7326             yield pp
7327
7328
7329 class SetOf(SequenceOf):
7330     """``SET OF`` sequence type
7331
7332     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
7333     """
7334     __slots__ = ()
7335     tag_default = tag_encode(form=TagFormConstructed, num=17)
7336     asn1_type_name = "SET OF"
7337
7338     def _value_sanitize(self, value):
7339         value = super()._value_sanitize(value)
7340         if hasattr(value, NEXT_ATTR_NAME):
7341             raise ValueError(
7342                 "SetOf does not support iterator values, as no sense in them"
7343             )
7344         return value
7345
7346     def _encode(self):
7347         v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
7348         return b"".join((self.tag, len_encode(len(v)), v))
7349
7350     def _encode2nd(self, writer, state_iter):
7351         write_full(writer, self.tag + len_encode(next(state_iter)))
7352         values = []
7353         for v in self._values_for_encoding():
7354             buf = BytesIO()
7355             v.encode2nd(buf.write, state_iter)
7356             values.append(buf.getvalue())
7357         values.sort()
7358         for v in values:
7359             write_full(writer, v)
7360
7361     def _encode_cer(self, writer):
7362         write_full(writer, self.tag + LENINDEF)
7363         for v in sorted(encode_cer(v) for v in self._values_for_encoding()):
7364             write_full(writer, v)
7365         write_full(writer, EOC)
7366
7367     def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
7368         return super()._decode(
7369             tlv,
7370             offset,
7371             decode_path,
7372             ctx,
7373             tag_only,
7374             evgen_mode,
7375             ordering_check=True,
7376         )
7377
7378
7379 def obj_by_path(pypath):  # pragma: no cover
7380     """Import object specified as string Python path
7381
7382     Modules must be separated from classes/functions with ``:``.
7383
7384     >>> obj_by_path("foo.bar:Baz")
7385     <class 'foo.bar.Baz'>
7386     >>> obj_by_path("foo.bar:Baz.boo")
7387     <classmethod 'foo.bar.Baz.boo'>
7388     """
7389     mod, objs = pypath.rsplit(":", 1)
7390     from importlib import import_module
7391     obj = import_module(mod)
7392     for obj_name in objs.split("."):
7393         obj = getattr(obj, obj_name)
7394     return obj
7395
7396
7397 def generic_decoder():  # pragma: no cover
7398     # All of this below is a big hack with self references
7399     choice = PrimitiveTypes()
7400     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
7401     choice.specs["SetOf"] = SetOf(schema=choice)
7402     for i in range(31):
7403         choice.specs["SequenceOf%d" % i] = SequenceOf(
7404             schema=choice,
7405             expl=tag_ctxc(i),
7406         )
7407     choice.specs["Any"] = Any()
7408
7409     # Class name equals to type name, to omit it from output
7410     class SEQUENCEOF(SequenceOf):
7411         __slots__ = ()
7412         schema = choice
7413
7414     def pprint_any(
7415             obj,
7416             oid_maps=(),
7417             with_colours=False,
7418             with_decode_path=False,
7419             decode_path_only=(),
7420             decode_path=(),
7421     ):
7422         def _pprint_pps(pps):
7423             for pp in pps:
7424                 if hasattr(pp, "_fields"):
7425                     if (
7426                             decode_path_only != () and
7427                             pp.decode_path[:len(decode_path_only)] != decode_path_only
7428                     ):
7429                         continue
7430                     if pp.asn1_type_name == Choice.asn1_type_name:
7431                         continue
7432                     pp_kwargs = pp._asdict()
7433                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
7434                     pp = _pp(**pp_kwargs)
7435                     yield pp_console_row(
7436                         pp,
7437                         oid_maps=oid_maps,
7438                         with_offsets=True,
7439                         with_blob=False,
7440                         with_colours=with_colours,
7441                         with_decode_path=with_decode_path,
7442                         decode_path_len_decrease=len(decode_path_only),
7443                     )
7444                     for row in pp_console_blob(
7445                             pp,
7446                             decode_path_len_decrease=len(decode_path_only),
7447                     ):
7448                         yield row
7449                 else:
7450                     for row in _pprint_pps(pp):
7451                         yield row
7452         return "\n".join(_pprint_pps(obj.pps(decode_path)))
7453     return SEQUENCEOF(), pprint_any
7454
7455
7456 def ascii_visualize(ba):
7457     """Output only ASCII printable characters, like in hexdump -C
7458
7459     Example output for given binary string (right part)::
7460
7461         92 2b 39 20 65 91 e6 8e  95 93 1a 58 df 02 78 ea  |.+9 e......X..x.|
7462                                                            ^^^^^^^^^^^^^^^^
7463     """
7464     return "".join((chr(b) if 0x20 <= b <= 0x7E else ".") for b in ba)
7465
7466
7467 def hexdump(raw):
7468     """Generate ``hexdump -C`` like output
7469
7470     Rendered example::
7471
7472         00000000  30 80 30 80 a0 80 02 01  02 00 00 02 14 54 a5 18  |0.0..........T..|
7473         00000010  69 ef 8b 3f 15 fd ea ad  bd 47 e0 94 81 6b 06 6a  |i..?.....G...k.j|
7474
7475     Result of that function is a generator of lines, where each line is
7476     a list of columns::
7477
7478         [
7479             [...],
7480             ["00000010 ", " 69", " ef", " 8b", " 3f", " 15", " fd", " ea", " ad ",
7481                           " bd", " 47", " e0", " 94", " 81", " 6b", " 06", " 6a ",
7482                           " |i..?.....G...k.j|"]
7483             [...],
7484         ]
7485     """
7486     hexed = hexenc(raw).upper()
7487     addr, cols = 0, ["%08x " % 0]
7488     for i in range(0, len(hexed), 2):
7489         if i != 0 and i // 2 % 8 == 0:
7490             cols[-1] += " "
7491         if i != 0 and i // 2 % 16 == 0:
7492             cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:addr + 16])))
7493             yield cols
7494             addr += 16
7495             cols = ["%08x " % addr]
7496         cols.append(" " + hexed[i:i + 2])
7497     if len(cols) > 0:
7498         cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:])))
7499         yield cols
7500
7501
7502 def browse(raw, obj, oid_maps=()):
7503     """Interactive browser
7504
7505     :param bytes raw: binary data you decoded
7506     :param obj: decoded :py:class:`pyderasn.Obj`
7507     :param oid_maps: list of ``str(OID) <-> human readable string`` dictionaries.
7508                      Its human readable form is printed when OID is met
7509
7510     .. note:: `urwid <http://urwid.org/>`__ dependency required
7511
7512     This browser is an interactive terminal application for browsing
7513     structures of your decoded ASN.1 objects. You can quit it with **q**
7514     key. It consists of three windows:
7515
7516     :tree:
7517      View of ASN.1 elements hierarchy. You can navigate it using **Up**,
7518      **Down**, **PageUp**, **PageDown**, **Home**, **End** keys.
7519      **Left** key goes to constructed element above. **Plus**/**Minus**
7520      keys collapse/uncollapse constructed elements. **Space** toggles it
7521     :info:
7522      window with various information about element. You can scroll it
7523      with **h**/**l** (down, up) (**H**/**L** for triple speed) keys
7524     :hexdump:
7525      window with raw data hexdump and highlighted current element's
7526      contents. It automatically focuses on element's data. You can
7527      scroll it with **j**/**k** (down, up) (**J**/**K** for triple
7528      speed) keys. If element has explicit tag, then it also will be
7529      highlighted with different colour
7530
7531     Window's header contains current decode path and progress bars with
7532     position in *info* and *hexdump* windows.
7533
7534     If you press **d**, then current element will be saved in the
7535     current directory under its decode path name (adding ".0", ".1", etc
7536     suffix if such file already exists). **D** will save it with explicit tag.
7537
7538     You can also invoke it with ``--browse`` command line argument.
7539     """
7540     from copy import deepcopy
7541     from os.path import exists as path_exists
7542     import urwid
7543
7544     class TW(urwid.TreeWidget):
7545         def __init__(self, state, *args, **kwargs):
7546             self.state = state
7547             self.scrolled = {"info": False, "hexdump": False}
7548             super().__init__(*args, **kwargs)
7549
7550         def _get_pp(self):
7551             pp = self.get_node().get_value()
7552             constructed = len(pp) > 1
7553             return (pp if hasattr(pp, "_fields") else pp[0]), constructed
7554
7555         def _state_update(self):
7556             pp, _ = self._get_pp()
7557             self.state["decode_path"].set_text(
7558                 ":".join(str(p) for p in pp.decode_path)
7559             )
7560             lines = deepcopy(self.state["hexed"])
7561
7562             def attr_set(i, attr):
7563                 line = lines[i // 16]
7564                 idx = 1 + (i - 16 * (i // 16))
7565                 line[idx] = (attr, line[idx])
7566
7567             if pp.expl_offset is not None:
7568                 for i in range(
7569                         pp.expl_offset,
7570                         pp.expl_offset + pp.expl_tlen + pp.expl_llen,
7571                 ):
7572                     attr_set(i, "select-expl")
7573             for i in range(pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen):
7574                 attr_set(i, "select-value")
7575             self.state["hexdump"]._set_body([urwid.Text(line) for line in lines])
7576             self.state["hexdump"].set_focus(pp.offset // 16)
7577             self.state["hexdump"].set_focus_valign("middle")
7578             self.state["hexdump_bar"].set_completion(
7579                 (100 * pp.offset // 16) //
7580                 len(self.state["hexdump"]._body.positions())
7581             )
7582
7583             lines = [
7584                 [("header", "Name: "), pp.obj_name],
7585                 [("header", "Type: "), pp.asn1_type_name],
7586                 [("header", "Offset: "), "%d (0x%x)" % (pp.offset, pp.offset)],
7587                 [("header", "[TLV]len: "), "%d/%d/%d" % (
7588                     pp.tlen, pp.llen, pp.vlen,
7589                 )],
7590                 [("header", "TLVlen: "), "%d" % sum((
7591                     pp.tlen, pp.llen, pp.vlen,
7592                 ))],
7593                 [("header", "Slice: "), "[%d:%d]" % (
7594                     pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen,
7595                 )],
7596             ]
7597             if pp.lenindef:
7598                 lines.append([("warning", "LENINDEF")])
7599             if pp.ber_encoded:
7600                 lines.append([("warning", "BER encoded")])
7601             if pp.bered:
7602                 lines.append([("warning", "BERed")])
7603             if pp.expl is not None:
7604                 lines.append([("header", "EXPLICIT")])
7605                 klass, _, num = pp.expl
7606                 lines.append(["  Tag: %s%d" % (TagClassReprs[klass], num)])
7607                 if pp.expl_offset is not None:
7608                     lines.append(["  Offset: %d" % pp.expl_offset])
7609                     lines.append(["  [TLV]len: %d/%d/%d" % (
7610                         pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7611                     )])
7612                     lines.append(["  TLVlen: %d" % sum((
7613                         pp.expl_tlen, pp.expl_llen, pp.expl_vlen,
7614                     ))])
7615                     lines.append(["  Slice: [%d:%d]" % (
7616                         pp.expl_offset,
7617                         pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen,
7618                     )])
7619             if pp.impl is not None:
7620                 klass, _, num = pp.impl
7621                 lines.append([
7622                     ("header", "IMPLICIT: "), "%s%d" % (TagClassReprs[klass], num),
7623                 ])
7624             if pp.optional:
7625                 lines.append(["OPTIONAL"])
7626             if pp.default:
7627                 lines.append(["DEFAULT"])
7628             if len(pp.decode_path) > 0:
7629                 ent = pp.decode_path[-1]
7630                 if isinstance(ent, DecodePathDefBy):
7631                     lines.append([""])
7632                     value = str(ent.defined_by)
7633                     oid_name = find_oid_name(
7634                         ent.defined_by.asn1_type_name, oid_maps, value,
7635                     )
7636                     lines.append([("header", "DEFINED BY: "), "%s" % (
7637                         value if oid_name is None
7638                         else "%s (%s)" % (oid_name, value)
7639                     )])
7640             lines.append([""])
7641             if pp.value is not None:
7642                 lines.append([("header", "Value: "), pp.value])
7643                 if (
7644                         len(oid_maps) > 0 and
7645                         pp.asn1_type_name == ObjectIdentifier.asn1_type_name
7646                 ):
7647                     for oid_map in oid_maps:
7648                         oid_name = oid_map.get(pp.value)
7649                         if oid_name is not None:
7650                             lines.append([("header", "Human: "), oid_name])
7651                             break
7652                 if pp.asn1_type_name == Integer.asn1_type_name:
7653                     lines.append([
7654                         ("header", "Decimal: "), "%d" % int(pp.obj),
7655                     ])
7656                     lines.append([
7657                         ("header", "Hexadecimal: "), colonize_hex(pp.obj.tohex()),
7658                     ])
7659             if pp.blob.__class__ == bytes:
7660                 blob = hexenc(pp.blob).upper()
7661                 for i in range(0, len(blob), 32):
7662                     lines.append([colonize_hex(blob[i:i + 32])])
7663             elif pp.blob.__class__ == tuple:
7664                 lines.append([", ".join(pp.blob)])
7665             self.state["info"]._set_body([urwid.Text(line) for line in lines])
7666             self.state["info_bar"].set_completion(0)
7667
7668         def selectable(self):
7669             if self.state["widget_current"] != self:
7670                 self.state["widget_current"] = self
7671                 self.scrolled["info"] = False
7672                 self.scrolled["hexdump"] = False
7673                 self._state_update()
7674             return super().selectable()
7675
7676         def _get_display_text_without_offset(self):
7677             pp, constructed = self._get_pp()
7678             style = "constructed" if constructed else ""
7679             if len(pp.decode_path) == 0:
7680                 return (style, pp.obj_name)
7681             if pp.asn1_type_name == "EOC":
7682                 return ("eoc", "EOC")
7683             ent = pp.decode_path[-1]
7684             if isinstance(ent, DecodePathDefBy):
7685                 value = str(ent.defined_by)
7686                 oid_name = find_oid_name(
7687                     ent.defined_by.asn1_type_name, oid_maps, value,
7688                 )
7689                 return ("defby", "DEFBY:" + (
7690                     value if oid_name is None else oid_name
7691                 ))
7692             return (style, ent)
7693
7694         def get_display_text(self):
7695             pp, _ = self._get_pp()
7696             style, ent = self._get_display_text_without_offset()
7697             return [(style, ent), " [%d]" % pp.offset]
7698
7699         def _scroll(self, what, step):
7700             self.state[what]._invalidate()
7701             pos = self.state[what].focus_position
7702             if not self.scrolled[what]:
7703                 self.scrolled[what] = True
7704                 pos -= 2
7705             pos = max(0, pos + step)
7706             pos = min(pos, len(self.state[what]._body.positions()) - 1)
7707             self.state[what].set_focus(pos)
7708             self.state[what].set_focus_valign("top")
7709             self.state[what + "_bar"].set_completion(
7710                 (100 * pos) // len(self.state[what]._body.positions())
7711             )
7712
7713         def keypress(self, size, key):
7714             if key == "q":
7715                 raise urwid.ExitMainLoop()
7716
7717             if key == " ":
7718                 self.expanded = not self.expanded
7719                 self.update_expanded_icon()
7720                 return None
7721
7722             hexdump_steps = {"j": 1, "k": -1, "J": 5, "K": -5}
7723             if key in hexdump_steps:
7724                 self._scroll("hexdump", hexdump_steps[key])
7725                 return None
7726
7727             info_steps = {"h": 1, "l": -1, "H": 5, "L": -5}
7728             if key in info_steps:
7729                 self._scroll("info", info_steps[key])
7730                 return None
7731
7732             if key in ("d", "D"):
7733                 pp, _ = self._get_pp()
7734                 dp = ":".join(str(p) for p in pp.decode_path)
7735                 dp = dp.replace(" ", "_")
7736                 if dp == "":
7737                     dp = "root"
7738                 if key == "d" or pp.expl_offset is None:
7739                     data = self.state["raw"][pp.offset:(
7740                         pp.offset + pp.tlen + pp.llen + pp.vlen
7741                     )]
7742                 else:
7743                     data = self.state["raw"][pp.expl_offset:(
7744                         pp.expl_offset + pp.expl_tlen + pp.expl_llen + pp.expl_vlen
7745                     )]
7746                 ctr = 0
7747
7748                 def duplicate_path(dp, ctr):
7749                     if ctr == 0:
7750                         return dp
7751                     return "%s.%d" % (dp, ctr)
7752
7753                 while True:
7754                     if not path_exists(duplicate_path(dp, ctr)):
7755                         break
7756                     ctr += 1
7757                 dp = duplicate_path(dp, ctr)
7758                 with open(dp, "wb") as fd:
7759                     fd.write(data)
7760                 self.state["decode_path"].set_text(
7761                     ("warning", "Saved to: " + dp)
7762                 )
7763                 return None
7764             return super().keypress(size, key)
7765
7766     class PN(urwid.ParentNode):
7767         def __init__(self, state, value, *args, **kwargs):
7768             self.state = state
7769             if not hasattr(value, "_fields"):
7770                 value = list(value)
7771             super().__init__(value, *args, **kwargs)
7772
7773         def load_widget(self):
7774             return TW(self.state, self)
7775
7776         def load_child_keys(self):
7777             value = self.get_value()
7778             if hasattr(value, "_fields"):
7779                 return []
7780             return range(len(value[1:]))
7781
7782         def load_child_node(self, key):
7783             return PN(
7784                 self.state,
7785                 self.get_value()[key + 1],
7786                 parent=self,
7787                 key=key,
7788                 depth=self.get_depth() + 1,
7789             )
7790
7791     class LabeledPG(urwid.ProgressBar):
7792         def __init__(self, label, *args, **kwargs):
7793             self.label = label
7794             super().__init__(*args, **kwargs)
7795
7796         def get_text(self):
7797             return "%s: %s" % (self.label, super().get_text())
7798
7799     WinHexdump = urwid.ListBox([urwid.Text("")])
7800     WinInfo = urwid.ListBox([urwid.Text("")])
7801     WinDecodePath = urwid.Text("", "center")
7802     WinInfoBar = LabeledPG("info", "pg-normal", "pg-complete")
7803     WinHexdumpBar = LabeledPG("hexdump", "pg-normal", "pg-complete")
7804     WinTree = urwid.TreeListBox(urwid.TreeWalker(PN(
7805         {
7806             "raw": raw,
7807             "hexed": list(hexdump(raw)),
7808             "widget_current": None,
7809             "info": WinInfo,
7810             "info_bar": WinInfoBar,
7811             "hexdump": WinHexdump,
7812             "hexdump_bar": WinHexdumpBar,
7813             "decode_path": WinDecodePath,
7814         },
7815         list(obj.pps()),
7816     )))
7817     help_text = " ".join((
7818         "q:quit",
7819         "space:(un)collapse",
7820         "(pg)up/down/home/end:nav",
7821         "jkJK:hexdump hlHL:info",
7822         "dD:dump",
7823     ))
7824     urwid.MainLoop(
7825         urwid.Frame(
7826             urwid.Columns([
7827                 ("weight", 1, WinTree),
7828                 ("weight", 2, urwid.Pile([
7829                     urwid.LineBox(WinInfo),
7830                     urwid.LineBox(WinHexdump),
7831                 ])),
7832             ]),
7833             header=urwid.Columns([
7834                 ("weight", 2, urwid.AttrWrap(WinDecodePath, "header")),
7835                 ("weight", 1, WinInfoBar),
7836                 ("weight", 1, WinHexdumpBar),
7837             ]),
7838             footer=urwid.AttrWrap(urwid.Text(help_text), "help")
7839         ),
7840         [
7841             ("header", "bold", ""),
7842             ("constructed", "bold", ""),
7843             ("help", "light magenta", ""),
7844             ("warning", "light red", ""),
7845             ("defby", "light red", ""),
7846             ("eoc", "dark red", ""),
7847             ("select-value", "light green", ""),
7848             ("select-expl", "light red", ""),
7849             ("pg-normal", "", "light blue"),
7850             ("pg-complete", "black", "yellow"),
7851         ],
7852     ).run()
7853
7854
7855 def main():  # pragma: no cover
7856     import argparse
7857     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/CER/DER decoder")
7858     parser.add_argument(
7859         "--skip",
7860         type=int,
7861         default=0,
7862         help="Skip that number of bytes from the beginning",
7863     )
7864     parser.add_argument(
7865         "--oids",
7866         help="Python paths to dictionary with OIDs, comma separated",
7867     )
7868     parser.add_argument(
7869         "--schema",
7870         help="Python path to schema definition to use",
7871     )
7872     parser.add_argument(
7873         "--defines-by-path",
7874         help="Python path to decoder's defines_by_path",
7875     )
7876     parser.add_argument(
7877         "--nobered",
7878         action="store_true",
7879         help="Disallow BER encoding",
7880     )
7881     parser.add_argument(
7882         "--print-decode-path",
7883         action="store_true",
7884         help="Print decode paths",
7885     )
7886     parser.add_argument(
7887         "--decode-path-only",
7888         help="Print only specified decode path",
7889     )
7890     parser.add_argument(
7891         "--allow-expl-oob",
7892         action="store_true",
7893         help="Allow explicit tag out-of-bound",
7894     )
7895     parser.add_argument(
7896         "--evgen",
7897         action="store_true",
7898         help="Turn on event generation mode",
7899     )
7900     parser.add_argument(
7901         "--browse",
7902         action="store_true",
7903         help="Start ASN.1 browser",
7904     )
7905     parser.add_argument(
7906         "RAWFile",
7907         type=argparse.FileType("rb"),
7908         help="Path to BER/CER/DER file you want to decode",
7909     )
7910     args = parser.parse_args()
7911     try:
7912         raw = file_mmaped(args.RAWFile)[args.skip:]
7913     except:
7914         args.RAWFile.seek(args.skip)
7915         raw = memoryview(args.RAWFile.read())
7916         args.RAWFile.close()
7917     oid_maps = (
7918         [obj_by_path(_path) for _path in (args.oids or "").split(",")]
7919         if args.oids else ()
7920     )
7921     from functools import partial
7922     if args.schema:
7923         schema = obj_by_path(args.schema)
7924         pprinter = partial(pprint, big_blobs=True)
7925     else:
7926         schema, pprinter = generic_decoder()
7927     ctx = {
7928         "bered": not args.nobered,
7929         "allow_expl_oob": args.allow_expl_oob,
7930     }
7931     if args.defines_by_path is not None:
7932         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
7933     if args.browse:
7934         obj, _ = schema().decode(raw, ctx=ctx)
7935         browse(raw, obj, oid_maps)
7936         from sys import exit as sys_exit
7937         sys_exit(0)
7938     from os import environ
7939     pprinter = partial(
7940         pprinter,
7941         oid_maps=oid_maps,
7942         with_colours=environ.get("NO_COLOR") is None,
7943         with_decode_path=args.print_decode_path,
7944         decode_path_only=(
7945             () if args.decode_path_only is None else
7946             tuple(args.decode_path_only.split(":"))
7947         ),
7948     )
7949     if args.evgen:
7950         for decode_path, obj, tail in schema().decode_evgen(raw, ctx=ctx):
7951             print(pprinter(obj, decode_path=decode_path))
7952     else:
7953         obj, tail = schema().decode(raw, ctx=ctx)
7954         print(pprinter(obj))
7955     if tail != b"":
7956         print("\nTrailing data: %s" % hexenc(tail))
7957
7958
7959 if __name__ == "__main__":
7960     from pyderasn import *
7961     main()