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