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