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