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