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