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