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