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