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