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