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