]> Cypherpunks.ru repositories - pyderasn.git/blob - pyderasn.py
Sanitize all Set required values are decoded
[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             l, llen, v = len_decode(lv)
3890         except DecodeError as err:
3891             raise err.__class__(
3892                 msg=err.msg,
3893                 klass=self.__class__,
3894                 decode_path=decode_path,
3895                 offset=offset,
3896             )
3897         if l > len(v):
3898             raise NotEnoughData(
3899                 "encoded length is longer than data",
3900                 klass=self.__class__,
3901                 decode_path=decode_path,
3902                 offset=offset,
3903             )
3904         tlvlen = tlen + llen + l
3905         v, tail = tlv[:tlvlen], v[l:]
3906         obj = self.__class__(
3907             value=v.tobytes(),
3908             expl=self._expl,
3909             optional=self.optional,
3910             _decoded=(offset, 0, tlvlen),
3911         )
3912         obj.tag = t
3913         return obj, tail
3914
3915     def __repr__(self):
3916         return pp_console_row(next(self.pps()))
3917
3918     def pps(self, decode_path=()):
3919         yield _pp(
3920             asn1_type_name=self.asn1_type_name,
3921             obj_name=self.__class__.__name__,
3922             decode_path=decode_path,
3923             blob=self._value if self.ready else None,
3924             optional=self.optional,
3925             default=self == self.default,
3926             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3927             expl=None if self._expl is None else tag_decode(self._expl),
3928             offset=self.offset,
3929             tlen=self.tlen,
3930             llen=self.llen,
3931             vlen=self.vlen,
3932             expl_offset=self.expl_offset if self.expled else None,
3933             expl_tlen=self.expl_tlen if self.expled else None,
3934             expl_llen=self.expl_llen if self.expled else None,
3935             expl_vlen=self.expl_vlen if self.expled else None,
3936         )
3937         defined_by, defined = self.defined or (None, None)
3938         if defined_by is not None:
3939             yield defined.pps(
3940                 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3941             )
3942
3943
3944 ########################################################################
3945 # ASN.1 constructed types
3946 ########################################################################
3947
3948 def get_def_by_path(defines_by_path, sub_decode_path):
3949     """Get define by decode path
3950     """
3951     for path, define in defines_by_path:
3952         if len(path) != len(sub_decode_path):
3953             continue
3954         for p1, p2 in zip(path, sub_decode_path):
3955             if (p1 != any) and (p1 != p2):
3956                 break
3957         else:
3958             return define
3959
3960
3961 def abs_decode_path(decode_path, rel_path):
3962     """Create an absolute decode path from current and relative ones
3963
3964     :param decode_path: current decode path, starting point.
3965                         Tuple of strings
3966     :param rel_path: relative path to ``decode_path``. Tuple of strings.
3967                      If first tuple's element is "/", then treat it as
3968                      an absolute path, ignoring ``decode_path`` as
3969                      starting point. Also this tuple can contain ".."
3970                      elements, stripping the leading element from
3971                      ``decode_path``
3972
3973     >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3974     ("foo", "bar", "baz", "whatever")
3975     >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3976     ("foo", "whatever")
3977     >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3978     ("baz", "whatever")
3979     """
3980     if rel_path[0] == "/":
3981         return rel_path[1:]
3982     if rel_path[0] == "..":
3983         return abs_decode_path(decode_path[:-1], rel_path[1:])
3984     return decode_path + rel_path
3985
3986
3987 class Sequence(Obj):
3988     """``SEQUENCE`` structure type
3989
3990     You have to make specification of sequence::
3991
3992         class Extension(Sequence):
3993             schema = (
3994                 ("extnID", ObjectIdentifier()),
3995                 ("critical", Boolean(default=False)),
3996                 ("extnValue", OctetString()),
3997             )
3998
3999     Then, you can work with it as with dictionary.
4000
4001     >>> ext = Extension()
4002     >>> Extension().specs
4003     OrderedDict([
4004         ('extnID', OBJECT IDENTIFIER),
4005         ('critical', BOOLEAN False OPTIONAL DEFAULT),
4006         ('extnValue', OCTET STRING),
4007     ])
4008     >>> ext["extnID"] = "1.2.3"
4009     Traceback (most recent call last):
4010     pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4011     >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4012
4013     You can determine if sequence is ready to be encoded:
4014
4015     >>> ext.ready
4016     False
4017     >>> ext.encode()
4018     Traceback (most recent call last):
4019     pyderasn.ObjNotReady: object is not ready: extnValue
4020     >>> ext["extnValue"] = OctetString(b"foobar")
4021     >>> ext.ready
4022     True
4023
4024     Value you want to assign, must have the same **type** as in
4025     corresponding specification, but it can have different tags,
4026     optional/default attributes -- they will be taken from specification
4027     automatically::
4028
4029         class TBSCertificate(Sequence):
4030             schema = (
4031                 ("version", Version(expl=tag_ctxc(0), default="v1")),
4032             [...]
4033
4034     >>> tbs = TBSCertificate()
4035     >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4036
4037     Assign ``None`` to remove value from sequence.
4038
4039     You can set values in Sequence during its initialization:
4040
4041     >>> AlgorithmIdentifier((
4042         ("algorithm", ObjectIdentifier("1.2.3")),
4043         ("parameters", Any(Null()))
4044     ))
4045     AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4046
4047     You can determine if value exists/set in the sequence and take its value:
4048
4049     >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4050     (True, True, False)
4051     >>> ext["extnID"]
4052     OBJECT IDENTIFIER 1.2.3
4053
4054     But pay attention that if value has default, then it won't be (not
4055     in) in the sequence (because ``DEFAULT`` must not be encoded in
4056     DER), but you can read its value:
4057
4058     >>> "critical" in ext, ext["critical"]
4059     (False, BOOLEAN False)
4060     >>> ext["critical"] = Boolean(True)
4061     >>> "critical" in ext, ext["critical"]
4062     (True, BOOLEAN True)
4063
4064     All defaulted values are always optional.
4065
4066     .. _strict_default_existence_ctx:
4067
4068     .. warning::
4069
4070        When decoded DER contains defaulted value inside, then
4071        technically this is not valid DER encoding. But we allow and pass
4072        it **by default**. Of course reencoding of that kind of DER will
4073        result in different binary representation (validly without
4074        defaulted value inside). You can enable strict defaulted values
4075        existence validation by setting ``"strict_default_existence":
4076        True`` :ref:`context <ctx>` option -- decoding process will raise
4077        an exception if defaulted value is met.
4078
4079     Two sequences are equal if they have equal specification (schema),
4080     implicit/explicit tagging and the same values.
4081     """
4082     __slots__ = ("specs",)
4083     tag_default = tag_encode(form=TagFormConstructed, num=16)
4084     asn1_type_name = "SEQUENCE"
4085
4086     def __init__(
4087             self,
4088             value=None,
4089             schema=None,
4090             impl=None,
4091             expl=None,
4092             default=None,
4093             optional=False,
4094             _decoded=(0, 0, 0),
4095     ):
4096         super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4097         if schema is None:
4098             schema = getattr(self, "schema", ())
4099         self.specs = (
4100             schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4101         )
4102         self._value = {}
4103         if value is not None:
4104             if issubclass(value.__class__, Sequence):
4105                 self._value = value._value
4106             elif hasattr(value, "__iter__"):
4107                 for seq_key, seq_value in value:
4108                     self[seq_key] = seq_value
4109             else:
4110                 raise InvalidValueType((Sequence,))
4111         if default is not None:
4112             if not issubclass(default.__class__, Sequence):
4113                 raise InvalidValueType((Sequence,))
4114             default_value = default._value
4115             default_obj = self.__class__(impl=self.tag, expl=self._expl)
4116             default_obj.specs = self.specs
4117             default_obj._value = default_value
4118             self.default = default_obj
4119             if value is None:
4120                 self._value = default_obj.copy()._value
4121
4122     @property
4123     def ready(self):
4124         for name, spec in self.specs.items():
4125             value = self._value.get(name)
4126             if value is None:
4127                 if spec.optional:
4128                     continue
4129                 return False
4130             else:
4131                 if not value.ready:
4132                     return False
4133         return True
4134
4135     def copy(self):
4136         obj = self.__class__(schema=self.specs)
4137         obj.tag = self.tag
4138         obj._expl = self._expl
4139         obj.default = self.default
4140         obj.optional = self.optional
4141         obj.offset = self.offset
4142         obj.llen = self.llen
4143         obj.vlen = self.vlen
4144         obj._value = {k: v.copy() for k, v in self._value.items()}
4145         return obj
4146
4147     def __eq__(self, their):
4148         if not isinstance(their, self.__class__):
4149             return False
4150         return (
4151             self.specs == their.specs and
4152             self.tag == their.tag and
4153             self._expl == their._expl and
4154             self._value == their._value
4155         )
4156
4157     def __call__(
4158             self,
4159             value=None,
4160             impl=None,
4161             expl=None,
4162             default=None,
4163             optional=None,
4164     ):
4165         return self.__class__(
4166             value=value,
4167             schema=self.specs,
4168             impl=self.tag if impl is None else impl,
4169             expl=self._expl if expl is None else expl,
4170             default=self.default if default is None else default,
4171             optional=self.optional if optional is None else optional,
4172         )
4173
4174     def __contains__(self, key):
4175         return key in self._value
4176
4177     def __setitem__(self, key, value):
4178         spec = self.specs.get(key)
4179         if spec is None:
4180             raise ObjUnknown(key)
4181         if value is None:
4182             self._value.pop(key, None)
4183             return
4184         if not isinstance(value, spec.__class__):
4185             raise InvalidValueType((spec.__class__,))
4186         value = spec(value=value)
4187         if spec.default is not None and value == spec.default:
4188             self._value.pop(key, None)
4189             return
4190         self._value[key] = value
4191
4192     def __getitem__(self, key):
4193         value = self._value.get(key)
4194         if value is not None:
4195             return value
4196         spec = self.specs.get(key)
4197         if spec is None:
4198             raise ObjUnknown(key)
4199         if spec.default is not None:
4200             return spec.default
4201         return None
4202
4203     def _encoded_values(self):
4204         raws = []
4205         for name, spec in self.specs.items():
4206             value = self._value.get(name)
4207             if value is None:
4208                 if spec.optional:
4209                     continue
4210                 raise ObjNotReady(name)
4211             raws.append(value.encode())
4212         return raws
4213
4214     def _encode(self):
4215         v = b"".join(self._encoded_values())
4216         return b"".join((self.tag, len_encode(len(v)), v))
4217
4218     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4219         try:
4220             t, tlen, lv = tag_strip(tlv)
4221         except DecodeError as err:
4222             raise err.__class__(
4223                 msg=err.msg,
4224                 klass=self.__class__,
4225                 decode_path=decode_path,
4226                 offset=offset,
4227             )
4228         if t != self.tag:
4229             raise TagMismatch(
4230                 klass=self.__class__,
4231                 decode_path=decode_path,
4232                 offset=offset,
4233             )
4234         if tag_only:
4235             return
4236         try:
4237             l, llen, v = len_decode(lv)
4238         except DecodeError as err:
4239             raise err.__class__(
4240                 msg=err.msg,
4241                 klass=self.__class__,
4242                 decode_path=decode_path,
4243                 offset=offset,
4244             )
4245         if l > len(v):
4246             raise NotEnoughData(
4247                 "encoded length is longer than data",
4248                 klass=self.__class__,
4249                 decode_path=decode_path,
4250                 offset=offset,
4251             )
4252         v, tail = v[:l], v[l:]
4253         sub_offset = offset + tlen + llen
4254         values = {}
4255         for name, spec in self.specs.items():
4256             if len(v) == 0 and spec.optional:
4257                 continue
4258             sub_decode_path = decode_path + (name,)
4259             try:
4260                 value, v_tail = spec.decode(
4261                     v,
4262                     sub_offset,
4263                     leavemm=True,
4264                     decode_path=sub_decode_path,
4265                     ctx=ctx,
4266                 )
4267             except TagMismatch:
4268                 if spec.optional:
4269                     continue
4270                 raise
4271
4272             defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4273             if defined is not None:
4274                 defined_by, defined_spec = defined
4275                 if issubclass(value.__class__, SequenceOf):
4276                     for i, _value in enumerate(value):
4277                         sub_sub_decode_path = sub_decode_path + (
4278                             str(i),
4279                             DecodePathDefBy(defined_by),
4280                         )
4281                         defined_value, defined_tail = defined_spec.decode(
4282                             memoryview(bytes(_value)),
4283                             sub_offset + (
4284                                 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4285                                 if value.expled else (value.tlen + value.llen)
4286                             ),
4287                             leavemm=True,
4288                             decode_path=sub_sub_decode_path,
4289                             ctx=ctx,
4290                         )
4291                         if len(defined_tail) > 0:
4292                             raise DecodeError(
4293                                 "remaining data",
4294                                 klass=self.__class__,
4295                                 decode_path=sub_sub_decode_path,
4296                                 offset=offset,
4297                             )
4298                         _value.defined = (defined_by, defined_value)
4299                 else:
4300                     defined_value, defined_tail = defined_spec.decode(
4301                         memoryview(bytes(value)),
4302                         sub_offset + (
4303                             (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4304                             if value.expled else (value.tlen + value.llen)
4305                         ),
4306                         leavemm=True,
4307                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4308                         ctx=ctx,
4309                     )
4310                     if len(defined_tail) > 0:
4311                         raise DecodeError(
4312                             "remaining data",
4313                             klass=self.__class__,
4314                             decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4315                             offset=offset,
4316                         )
4317                     value.defined = (defined_by, defined_value)
4318
4319             sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4320             v = v_tail
4321             if spec.default is not None and value == spec.default:
4322                 if ctx.get("strict_default_existence", False):
4323                     raise DecodeError(
4324                         "DEFAULT value met",
4325                         klass=self.__class__,
4326                         decode_path=sub_decode_path,
4327                         offset=sub_offset,
4328                     )
4329                 else:
4330                     continue
4331             values[name] = value
4332
4333             spec_defines = getattr(spec, "defines", ())
4334             if len(spec_defines) == 0:
4335                 defines_by_path = ctx.get("defines_by_path", ())
4336                 if len(defines_by_path) > 0:
4337                     spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4338             if spec_defines is not None and len(spec_defines) > 0:
4339                 for rel_path, schema in spec_defines:
4340                     defined = schema.get(value, None)
4341                     if defined is not None:
4342                         ctx.setdefault("defines", []).append((
4343                             abs_decode_path(sub_decode_path[:-1], rel_path),
4344                             (value, defined),
4345                         ))
4346         if len(v) > 0:
4347             raise DecodeError(
4348                 "remaining data",
4349                 klass=self.__class__,
4350                 decode_path=decode_path,
4351                 offset=offset,
4352             )
4353         obj = self.__class__(
4354             schema=self.specs,
4355             impl=self.tag,
4356             expl=self._expl,
4357             default=self.default,
4358             optional=self.optional,
4359             _decoded=(offset, llen, l),
4360         )
4361         obj._value = values
4362         return obj, tail
4363
4364     def __repr__(self):
4365         value = pp_console_row(next(self.pps()))
4366         cols = []
4367         for name in self.specs:
4368             _value = self._value.get(name)
4369             if _value is None:
4370                 continue
4371             cols.append(repr(_value))
4372         return "%s[%s]" % (value, ", ".join(cols))
4373
4374     def pps(self, decode_path=()):
4375         yield _pp(
4376             asn1_type_name=self.asn1_type_name,
4377             obj_name=self.__class__.__name__,
4378             decode_path=decode_path,
4379             optional=self.optional,
4380             default=self == self.default,
4381             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4382             expl=None if self._expl is None else tag_decode(self._expl),
4383             offset=self.offset,
4384             tlen=self.tlen,
4385             llen=self.llen,
4386             vlen=self.vlen,
4387             expl_offset=self.expl_offset if self.expled else None,
4388             expl_tlen=self.expl_tlen if self.expled else None,
4389             expl_llen=self.expl_llen if self.expled else None,
4390             expl_vlen=self.expl_vlen if self.expled else None,
4391         )
4392         for name in self.specs:
4393             value = self._value.get(name)
4394             if value is None:
4395                 continue
4396             yield value.pps(decode_path=decode_path + (name,))
4397
4398
4399 class Set(Sequence):
4400     """``SET`` structure type
4401
4402     Its usage is identical to :py:class:`pyderasn.Sequence`.
4403     """
4404     __slots__ = ()
4405     tag_default = tag_encode(form=TagFormConstructed, num=17)
4406     asn1_type_name = "SET"
4407
4408     def _encode(self):
4409         raws = self._encoded_values()
4410         raws.sort()
4411         v = b"".join(raws)
4412         return b"".join((self.tag, len_encode(len(v)), v))
4413
4414     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4415         try:
4416             t, tlen, lv = tag_strip(tlv)
4417         except DecodeError as err:
4418             raise err.__class__(
4419                 msg=err.msg,
4420                 klass=self.__class__,
4421                 decode_path=decode_path,
4422                 offset=offset,
4423             )
4424         if t != self.tag:
4425             raise TagMismatch(
4426                 klass=self.__class__,
4427                 decode_path=decode_path,
4428                 offset=offset,
4429             )
4430         if tag_only:
4431             return
4432         try:
4433             l, llen, v = len_decode(lv)
4434         except DecodeError as err:
4435             raise err.__class__(
4436                 msg=err.msg,
4437                 klass=self.__class__,
4438                 decode_path=decode_path,
4439                 offset=offset,
4440             )
4441         if l > len(v):
4442             raise NotEnoughData(
4443                 "encoded length is longer than data",
4444                 klass=self.__class__,
4445                 offset=offset,
4446             )
4447         v, tail = v[:l], v[l:]
4448         sub_offset = offset + tlen + llen
4449         values = {}
4450         specs_items = self.specs.items
4451         while len(v) > 0:
4452             for name, spec in specs_items():
4453                 sub_decode_path = decode_path + (name,)
4454                 try:
4455                     spec.decode(
4456                         v,
4457                         sub_offset,
4458                         leavemm=True,
4459                         decode_path=sub_decode_path,
4460                         ctx=ctx,
4461                         tag_only=True,
4462                     )
4463                 except TagMismatch:
4464                     continue
4465                 break
4466             else:
4467                 raise TagMismatch(
4468                     klass=self.__class__,
4469                     decode_path=decode_path,
4470                     offset=offset,
4471                 )
4472             value, v_tail = spec.decode(
4473                 v,
4474                 sub_offset,
4475                 leavemm=True,
4476                 decode_path=sub_decode_path,
4477                 ctx=ctx,
4478             )
4479             sub_offset += (
4480                 value.expl_tlvlen if value.expled else value.tlvlen
4481             )
4482             v = v_tail
4483             if spec.default is None or value != spec.default:  # pragma: no cover
4484                 # SeqMixing.test_encoded_default_accepted covers that place
4485                 values[name] = value
4486         obj = self.__class__(
4487             schema=self.specs,
4488             impl=self.tag,
4489             expl=self._expl,
4490             default=self.default,
4491             optional=self.optional,
4492             _decoded=(offset, llen, l),
4493         )
4494         obj._value = values
4495         if not obj.ready:
4496             raise DecodeError(
4497                 msg="not all values are ready",
4498                 klass=self.__class__,
4499                 decode_path=decode_path,
4500                 offset=offset,
4501             )
4502         return obj, tail
4503
4504
4505 class SequenceOf(Obj):
4506     """``SEQUENCE OF`` sequence type
4507
4508     For that kind of type you must specify the object it will carry on
4509     (bounds are for example here, not required)::
4510
4511         class Ints(SequenceOf):
4512             schema = Integer()
4513             bounds = (0, 2)
4514
4515     >>> ints = Ints()
4516     >>> ints.append(Integer(123))
4517     >>> ints.append(Integer(234))
4518     >>> ints
4519     Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4520     >>> [int(i) for i in ints]
4521     [123, 234]
4522     >>> ints.append(Integer(345))
4523     Traceback (most recent call last):
4524     pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4525     >>> ints[1]
4526     INTEGER 234
4527     >>> ints[1] = Integer(345)
4528     >>> ints
4529     Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4530
4531     Also you can initialize sequence with preinitialized values:
4532
4533     >>> ints = Ints([Integer(123), Integer(234)])
4534     """
4535     __slots__ = ("spec", "_bound_min", "_bound_max")
4536     tag_default = tag_encode(form=TagFormConstructed, num=16)
4537     asn1_type_name = "SEQUENCE OF"
4538
4539     def __init__(
4540             self,
4541             value=None,
4542             schema=None,
4543             bounds=None,
4544             impl=None,
4545             expl=None,
4546             default=None,
4547             optional=False,
4548             _decoded=(0, 0, 0),
4549     ):
4550         super(SequenceOf, self).__init__(
4551             impl,
4552             expl,
4553             default,
4554             optional,
4555             _decoded,
4556         )
4557         if schema is None:
4558             schema = getattr(self, "schema", None)
4559         if schema is None:
4560             raise ValueError("schema must be specified")
4561         self.spec = schema
4562         self._bound_min, self._bound_max = getattr(
4563             self,
4564             "bounds",
4565             (0, float("+inf")),
4566         ) if bounds is None else bounds
4567         self._value = []
4568         if value is not None:
4569             self._value = self._value_sanitize(value)
4570         if default is not None:
4571             default_value = self._value_sanitize(default)
4572             default_obj = self.__class__(
4573                 schema=schema,
4574                 impl=self.tag,
4575                 expl=self._expl,
4576             )
4577             default_obj._value = default_value
4578             self.default = default_obj
4579             if value is None:
4580                 self._value = default_obj.copy()._value
4581
4582     def _value_sanitize(self, value):
4583         if issubclass(value.__class__, SequenceOf):
4584             value = value._value
4585         elif hasattr(value, "__iter__"):
4586             value = list(value)
4587         else:
4588             raise InvalidValueType((self.__class__, iter))
4589         if not self._bound_min <= len(value) <= self._bound_max:
4590             raise BoundsError(self._bound_min, len(value), self._bound_max)
4591         for v in value:
4592             if not isinstance(v, self.spec.__class__):
4593                 raise InvalidValueType((self.spec.__class__,))
4594         return value
4595
4596     @property
4597     def ready(self):
4598         return all(v.ready for v in self._value)
4599
4600     def copy(self):
4601         obj = self.__class__(schema=self.spec)
4602         obj._bound_min = self._bound_min
4603         obj._bound_max = self._bound_max
4604         obj.tag = self.tag
4605         obj._expl = self._expl
4606         obj.default = self.default
4607         obj.optional = self.optional
4608         obj.offset = self.offset
4609         obj.llen = self.llen
4610         obj.vlen = self.vlen
4611         obj._value = [v.copy() for v in self._value]
4612         return obj
4613
4614     def __eq__(self, their):
4615         if isinstance(their, self.__class__):
4616             return (
4617                 self.spec == their.spec and
4618                 self.tag == their.tag and
4619                 self._expl == their._expl and
4620                 self._value == their._value
4621             )
4622         if hasattr(their, "__iter__"):
4623             return self._value == list(their)
4624         return False
4625
4626     def __call__(
4627             self,
4628             value=None,
4629             bounds=None,
4630             impl=None,
4631             expl=None,
4632             default=None,
4633             optional=None,
4634     ):
4635         return self.__class__(
4636             value=value,
4637             schema=self.spec,
4638             bounds=(
4639                 (self._bound_min, self._bound_max)
4640                 if bounds is None else bounds
4641             ),
4642             impl=self.tag if impl is None else impl,
4643             expl=self._expl if expl is None else expl,
4644             default=self.default if default is None else default,
4645             optional=self.optional if optional is None else optional,
4646         )
4647
4648     def __contains__(self, key):
4649         return key in self._value
4650
4651     def append(self, value):
4652         if not isinstance(value, self.spec.__class__):
4653             raise InvalidValueType((self.spec.__class__,))
4654         if len(self._value) + 1 > self._bound_max:
4655             raise BoundsError(
4656                 self._bound_min,
4657                 len(self._value) + 1,
4658                 self._bound_max,
4659             )
4660         self._value.append(value)
4661
4662     def __iter__(self):
4663         self._assert_ready()
4664         return iter(self._value)
4665
4666     def __len__(self):
4667         self._assert_ready()
4668         return len(self._value)
4669
4670     def __setitem__(self, key, value):
4671         if not isinstance(value, self.spec.__class__):
4672             raise InvalidValueType((self.spec.__class__,))
4673         self._value[key] = self.spec(value=value)
4674
4675     def __getitem__(self, key):
4676         return self._value[key]
4677
4678     def _encoded_values(self):
4679         return [v.encode() for v in self._value]
4680
4681     def _encode(self):
4682         v = b"".join(self._encoded_values())
4683         return b"".join((self.tag, len_encode(len(v)), v))
4684
4685     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4686         try:
4687             t, tlen, lv = tag_strip(tlv)
4688         except DecodeError as err:
4689             raise err.__class__(
4690                 msg=err.msg,
4691                 klass=self.__class__,
4692                 decode_path=decode_path,
4693                 offset=offset,
4694             )
4695         if t != self.tag:
4696             raise TagMismatch(
4697                 klass=self.__class__,
4698                 decode_path=decode_path,
4699                 offset=offset,
4700             )
4701         if tag_only:
4702             return
4703         try:
4704             l, llen, v = len_decode(lv)
4705         except DecodeError as err:
4706             raise err.__class__(
4707                 msg=err.msg,
4708                 klass=self.__class__,
4709                 decode_path=decode_path,
4710                 offset=offset,
4711             )
4712         if l > len(v):
4713             raise NotEnoughData(
4714                 "encoded length is longer than data",
4715                 klass=self.__class__,
4716                 decode_path=decode_path,
4717                 offset=offset,
4718             )
4719         v, tail = v[:l], v[l:]
4720         sub_offset = offset + tlen + llen
4721         _value = []
4722         spec = self.spec
4723         while len(v) > 0:
4724             value, v_tail = spec.decode(
4725                 v,
4726                 sub_offset,
4727                 leavemm=True,
4728                 decode_path=decode_path + (str(len(_value)),),
4729                 ctx=ctx,
4730             )
4731             sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4732             v = v_tail
4733             _value.append(value)
4734         obj = self.__class__(
4735             value=_value,
4736             schema=spec,
4737             bounds=(self._bound_min, self._bound_max),
4738             impl=self.tag,
4739             expl=self._expl,
4740             default=self.default,
4741             optional=self.optional,
4742             _decoded=(offset, llen, l),
4743         )
4744         return obj, tail
4745
4746     def __repr__(self):
4747         return "%s[%s]" % (
4748             pp_console_row(next(self.pps())),
4749             ", ".join(repr(v) for v in self._value),
4750         )
4751
4752     def pps(self, decode_path=()):
4753         yield _pp(
4754             asn1_type_name=self.asn1_type_name,
4755             obj_name=self.__class__.__name__,
4756             decode_path=decode_path,
4757             optional=self.optional,
4758             default=self == self.default,
4759             impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4760             expl=None if self._expl is None else tag_decode(self._expl),
4761             offset=self.offset,
4762             tlen=self.tlen,
4763             llen=self.llen,
4764             vlen=self.vlen,
4765             expl_offset=self.expl_offset if self.expled else None,
4766             expl_tlen=self.expl_tlen if self.expled else None,
4767             expl_llen=self.expl_llen if self.expled else None,
4768             expl_vlen=self.expl_vlen if self.expled else None,
4769         )
4770         for i, value in enumerate(self._value):
4771             yield value.pps(decode_path=decode_path + (str(i),))
4772
4773
4774 class SetOf(SequenceOf):
4775     """``SET OF`` sequence type
4776
4777     Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4778     """
4779     __slots__ = ()
4780     tag_default = tag_encode(form=TagFormConstructed, num=17)
4781     asn1_type_name = "SET OF"
4782
4783     def _encode(self):
4784         raws = self._encoded_values()
4785         raws.sort()
4786         v = b"".join(raws)
4787         return b"".join((self.tag, len_encode(len(v)), v))
4788
4789
4790 def obj_by_path(pypath):  # pragma: no cover
4791     """Import object specified as string Python path
4792
4793     Modules must be separated from classes/functions with ``:``.
4794
4795     >>> obj_by_path("foo.bar:Baz")
4796     <class 'foo.bar.Baz'>
4797     >>> obj_by_path("foo.bar:Baz.boo")
4798     <classmethod 'foo.bar.Baz.boo'>
4799     """
4800     mod, objs = pypath.rsplit(":", 1)
4801     from importlib import import_module
4802     obj = import_module(mod)
4803     for obj_name in objs.split("."):
4804         obj = getattr(obj, obj_name)
4805     return obj
4806
4807
4808 def generic_decoder():  # pragma: no cover
4809     # All of this below is a big hack with self references
4810     choice = PrimitiveTypes()
4811     choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4812     choice.specs["SetOf"] = SetOf(schema=choice)
4813     for i in range(31):
4814         choice.specs["SequenceOf%d" % i] = SequenceOf(
4815             schema=choice,
4816             expl=tag_ctxc(i),
4817         )
4818     choice.specs["Any"] = Any()
4819
4820     # Class name equals to type name, to omit it from output
4821     class SEQUENCEOF(SequenceOf):
4822         __slots__ = ()
4823         schema = choice
4824
4825     def pprint_any(obj, oids=None, with_colours=False):
4826         def _pprint_pps(pps):
4827             for pp in pps:
4828                 if hasattr(pp, "_fields"):
4829                     if pp.asn1_type_name == Choice.asn1_type_name:
4830                         continue
4831                     pp_kwargs = pp._asdict()
4832                     pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4833                     pp = _pp(**pp_kwargs)
4834                     yield pp_console_row(
4835                         pp,
4836                         oids=oids,
4837                         with_offsets=True,
4838                         with_blob=False,
4839                         with_colours=with_colours,
4840                     )
4841                     for row in pp_console_blob(pp):
4842                         yield row
4843                 else:
4844                     for row in _pprint_pps(pp):
4845                         yield row
4846         return "\n".join(_pprint_pps(obj.pps()))
4847     return SEQUENCEOF(), pprint_any
4848
4849
4850 def main():  # pragma: no cover
4851     import argparse
4852     parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4853     parser.add_argument(
4854         "--skip",
4855         type=int,
4856         default=0,
4857         help="Skip that number of bytes from the beginning",
4858     )
4859     parser.add_argument(
4860         "--oids",
4861         help="Python path to dictionary with OIDs",
4862     )
4863     parser.add_argument(
4864         "--schema",
4865         help="Python path to schema definition to use",
4866     )
4867     parser.add_argument(
4868         "--defines-by-path",
4869         help="Python path to decoder's defines_by_path",
4870     )
4871     parser.add_argument(
4872         "DERFile",
4873         type=argparse.FileType("rb"),
4874         help="Path to DER file you want to decode",
4875     )
4876     args = parser.parse_args()
4877     args.DERFile.seek(args.skip)
4878     der = memoryview(args.DERFile.read())
4879     args.DERFile.close()
4880     oids = obj_by_path(args.oids) if args.oids else {}
4881     if args.schema:
4882         schema = obj_by_path(args.schema)
4883         from functools import partial
4884         pprinter = partial(pprint, big_blobs=True)
4885     else:
4886         schema, pprinter = generic_decoder()
4887     obj, tail = schema().decode(
4888         der,
4889         ctx=(
4890             None if args.defines_by_path is None else
4891             {"defines_by_path": obj_by_path(args.defines_by_path)}
4892         ),
4893     )
4894     print(pprinter(
4895         obj,
4896         oids=oids,
4897         with_colours=True if environ.get("NO_COLOR") is None else False,
4898     ))
4899     if tail != b"":
4900         print("\nTrailing data: %s" % hexenc(tail))
4901
4902
4903 if __name__ == "__main__":
4904     main()