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