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