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