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