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