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