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