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