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