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