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