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