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