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