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