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