about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/pyasn1
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/pyasn1
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/pyasn1')
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/__init__.py2
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/__init__.py1
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/ber/__init__.py1
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/ber/decoder.py2189
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/ber/encoder.py954
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/ber/eoo.py28
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/cer/__init__.py1
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/cer/decoder.py149
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/cer/encoder.py331
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/der/__init__.py1
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/der/decoder.py120
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/der/encoder.py126
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/native/__init__.py1
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/native/decoder.py244
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/native/encoder.py285
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/codec/streaming.py234
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/compat/__init__.py4
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/compat/integer.py13
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/debug.py146
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/error.py116
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/__init__.py1
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/base.py699
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/char.py288
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/constraint.py751
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/error.py11
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/namedtype.py550
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/namedval.py192
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/opentype.py104
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/tag.py335
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/tagmap.py96
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/univ.py3327
-rw-r--r--.venv/lib/python3.12/site-packages/pyasn1/type/useful.py189
32 files changed, 11489 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/__init__.py b/.venv/lib/python3.12/site-packages/pyasn1/__init__.py
new file mode 100644
index 00000000..7fa1d9e6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/__init__.py
@@ -0,0 +1,2 @@
+# https://www.python.org/dev/peps/pep-0396/
+__version__ = '0.6.1'
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/__init__.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/__init__.py
new file mode 100644
index 00000000..8c3066b2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/__init__.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/__init__.py
new file mode 100644
index 00000000..8c3066b2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/decoder.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/decoder.py
new file mode 100644
index 00000000..7e69ca15
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/decoder.py
@@ -0,0 +1,2189 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import io
+import os
+import sys
+import warnings
+
+from pyasn1 import debug
+from pyasn1 import error
+from pyasn1.codec.ber import eoo
+from pyasn1.codec.streaming import asSeekableStream
+from pyasn1.codec.streaming import isEndOfStream
+from pyasn1.codec.streaming import peekIntoStream
+from pyasn1.codec.streaming import readFromStream
+from pyasn1.compat import _MISSING
+from pyasn1.error import PyAsn1Error
+from pyasn1.type import base
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['StreamingDecoder', 'Decoder', 'decode']
+
+LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER)
+
+noValue = base.noValue
+
+SubstrateUnderrunError = error.SubstrateUnderrunError
+
+
+class AbstractPayloadDecoder(object):
+    protoComponent = None
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        """Decode value with fixed byte length.
+
+        The decoder is allowed to consume as many bytes as necessary.
+        """
+        raise error.PyAsn1Error('SingleItemDecoder not implemented for %s' % (tagSet,))  # TODO: Seems more like an NotImplementedError?
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        """Decode value with undefined length.
+
+        The decoder is allowed to consume as many bytes as necessary.
+        """
+        raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,)) # TODO: Seems more like an NotImplementedError?
+
+    @staticmethod
+    def _passAsn1Object(asn1Object, options):
+        if 'asn1Object' not in options:
+            options['asn1Object'] = asn1Object
+
+        return options
+
+
+class AbstractSimplePayloadDecoder(AbstractPayloadDecoder):
+    @staticmethod
+    def substrateCollector(asn1Object, substrate, length, options):
+        for chunk in readFromStream(substrate, length, options):
+            yield chunk
+
+    def _createComponent(self, asn1Spec, tagSet, value, **options):
+        if options.get('native'):
+            return value
+        elif asn1Spec is None:
+            return self.protoComponent.clone(value, tagSet=tagSet)
+        elif value is noValue:
+            return asn1Spec
+        else:
+            return asn1Spec.clone(value)
+
+
+class RawPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.Any('')
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if substrateFun:
+            asn1Object = self._createComponent(asn1Spec, tagSet, '', **options)
+
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        for value in decodeFun(substrate, asn1Spec, tagSet, length, **options):
+            yield value
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if substrateFun:
+            asn1Object = self._createComponent(asn1Spec, tagSet, '', **options)
+
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        while True:
+            for value in decodeFun(
+                    substrate, asn1Spec, tagSet, length,
+                    allowEoo=True, **options):
+
+                if value is eoo.endOfOctets:
+                    return
+
+                yield value
+
+
+rawPayloadDecoder = RawPayloadDecoder()
+
+
+class IntegerPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.Integer(0)
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        for chunk in readFromStream(substrate, length, options):
+            if isinstance(chunk, SubstrateUnderrunError):
+                yield chunk
+
+        if chunk:
+            value = int.from_bytes(bytes(chunk), 'big', signed=True)
+
+        else:
+            value = 0
+
+        yield self._createComponent(asn1Spec, tagSet, value, **options)
+
+
+class BooleanPayloadDecoder(IntegerPayloadDecoder):
+    protoComponent = univ.Boolean(0)
+
+    def _createComponent(self, asn1Spec, tagSet, value, **options):
+        return IntegerPayloadDecoder._createComponent(
+            self, asn1Spec, tagSet, value and 1 or 0, **options)
+
+
+class BitStringPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.BitString(())
+    supportConstructedForm = True
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+
+        if substrateFun:
+            asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        if not length:
+            raise error.PyAsn1Error('Empty BIT STRING substrate')
+
+        for chunk in isEndOfStream(substrate):
+            if isinstance(chunk, SubstrateUnderrunError):
+                yield chunk
+
+        if chunk:
+            raise error.PyAsn1Error('Empty BIT STRING substrate')
+
+        if tagSet[0].tagFormat == tag.tagFormatSimple:  # XXX what tag to check?
+
+            for trailingBits in readFromStream(substrate, 1, options):
+                if isinstance(trailingBits, SubstrateUnderrunError):
+                    yield trailingBits
+
+            trailingBits = ord(trailingBits)
+            if trailingBits > 7:
+                raise error.PyAsn1Error(
+                    'Trailing bits overflow %s' % trailingBits
+                )
+
+            for chunk in readFromStream(substrate, length - 1, options):
+                if isinstance(chunk, SubstrateUnderrunError):
+                    yield chunk
+
+            value = self.protoComponent.fromOctetString(
+                chunk, internalFormat=True, padding=trailingBits)
+
+            yield self._createComponent(asn1Spec, tagSet, value, **options)
+
+            return
+
+        if not self.supportConstructedForm:
+            raise error.PyAsn1Error('Constructed encoding form prohibited '
+                                    'at %s' % self.__class__.__name__)
+
+        if LOG:
+            LOG('assembling constructed serialization')
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        bitString = self.protoComponent.fromOctetString(b'', internalFormat=True)
+
+        current_position = substrate.tell()
+
+        while substrate.tell() - current_position < length:
+            for component in decodeFun(
+                    substrate, self.protoComponent, substrateFun=substrateFun,
+                    **options):
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+            trailingBits = component[0]
+            if trailingBits > 7:
+                raise error.PyAsn1Error(
+                    'Trailing bits overflow %s' % trailingBits
+                )
+
+            bitString = self.protoComponent.fromOctetString(
+                component[1:], internalFormat=True,
+                prepend=bitString, padding=trailingBits
+            )
+
+        yield self._createComponent(asn1Spec, tagSet, bitString, **options)
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+
+        if substrateFun:
+            asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        bitString = self.protoComponent.fromOctetString(b'', internalFormat=True)
+
+        while True:  # loop over fragments
+
+            for component in decodeFun(
+                    substrate, self.protoComponent, substrateFun=substrateFun,
+                    allowEoo=True, **options):
+
+                if component is eoo.endOfOctets:
+                    break
+
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+            if component is eoo.endOfOctets:
+                break
+
+            trailingBits = component[0]
+            if trailingBits > 7:
+                raise error.PyAsn1Error(
+                    'Trailing bits overflow %s' % trailingBits
+                )
+
+            bitString = self.protoComponent.fromOctetString(
+                component[1:], internalFormat=True,
+                prepend=bitString, padding=trailingBits
+            )
+
+        yield self._createComponent(asn1Spec, tagSet, bitString, **options)
+
+
+class OctetStringPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.OctetString('')
+    supportConstructedForm = True
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if substrateFun:
+            asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        if tagSet[0].tagFormat == tag.tagFormatSimple:  # XXX what tag to check?
+            for chunk in readFromStream(substrate, length, options):
+                if isinstance(chunk, SubstrateUnderrunError):
+                    yield chunk
+
+            yield self._createComponent(asn1Spec, tagSet, chunk, **options)
+
+            return
+
+        if not self.supportConstructedForm:
+            raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__)
+
+        if LOG:
+            LOG('assembling constructed serialization')
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        header = b''
+
+        original_position = substrate.tell()
+        # head = popSubstream(substrate, length)
+        while substrate.tell() - original_position < length:
+            for component in decodeFun(
+                    substrate, self.protoComponent, substrateFun=substrateFun,
+                    **options):
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+            header += component
+
+        yield self._createComponent(asn1Spec, tagSet, header, **options)
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if substrateFun and substrateFun is not self.substrateCollector:
+            asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        header = b''
+
+        while True:  # loop over fragments
+
+            for component in decodeFun(
+                    substrate, self.protoComponent, substrateFun=substrateFun,
+                    allowEoo=True, **options):
+
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+                if component is eoo.endOfOctets:
+                    break
+
+            if component is eoo.endOfOctets:
+                break
+
+            header += component
+
+        yield self._createComponent(asn1Spec, tagSet, header, **options)
+
+
+class NullPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.Null('')
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        for chunk in readFromStream(substrate, length, options):
+            if isinstance(chunk, SubstrateUnderrunError):
+                yield chunk
+
+        component = self._createComponent(asn1Spec, tagSet, '', **options)
+
+        if chunk:
+            raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
+
+        yield component
+
+
+class ObjectIdentifierPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.ObjectIdentifier(())
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        for chunk in readFromStream(substrate, length, options):
+            if isinstance(chunk, SubstrateUnderrunError):
+                yield chunk
+
+        if not chunk:
+            raise error.PyAsn1Error('Empty substrate')
+
+        oid = ()
+        index = 0
+        substrateLen = len(chunk)
+        while index < substrateLen:
+            subId = chunk[index]
+            index += 1
+            if subId < 128:
+                oid += (subId,)
+            elif subId > 128:
+                # Construct subid from a number of octets
+                nextSubId = subId
+                subId = 0
+                while nextSubId >= 128:
+                    subId = (subId << 7) + (nextSubId & 0x7F)
+                    if index >= substrateLen:
+                        raise error.SubstrateUnderrunError(
+                            'Short substrate for sub-OID past %s' % (oid,)
+                        )
+                    nextSubId = chunk[index]
+                    index += 1
+                oid += ((subId << 7) + nextSubId,)
+            elif subId == 128:
+                # ASN.1 spec forbids leading zeros (0x80) in OID
+                # encoding, tolerating it opens a vulnerability. See
+                # https://www.esat.kuleuven.be/cosic/publications/article-1432.pdf
+                # page 7
+                raise error.PyAsn1Error('Invalid octet 0x80 in OID encoding')
+
+        # Decode two leading arcs
+        if 0 <= oid[0] <= 39:
+            oid = (0,) + oid
+        elif 40 <= oid[0] <= 79:
+            oid = (1, oid[0] - 40) + oid[1:]
+        elif oid[0] >= 80:
+            oid = (2, oid[0] - 80) + oid[1:]
+        else:
+            raise error.PyAsn1Error('Malformed first OID octet: %s' % chunk[0])
+
+        yield self._createComponent(asn1Spec, tagSet, oid, **options)
+
+
+class RelativeOIDPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.RelativeOID(())
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        for chunk in readFromStream(substrate, length, options):
+            if isinstance(chunk, SubstrateUnderrunError):
+                yield chunk
+
+        if not chunk:
+            raise error.PyAsn1Error('Empty substrate')
+
+        reloid = ()
+        index = 0
+        substrateLen = len(chunk)
+        while index < substrateLen:
+            subId = chunk[index]
+            index += 1
+            if subId < 128:
+                reloid += (subId,)
+            elif subId > 128:
+                # Construct subid from a number of octets
+                nextSubId = subId
+                subId = 0
+                while nextSubId >= 128:
+                    subId = (subId << 7) + (nextSubId & 0x7F)
+                    if index >= substrateLen:
+                        raise error.SubstrateUnderrunError(
+                            'Short substrate for sub-OID past %s' % (reloid,)
+                        )
+                    nextSubId = chunk[index]
+                    index += 1
+                reloid += ((subId << 7) + nextSubId,)
+            elif subId == 128:
+                # ASN.1 spec forbids leading zeros (0x80) in OID
+                # encoding, tolerating it opens a vulnerability. See
+                # https://www.esat.kuleuven.be/cosic/publications/article-1432.pdf
+                # page 7
+                raise error.PyAsn1Error('Invalid octet 0x80 in RELATIVE-OID encoding')
+
+        yield self._createComponent(asn1Spec, tagSet, reloid, **options)
+
+
+class RealPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.Real()
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if tagSet[0].tagFormat != tag.tagFormatSimple:
+            raise error.PyAsn1Error('Simple tag format expected')
+
+        for chunk in readFromStream(substrate, length, options):
+            if isinstance(chunk, SubstrateUnderrunError):
+                yield chunk
+
+        if not chunk:
+            yield self._createComponent(asn1Spec, tagSet, 0.0, **options)
+            return
+
+        fo = chunk[0]
+        chunk = chunk[1:]
+        if fo & 0x80:  # binary encoding
+            if not chunk:
+                raise error.PyAsn1Error("Incomplete floating-point value")
+
+            if LOG:
+                LOG('decoding binary encoded REAL')
+
+            n = (fo & 0x03) + 1
+
+            if n == 4:
+                n = chunk[0]
+                chunk = chunk[1:]
+
+            eo, chunk = chunk[:n], chunk[n:]
+
+            if not eo or not chunk:
+                raise error.PyAsn1Error('Real exponent screwed')
+
+            e = eo[0] & 0x80 and -1 or 0
+
+            while eo:  # exponent
+                e <<= 8
+                e |= eo[0]
+                eo = eo[1:]
+
+            b = fo >> 4 & 0x03  # base bits
+
+            if b > 2:
+                raise error.PyAsn1Error('Illegal Real base')
+
+            if b == 1:  # encbase = 8
+                e *= 3
+
+            elif b == 2:  # encbase = 16
+                e *= 4
+            p = 0
+
+            while chunk:  # value
+                p <<= 8
+                p |= chunk[0]
+                chunk = chunk[1:]
+
+            if fo & 0x40:  # sign bit
+                p = -p
+
+            sf = fo >> 2 & 0x03  # scale bits
+            p *= 2 ** sf
+            value = (p, 2, e)
+
+        elif fo & 0x40:  # infinite value
+            if LOG:
+                LOG('decoding infinite REAL')
+
+            value = fo & 0x01 and '-inf' or 'inf'
+
+        elif fo & 0xc0 == 0:  # character encoding
+            if not chunk:
+                raise error.PyAsn1Error("Incomplete floating-point value")
+
+            if LOG:
+                LOG('decoding character encoded REAL')
+
+            try:
+                if fo & 0x3 == 0x1:  # NR1
+                    value = (int(chunk), 10, 0)
+
+                elif fo & 0x3 == 0x2:  # NR2
+                    value = float(chunk)
+
+                elif fo & 0x3 == 0x3:  # NR3
+                    value = float(chunk)
+
+                else:
+                    raise error.SubstrateUnderrunError(
+                        'Unknown NR (tag %s)' % fo
+                    )
+
+            except ValueError:
+                raise error.SubstrateUnderrunError(
+                    'Bad character Real syntax'
+                )
+
+        else:
+            raise error.SubstrateUnderrunError(
+                'Unknown encoding (tag %s)' % fo
+            )
+
+        yield self._createComponent(asn1Spec, tagSet, value, **options)
+
+
+class AbstractConstructedPayloadDecoder(AbstractPayloadDecoder):
+    protoComponent = None
+
+
+class ConstructedPayloadDecoderBase(AbstractConstructedPayloadDecoder):
+    protoRecordComponent = None
+    protoSequenceComponent = None
+
+    def _getComponentTagMap(self, asn1Object, idx):
+        raise NotImplementedError
+
+    def _getComponentPositionByType(self, asn1Object, tagSet, idx):
+        raise NotImplementedError
+
+    def _decodeComponentsSchemaless(
+            self, substrate, tagSet=None, decodeFun=None,
+            length=None, **options):
+
+        asn1Object = None
+
+        components = []
+        componentTypes = set()
+
+        original_position = substrate.tell()
+
+        while length == -1 or substrate.tell() < original_position + length:
+            for component in decodeFun(substrate, **options):
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+            if length == -1 and component is eoo.endOfOctets:
+                break
+
+            components.append(component)
+            componentTypes.add(component.tagSet)
+
+            # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF
+            # The heuristics is:
+            # * 1+ components of different types -> likely SEQUENCE/SET
+            # * otherwise -> likely SEQUENCE OF/SET OF
+            if len(componentTypes) > 1:
+                protoComponent = self.protoRecordComponent
+
+            else:
+                protoComponent = self.protoSequenceComponent
+
+            asn1Object = protoComponent.clone(
+                # construct tagSet from base tag from prototype ASN.1 object
+                # and additional tags recovered from the substrate
+                tagSet=tag.TagSet(protoComponent.tagSet.baseTag, *tagSet.superTags)
+            )
+
+        if LOG:
+            LOG('guessed %r container type (pass `asn1Spec` to guide the '
+                'decoder)' % asn1Object)
+
+        for idx, component in enumerate(components):
+            asn1Object.setComponentByPosition(
+                idx, component,
+                verifyConstraints=False,
+                matchTags=False, matchConstraints=False
+            )
+
+        yield asn1Object
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if tagSet[0].tagFormat != tag.tagFormatConstructed:
+            raise error.PyAsn1Error('Constructed tag format expected')
+
+        original_position = substrate.tell()
+
+        if substrateFun:
+            if asn1Spec is not None:
+                asn1Object = asn1Spec.clone()
+
+            elif self.protoComponent is not None:
+                asn1Object = self.protoComponent.clone(tagSet=tagSet)
+
+            else:
+                asn1Object = self.protoRecordComponent, self.protoSequenceComponent
+
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        if asn1Spec is None:
+            for asn1Object in self._decodeComponentsSchemaless(
+                    substrate, tagSet=tagSet, decodeFun=decodeFun,
+                    length=length, **options):
+                if isinstance(asn1Object, SubstrateUnderrunError):
+                    yield asn1Object
+
+            if substrate.tell() < original_position + length:
+                if LOG:
+                    for trailing in readFromStream(substrate, context=options):
+                        if isinstance(trailing, SubstrateUnderrunError):
+                            yield trailing
+
+                    LOG('Unused trailing %d octets encountered: %s' % (
+                        len(trailing), debug.hexdump(trailing)))
+
+            yield asn1Object
+
+            return
+
+        asn1Object = asn1Spec.clone()
+        asn1Object.clear()
+
+        options = self._passAsn1Object(asn1Object, options)
+
+        if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
+
+            namedTypes = asn1Spec.componentType
+
+            isSetType = asn1Spec.typeId == univ.Set.typeId
+            isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault
+
+            if LOG:
+                LOG('decoding %sdeterministic %s type %r chosen by type ID' % (
+                    not isDeterministic and 'non-' or '', isSetType and 'SET' or '',
+                    asn1Spec))
+
+            seenIndices = set()
+            idx = 0
+            while substrate.tell() - original_position < length:
+                if not namedTypes:
+                    componentType = None
+
+                elif isSetType:
+                    componentType = namedTypes.tagMapUnique
+
+                else:
+                    try:
+                        if isDeterministic:
+                            componentType = namedTypes[idx].asn1Object
+
+                        elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+                            componentType = namedTypes.getTagMapNearPosition(idx)
+
+                        else:
+                            componentType = namedTypes[idx].asn1Object
+
+                    except IndexError:
+                        raise error.PyAsn1Error(
+                            'Excessive components decoded at %r' % (asn1Spec,)
+                        )
+
+                for component in decodeFun(substrate, componentType, **options):
+                    if isinstance(component, SubstrateUnderrunError):
+                        yield component
+
+                if not isDeterministic and namedTypes:
+                    if isSetType:
+                        idx = namedTypes.getPositionByType(component.effectiveTagSet)
+
+                    elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+                        idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
+
+                asn1Object.setComponentByPosition(
+                    idx, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False
+                )
+
+                seenIndices.add(idx)
+                idx += 1
+
+            if LOG:
+                LOG('seen component indices %s' % seenIndices)
+
+            if namedTypes:
+                if not namedTypes.requiredComponents.issubset(seenIndices):
+                    raise error.PyAsn1Error(
+                        'ASN.1 object %s has uninitialized '
+                        'components' % asn1Object.__class__.__name__)
+
+                if  namedTypes.hasOpenTypes:
+
+                    openTypes = options.get('openTypes', {})
+
+                    if LOG:
+                        LOG('user-specified open types map:')
+
+                        for k, v in openTypes.items():
+                            LOG('%s -> %r' % (k, v))
+
+                    if openTypes or options.get('decodeOpenTypes', False):
+
+                        for idx, namedType in enumerate(namedTypes.namedTypes):
+                            if not namedType.openType:
+                                continue
+
+                            if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue:
+                                continue
+
+                            governingValue = asn1Object.getComponentByName(
+                                namedType.openType.name
+                            )
+
+                            try:
+                                openType = openTypes[governingValue]
+
+                            except KeyError:
+
+                                if LOG:
+                                    LOG('default open types map of component '
+                                        '"%s.%s" governed by component "%s.%s"'
+                                        ':' % (asn1Object.__class__.__name__,
+                                               namedType.name,
+                                               asn1Object.__class__.__name__,
+                                               namedType.openType.name))
+
+                                    for k, v in namedType.openType.items():
+                                        LOG('%s -> %r' % (k, v))
+
+                                try:
+                                    openType = namedType.openType[governingValue]
+
+                                except KeyError:
+                                    if LOG:
+                                        LOG('failed to resolve open type by governing '
+                                            'value %r' % (governingValue,))
+                                    continue
+
+                            if LOG:
+                                LOG('resolved open type %r by governing '
+                                    'value %r' % (openType, governingValue))
+
+                            containerValue = asn1Object.getComponentByPosition(idx)
+
+                            if containerValue.typeId in (
+                                    univ.SetOf.typeId, univ.SequenceOf.typeId):
+
+                                for pos, containerElement in enumerate(
+                                        containerValue):
+
+                                    stream = asSeekableStream(containerValue[pos].asOctets())
+
+                                    for component in decodeFun(stream, asn1Spec=openType, **options):
+                                        if isinstance(component, SubstrateUnderrunError):
+                                            yield component
+
+                                    containerValue[pos] = component
+
+                            else:
+                                stream = asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets())
+
+                                for component in decodeFun(stream, asn1Spec=openType, **options):
+                                    if isinstance(component, SubstrateUnderrunError):
+                                        yield component
+
+                                asn1Object.setComponentByPosition(idx, component)
+
+            else:
+                inconsistency = asn1Object.isInconsistent
+                if inconsistency:
+                    raise error.PyAsn1Error(
+                        f"ASN.1 object {asn1Object.__class__.__name__} is inconsistent")
+
+        else:
+            componentType = asn1Spec.componentType
+
+            if LOG:
+                LOG('decoding type %r chosen by given `asn1Spec`' % componentType)
+
+            idx = 0
+
+            while substrate.tell() - original_position < length:
+                for component in decodeFun(substrate, componentType, **options):
+                    if isinstance(component, SubstrateUnderrunError):
+                        yield component
+
+                asn1Object.setComponentByPosition(
+                    idx, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False
+                )
+
+                idx += 1
+
+        yield asn1Object
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if tagSet[0].tagFormat != tag.tagFormatConstructed:
+            raise error.PyAsn1Error('Constructed tag format expected')
+
+        if substrateFun is not None:
+            if asn1Spec is not None:
+                asn1Object = asn1Spec.clone()
+
+            elif self.protoComponent is not None:
+                asn1Object = self.protoComponent.clone(tagSet=tagSet)
+
+            else:
+                asn1Object = self.protoRecordComponent, self.protoSequenceComponent
+
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        if asn1Spec is None:
+            for asn1Object in self._decodeComponentsSchemaless(
+                    substrate, tagSet=tagSet, decodeFun=decodeFun,
+                    length=length, **dict(options, allowEoo=True)):
+                if isinstance(asn1Object, SubstrateUnderrunError):
+                    yield asn1Object
+
+            yield asn1Object
+
+            return
+
+        asn1Object = asn1Spec.clone()
+        asn1Object.clear()
+
+        options = self._passAsn1Object(asn1Object, options)
+
+        if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
+
+            namedTypes = asn1Object.componentType
+
+            isSetType = asn1Object.typeId == univ.Set.typeId
+            isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault
+
+            if LOG:
+                LOG('decoding %sdeterministic %s type %r chosen by type ID' % (
+                    not isDeterministic and 'non-' or '', isSetType and 'SET' or '',
+                    asn1Spec))
+
+            seenIndices = set()
+
+            idx = 0
+
+            while True:  # loop over components
+                if len(namedTypes) <= idx:
+                    asn1Spec = None
+
+                elif isSetType:
+                    asn1Spec = namedTypes.tagMapUnique
+
+                else:
+                    try:
+                        if isDeterministic:
+                            asn1Spec = namedTypes[idx].asn1Object
+
+                        elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+                            asn1Spec = namedTypes.getTagMapNearPosition(idx)
+
+                        else:
+                            asn1Spec = namedTypes[idx].asn1Object
+
+                    except IndexError:
+                        raise error.PyAsn1Error(
+                            'Excessive components decoded at %r' % (asn1Object,)
+                        )
+
+                for component in decodeFun(substrate, asn1Spec, allowEoo=True, **options):
+
+                    if isinstance(component, SubstrateUnderrunError):
+                        yield component
+
+                    if component is eoo.endOfOctets:
+                        break
+
+                if component is eoo.endOfOctets:
+                    break
+
+                if not isDeterministic and namedTypes:
+                    if isSetType:
+                        idx = namedTypes.getPositionByType(component.effectiveTagSet)
+
+                    elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+                        idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
+
+                asn1Object.setComponentByPosition(
+                    idx, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False
+                )
+
+                seenIndices.add(idx)
+                idx += 1
+
+            if LOG:
+                LOG('seen component indices %s' % seenIndices)
+
+            if namedTypes:
+                if not namedTypes.requiredComponents.issubset(seenIndices):
+                    raise error.PyAsn1Error(
+                        'ASN.1 object %s has uninitialized '
+                        'components' % asn1Object.__class__.__name__)
+
+                if namedTypes.hasOpenTypes:
+
+                    openTypes = options.get('openTypes', {})
+
+                    if LOG:
+                        LOG('user-specified open types map:')
+
+                        for k, v in openTypes.items():
+                            LOG('%s -> %r' % (k, v))
+
+                    if openTypes or options.get('decodeOpenTypes', False):
+
+                        for idx, namedType in enumerate(namedTypes.namedTypes):
+                            if not namedType.openType:
+                                continue
+
+                            if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue:
+                                continue
+
+                            governingValue = asn1Object.getComponentByName(
+                                namedType.openType.name
+                            )
+
+                            try:
+                                openType = openTypes[governingValue]
+
+                            except KeyError:
+
+                                if LOG:
+                                    LOG('default open types map of component '
+                                        '"%s.%s" governed by component "%s.%s"'
+                                        ':' % (asn1Object.__class__.__name__,
+                                               namedType.name,
+                                               asn1Object.__class__.__name__,
+                                               namedType.openType.name))
+
+                                    for k, v in namedType.openType.items():
+                                        LOG('%s -> %r' % (k, v))
+
+                                try:
+                                    openType = namedType.openType[governingValue]
+
+                                except KeyError:
+                                    if LOG:
+                                        LOG('failed to resolve open type by governing '
+                                            'value %r' % (governingValue,))
+                                    continue
+
+                            if LOG:
+                                LOG('resolved open type %r by governing '
+                                    'value %r' % (openType, governingValue))
+
+                            containerValue = asn1Object.getComponentByPosition(idx)
+
+                            if containerValue.typeId in (
+                                    univ.SetOf.typeId, univ.SequenceOf.typeId):
+
+                                for pos, containerElement in enumerate(
+                                        containerValue):
+
+                                    stream = asSeekableStream(containerValue[pos].asOctets())
+
+                                    for component in decodeFun(stream, asn1Spec=openType,
+                                                               **dict(options, allowEoo=True)):
+                                        if isinstance(component, SubstrateUnderrunError):
+                                            yield component
+
+                                        if component is eoo.endOfOctets:
+                                            break
+
+                                    containerValue[pos] = component
+
+                            else:
+                                stream = asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets())
+                                for component in decodeFun(stream, asn1Spec=openType,
+                                                           **dict(options, allowEoo=True)):
+                                    if isinstance(component, SubstrateUnderrunError):
+                                        yield component
+
+                                    if component is eoo.endOfOctets:
+                                        break
+
+                                    asn1Object.setComponentByPosition(idx, component)
+
+                else:
+                    inconsistency = asn1Object.isInconsistent
+                    if inconsistency:
+                        raise error.PyAsn1Error(
+                            f"ASN.1 object {asn1Object.__class__.__name__} is inconsistent")
+
+        else:
+            componentType = asn1Spec.componentType
+
+            if LOG:
+                LOG('decoding type %r chosen by given `asn1Spec`' % componentType)
+
+            idx = 0
+
+            while True:
+
+                for component in decodeFun(
+                        substrate, componentType, allowEoo=True, **options):
+
+                    if isinstance(component, SubstrateUnderrunError):
+                        yield component
+
+                    if component is eoo.endOfOctets:
+                        break
+
+                if component is eoo.endOfOctets:
+                    break
+
+                asn1Object.setComponentByPosition(
+                    idx, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False
+                )
+
+                idx += 1
+
+        yield asn1Object
+
+
+class SequenceOrSequenceOfPayloadDecoder(ConstructedPayloadDecoderBase):
+    protoRecordComponent = univ.Sequence()
+    protoSequenceComponent = univ.SequenceOf()
+
+
+class SequencePayloadDecoder(SequenceOrSequenceOfPayloadDecoder):
+    protoComponent = univ.Sequence()
+
+
+class SequenceOfPayloadDecoder(SequenceOrSequenceOfPayloadDecoder):
+    protoComponent = univ.SequenceOf()
+
+
+class SetOrSetOfPayloadDecoder(ConstructedPayloadDecoderBase):
+    protoRecordComponent = univ.Set()
+    protoSequenceComponent = univ.SetOf()
+
+
+class SetPayloadDecoder(SetOrSetOfPayloadDecoder):
+    protoComponent = univ.Set()
+
+
+class SetOfPayloadDecoder(SetOrSetOfPayloadDecoder):
+    protoComponent = univ.SetOf()
+
+
+class ChoicePayloadDecoder(ConstructedPayloadDecoderBase):
+    protoComponent = univ.Choice()
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if asn1Spec is None:
+            asn1Object = self.protoComponent.clone(tagSet=tagSet)
+
+        else:
+            asn1Object = asn1Spec.clone()
+
+        if substrateFun:
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        options = self._passAsn1Object(asn1Object, options)
+
+        if asn1Object.tagSet == tagSet:
+            if LOG:
+                LOG('decoding %s as explicitly tagged CHOICE' % (tagSet,))
+
+            for component in decodeFun(
+                    substrate, asn1Object.componentTagMap, **options):
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+        else:
+            if LOG:
+                LOG('decoding %s as untagged CHOICE' % (tagSet,))
+
+            for component in decodeFun(
+                    substrate, asn1Object.componentTagMap, tagSet, length,
+                    state, **options):
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+        effectiveTagSet = component.effectiveTagSet
+
+        if LOG:
+            LOG('decoded component %s, effective tag set %s' % (component, effectiveTagSet))
+
+        asn1Object.setComponentByType(
+            effectiveTagSet, component,
+            verifyConstraints=False,
+            matchTags=False, matchConstraints=False,
+            innerFlag=False
+        )
+
+        yield asn1Object
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if asn1Spec is None:
+            asn1Object = self.protoComponent.clone(tagSet=tagSet)
+
+        else:
+            asn1Object = asn1Spec.clone()
+
+        if substrateFun:
+            for chunk in substrateFun(asn1Object, substrate, length, options):
+                yield chunk
+
+            return
+
+        options = self._passAsn1Object(asn1Object, options)
+
+        isTagged = asn1Object.tagSet == tagSet
+
+        if LOG:
+            LOG('decoding %s as %stagged CHOICE' % (
+                tagSet, isTagged and 'explicitly ' or 'un'))
+
+        while True:
+
+            if isTagged:
+                iterator = decodeFun(
+                    substrate, asn1Object.componentType.tagMapUnique,
+                    **dict(options, allowEoo=True))
+
+            else:
+                iterator = decodeFun(
+                    substrate, asn1Object.componentType.tagMapUnique,
+                    tagSet, length, state, **dict(options, allowEoo=True))
+
+            for component in iterator:
+
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+                if component is eoo.endOfOctets:
+                    break
+
+                effectiveTagSet = component.effectiveTagSet
+
+                if LOG:
+                    LOG('decoded component %s, effective tag set '
+                        '%s' % (component, effectiveTagSet))
+
+                asn1Object.setComponentByType(
+                    effectiveTagSet, component,
+                    verifyConstraints=False,
+                    matchTags=False, matchConstraints=False,
+                    innerFlag=False
+                )
+
+                if not isTagged:
+                    break
+
+            if not isTagged or component is eoo.endOfOctets:
+                break
+
+        yield asn1Object
+
+
+class AnyPayloadDecoder(AbstractSimplePayloadDecoder):
+    protoComponent = univ.Any()
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+        if asn1Spec is None:
+            isUntagged = True
+
+        elif asn1Spec.__class__ is tagmap.TagMap:
+            isUntagged = tagSet not in asn1Spec.tagMap
+
+        else:
+            isUntagged = tagSet != asn1Spec.tagSet
+
+        if isUntagged:
+            fullPosition = substrate.markedPosition
+            currentPosition = substrate.tell()
+
+            substrate.seek(fullPosition, os.SEEK_SET)
+            length += currentPosition - fullPosition
+
+            if LOG:
+                for chunk in peekIntoStream(substrate, length):
+                    if isinstance(chunk, SubstrateUnderrunError):
+                        yield chunk
+                LOG('decoding as untagged ANY, substrate '
+                    '%s' % debug.hexdump(chunk))
+
+        if substrateFun:
+            for chunk in substrateFun(
+                    self._createComponent(asn1Spec, tagSet, noValue, **options),
+                    substrate, length, options):
+                yield chunk
+
+            return
+
+        for chunk in readFromStream(substrate, length, options):
+            if isinstance(chunk, SubstrateUnderrunError):
+                yield chunk
+
+        yield self._createComponent(asn1Spec, tagSet, chunk, **options)
+
+    def indefLenValueDecoder(self, substrate, asn1Spec,
+                             tagSet=None, length=None, state=None,
+                             decodeFun=None, substrateFun=None,
+                             **options):
+        if asn1Spec is None:
+            isTagged = False
+
+        elif asn1Spec.__class__ is tagmap.TagMap:
+            isTagged = tagSet in asn1Spec.tagMap
+
+        else:
+            isTagged = tagSet == asn1Spec.tagSet
+
+        if isTagged:
+            # tagged Any type -- consume header substrate
+            chunk = b''
+
+            if LOG:
+                LOG('decoding as tagged ANY')
+
+        else:
+            # TODO: Seems not to be tested
+            fullPosition = substrate.markedPosition
+            currentPosition = substrate.tell()
+
+            substrate.seek(fullPosition, os.SEEK_SET)
+            for chunk in readFromStream(substrate, currentPosition - fullPosition, options):
+                if isinstance(chunk, SubstrateUnderrunError):
+                    yield chunk
+
+            if LOG:
+                LOG('decoding as untagged ANY, header substrate %s' % debug.hexdump(chunk))
+
+        # Any components do not inherit initial tag
+        asn1Spec = self.protoComponent
+
+        if substrateFun and substrateFun is not self.substrateCollector:
+            asn1Object = self._createComponent(
+                asn1Spec, tagSet, noValue, **options)
+
+            for chunk in substrateFun(
+                    asn1Object, chunk + substrate, length + len(chunk), options):
+                yield chunk
+
+            return
+
+        if LOG:
+            LOG('assembling constructed serialization')
+
+        # All inner fragments are of the same type, treat them as octet string
+        substrateFun = self.substrateCollector
+
+        while True:  # loop over fragments
+
+            for component in decodeFun(
+                    substrate, asn1Spec, substrateFun=substrateFun,
+                    allowEoo=True, **options):
+
+                if isinstance(component, SubstrateUnderrunError):
+                    yield component
+
+                if component is eoo.endOfOctets:
+                    break
+
+            if component is eoo.endOfOctets:
+                break
+
+            chunk += component
+
+        if substrateFun:
+            yield chunk  # TODO: Weird
+
+        else:
+            yield self._createComponent(asn1Spec, tagSet, chunk, **options)
+
+
+# character string types
+class UTF8StringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.UTF8String()
+
+
+class NumericStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.NumericString()
+
+
+class PrintableStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.PrintableString()
+
+
+class TeletexStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.TeletexString()
+
+
+class VideotexStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.VideotexString()
+
+
+class IA5StringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.IA5String()
+
+
+class GraphicStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.GraphicString()
+
+
+class VisibleStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.VisibleString()
+
+
+class GeneralStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.GeneralString()
+
+
+class UniversalStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.UniversalString()
+
+
+class BMPStringPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = char.BMPString()
+
+
+# "useful" types
+class ObjectDescriptorPayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = useful.ObjectDescriptor()
+
+
+class GeneralizedTimePayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = useful.GeneralizedTime()
+
+
+class UTCTimePayloadDecoder(OctetStringPayloadDecoder):
+    protoComponent = useful.UTCTime()
+
+
+TAG_MAP = {
+    univ.Integer.tagSet: IntegerPayloadDecoder(),
+    univ.Boolean.tagSet: BooleanPayloadDecoder(),
+    univ.BitString.tagSet: BitStringPayloadDecoder(),
+    univ.OctetString.tagSet: OctetStringPayloadDecoder(),
+    univ.Null.tagSet: NullPayloadDecoder(),
+    univ.ObjectIdentifier.tagSet: ObjectIdentifierPayloadDecoder(),
+    univ.RelativeOID.tagSet: RelativeOIDPayloadDecoder(),
+    univ.Enumerated.tagSet: IntegerPayloadDecoder(),
+    univ.Real.tagSet: RealPayloadDecoder(),
+    univ.Sequence.tagSet: SequenceOrSequenceOfPayloadDecoder(),  # conflicts with SequenceOf
+    univ.Set.tagSet: SetOrSetOfPayloadDecoder(),  # conflicts with SetOf
+    univ.Choice.tagSet: ChoicePayloadDecoder(),  # conflicts with Any
+    # character string types
+    char.UTF8String.tagSet: UTF8StringPayloadDecoder(),
+    char.NumericString.tagSet: NumericStringPayloadDecoder(),
+    char.PrintableString.tagSet: PrintableStringPayloadDecoder(),
+    char.TeletexString.tagSet: TeletexStringPayloadDecoder(),
+    char.VideotexString.tagSet: VideotexStringPayloadDecoder(),
+    char.IA5String.tagSet: IA5StringPayloadDecoder(),
+    char.GraphicString.tagSet: GraphicStringPayloadDecoder(),
+    char.VisibleString.tagSet: VisibleStringPayloadDecoder(),
+    char.GeneralString.tagSet: GeneralStringPayloadDecoder(),
+    char.UniversalString.tagSet: UniversalStringPayloadDecoder(),
+    char.BMPString.tagSet: BMPStringPayloadDecoder(),
+    # useful types
+    useful.ObjectDescriptor.tagSet: ObjectDescriptorPayloadDecoder(),
+    useful.GeneralizedTime.tagSet: GeneralizedTimePayloadDecoder(),
+    useful.UTCTime.tagSet: UTCTimePayloadDecoder()
+}
+
+# Type-to-codec map for ambiguous ASN.1 types
+TYPE_MAP = {
+    univ.Set.typeId: SetPayloadDecoder(),
+    univ.SetOf.typeId: SetOfPayloadDecoder(),
+    univ.Sequence.typeId: SequencePayloadDecoder(),
+    univ.SequenceOf.typeId: SequenceOfPayloadDecoder(),
+    univ.Choice.typeId: ChoicePayloadDecoder(),
+    univ.Any.typeId: AnyPayloadDecoder()
+}
+
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in TAG_MAP.values():
+    if typeDecoder.protoComponent is not None:
+        typeId = typeDecoder.protoComponent.__class__.typeId
+        if typeId is not None and typeId not in TYPE_MAP:
+            TYPE_MAP[typeId] = typeDecoder
+
+
+(stDecodeTag,
+ stDecodeLength,
+ stGetValueDecoder,
+ stGetValueDecoderByAsn1Spec,
+ stGetValueDecoderByTag,
+ stTryAsExplicitTag,
+ stDecodeValue,
+ stDumpRawValue,
+ stErrorCondition,
+ stStop) = [x for x in range(10)]
+
+
+EOO_SENTINEL = bytes((0, 0))
+
+
+class SingleItemDecoder(object):
+    defaultErrorState = stErrorCondition
+    #defaultErrorState = stDumpRawValue
+    defaultRawDecoder = AnyPayloadDecoder()
+
+    supportIndefLength = True
+
+    TAG_MAP = TAG_MAP
+    TYPE_MAP = TYPE_MAP
+
+    def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored):
+        self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP
+        self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP
+
+        # Tag & TagSet objects caches
+        self._tagCache = {}
+        self._tagSetCache = {}
+
+    def __call__(self, substrate, asn1Spec=None,
+                 tagSet=None, length=None, state=stDecodeTag,
+                 decodeFun=None, substrateFun=None,
+                 **options):
+
+        allowEoo = options.pop('allowEoo', False)
+
+        if LOG:
+            LOG('decoder called at scope %s with state %d, working with up '
+                'to %s octets of substrate: '
+                '%s' % (debug.scope, state, length, substrate))
+
+        # Look for end-of-octets sentinel
+        if allowEoo and self.supportIndefLength:
+
+            for eoo_candidate in readFromStream(substrate, 2, options):
+                if isinstance(eoo_candidate, SubstrateUnderrunError):
+                    yield eoo_candidate
+
+            if eoo_candidate == EOO_SENTINEL:
+                if LOG:
+                    LOG('end-of-octets sentinel found')
+                yield eoo.endOfOctets
+                return
+
+            else:
+                substrate.seek(-2, os.SEEK_CUR)
+
+        tagMap = self._tagMap
+        typeMap = self._typeMap
+        tagCache = self._tagCache
+        tagSetCache = self._tagSetCache
+
+        value = noValue
+
+        substrate.markedPosition = substrate.tell()
+
+        while state is not stStop:
+
+            if state is stDecodeTag:
+                # Decode tag
+                isShortTag = True
+
+                for firstByte in readFromStream(substrate, 1, options):
+                    if isinstance(firstByte, SubstrateUnderrunError):
+                        yield firstByte
+
+                firstOctet = ord(firstByte)
+
+                try:
+                    lastTag = tagCache[firstOctet]
+
+                except KeyError:
+                    integerTag = firstOctet
+                    tagClass = integerTag & 0xC0
+                    tagFormat = integerTag & 0x20
+                    tagId = integerTag & 0x1F
+
+                    if tagId == 0x1F:
+                        isShortTag = False
+                        lengthOctetIdx = 0
+                        tagId = 0
+
+                        while True:
+                            for integerByte in readFromStream(substrate, 1, options):
+                                if isinstance(integerByte, SubstrateUnderrunError):
+                                    yield integerByte
+
+                            if not integerByte:
+                                raise error.SubstrateUnderrunError(
+                                    'Short octet stream on long tag decoding'
+                                )
+
+                            integerTag = ord(integerByte)
+                            lengthOctetIdx += 1
+                            tagId <<= 7
+                            tagId |= (integerTag & 0x7F)
+
+                            if not integerTag & 0x80:
+                                break
+
+                    lastTag = tag.Tag(
+                        tagClass=tagClass, tagFormat=tagFormat, tagId=tagId
+                    )
+
+                    if isShortTag:
+                        # cache short tags
+                        tagCache[firstOctet] = lastTag
+
+                if tagSet is None:
+                    if isShortTag:
+                        try:
+                            tagSet = tagSetCache[firstOctet]
+
+                        except KeyError:
+                            # base tag not recovered
+                            tagSet = tag.TagSet((), lastTag)
+                            tagSetCache[firstOctet] = tagSet
+                    else:
+                        tagSet = tag.TagSet((), lastTag)
+
+                else:
+                    tagSet = lastTag + tagSet
+
+                state = stDecodeLength
+
+                if LOG:
+                    LOG('tag decoded into %s, decoding length' % tagSet)
+
+            if state is stDecodeLength:
+                # Decode length
+                for firstOctet in readFromStream(substrate, 1, options):
+                    if isinstance(firstOctet, SubstrateUnderrunError):
+                        yield firstOctet
+
+                firstOctet = ord(firstOctet)
+
+                if firstOctet < 128:
+                    length = firstOctet
+
+                elif firstOctet > 128:
+                    size = firstOctet & 0x7F
+                    # encoded in size bytes
+                    for encodedLength in readFromStream(substrate, size, options):
+                        if isinstance(encodedLength, SubstrateUnderrunError):
+                            yield encodedLength
+                    encodedLength = list(encodedLength)
+                    # missing check on maximum size, which shouldn't be a
+                    # problem, we can handle more than is possible
+                    if len(encodedLength) != size:
+                        raise error.SubstrateUnderrunError(
+                            '%s<%s at %s' % (size, len(encodedLength), tagSet)
+                        )
+
+                    length = 0
+                    for lengthOctet in encodedLength:
+                        length <<= 8
+                        length |= lengthOctet
+                    size += 1
+
+                else:  # 128 means indefinite
+                    length = -1
+
+                if length == -1 and not self.supportIndefLength:
+                    raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
+
+                state = stGetValueDecoder
+
+                if LOG:
+                    LOG('value length decoded into %d' % length)
+
+            if state is stGetValueDecoder:
+                if asn1Spec is None:
+                    state = stGetValueDecoderByTag
+
+                else:
+                    state = stGetValueDecoderByAsn1Spec
+            #
+            # There're two ways of creating subtypes in ASN.1 what influences
+            # decoder operation. These methods are:
+            # 1) Either base types used in or no IMPLICIT tagging has been
+            #    applied on subtyping.
+            # 2) Subtype syntax drops base type information (by means of
+            #    IMPLICIT tagging.
+            # The first case allows for complete tag recovery from substrate
+            # while the second one requires original ASN.1 type spec for
+            # decoding.
+            #
+            # In either case a set of tags (tagSet) is coming from substrate
+            # in an incremental, tag-by-tag fashion (this is the case of
+            # EXPLICIT tag which is most basic). Outermost tag comes first
+            # from the wire.
+            #
+            if state is stGetValueDecoderByTag:
+                try:
+                    concreteDecoder = tagMap[tagSet]
+
+                except KeyError:
+                    concreteDecoder = None
+
+                if concreteDecoder:
+                    state = stDecodeValue
+
+                else:
+                    try:
+                        concreteDecoder = tagMap[tagSet[:1]]
+
+                    except KeyError:
+                        concreteDecoder = None
+
+                    if concreteDecoder:
+                        state = stDecodeValue
+                    else:
+                        state = stTryAsExplicitTag
+
+                if LOG:
+                    LOG('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag'))
+                    debug.scope.push(concreteDecoder is None and '?' or concreteDecoder.protoComponent.__class__.__name__)
+
+            if state is stGetValueDecoderByAsn1Spec:
+
+                if asn1Spec.__class__ is tagmap.TagMap:
+                    try:
+                        chosenSpec = asn1Spec[tagSet]
+
+                    except KeyError:
+                        chosenSpec = None
+
+                    if LOG:
+                        LOG('candidate ASN.1 spec is a map of:')
+
+                        for firstOctet, v in asn1Spec.presentTypes.items():
+                            LOG('  %s -> %s' % (firstOctet, v.__class__.__name__))
+
+                        if asn1Spec.skipTypes:
+                            LOG('but neither of: ')
+                            for firstOctet, v in asn1Spec.skipTypes.items():
+                                LOG('  %s -> %s' % (firstOctet, v.__class__.__name__))
+                        LOG('new candidate ASN.1 spec is %s, chosen by %s' % (chosenSpec is None and '<none>' or chosenSpec.prettyPrintType(), tagSet))
+
+                elif tagSet == asn1Spec.tagSet or tagSet in asn1Spec.tagMap:
+                    chosenSpec = asn1Spec
+                    if LOG:
+                        LOG('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__)
+
+                else:
+                    chosenSpec = None
+
+                if chosenSpec is not None:
+                    try:
+                        # ambiguous type or just faster codec lookup
+                        concreteDecoder = typeMap[chosenSpec.typeId]
+
+                        if LOG:
+                            LOG('value decoder chosen for an ambiguous type by type ID %s' % (chosenSpec.typeId,))
+
+                    except KeyError:
+                        # use base type for codec lookup to recover untagged types
+                        baseTagSet = tag.TagSet(chosenSpec.tagSet.baseTag,  chosenSpec.tagSet.baseTag)
+                        try:
+                            # base type or tagged subtype
+                            concreteDecoder = tagMap[baseTagSet]
+
+                            if LOG:
+                                LOG('value decoder chosen by base %s' % (baseTagSet,))
+
+                        except KeyError:
+                            concreteDecoder = None
+
+                    if concreteDecoder:
+                        asn1Spec = chosenSpec
+                        state = stDecodeValue
+
+                    else:
+                        state = stTryAsExplicitTag
+
+                else:
+                    concreteDecoder = None
+                    state = stTryAsExplicitTag
+
+                if LOG:
+                    LOG('codec %s chosen by ASN.1 spec, decoding %s' % (state is stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag'))
+                    debug.scope.push(chosenSpec is None and '?' or chosenSpec.__class__.__name__)
+
+            if state is stDecodeValue:
+                if not options.get('recursiveFlag', True) and not substrateFun:  # deprecate this
+                    def substrateFun(asn1Object, _substrate, _length, _options):
+                        """Legacy hack to keep the recursiveFlag=False option supported.
+
+                        The decode(..., substrateFun=userCallback) option was introduced in 0.1.4 as a generalization
+                        of the old recursiveFlag=False option. Users should pass their callback instead of using
+                        recursiveFlag.
+                        """
+                        yield asn1Object
+
+                original_position = substrate.tell()
+
+                if length == -1:  # indef length
+                    for value in concreteDecoder.indefLenValueDecoder(
+                            substrate, asn1Spec,
+                            tagSet, length, stGetValueDecoder,
+                            self, substrateFun, **options):
+                        if isinstance(value, SubstrateUnderrunError):
+                            yield value
+
+                else:
+                    for value in concreteDecoder.valueDecoder(
+                            substrate, asn1Spec,
+                            tagSet, length, stGetValueDecoder,
+                            self, substrateFun, **options):
+                        if isinstance(value, SubstrateUnderrunError):
+                            yield value
+
+                    bytesRead = substrate.tell() - original_position
+                    if not substrateFun and bytesRead != length:
+                        raise PyAsn1Error(
+                            "Read %s bytes instead of expected %s." % (bytesRead, length))
+                    elif substrateFun and bytesRead > length:
+                        # custom substrateFun may be used for partial decoding, reading less is expected there
+                        raise PyAsn1Error(
+                            "Read %s bytes are more than expected %s." % (bytesRead, length))
+
+                if LOG:
+                   LOG('codec %s yields type %s, value:\n%s\n...' % (
+                       concreteDecoder.__class__.__name__, value.__class__.__name__,
+                       isinstance(value, base.Asn1Item) and value.prettyPrint() or value))
+
+                state = stStop
+                break
+
+            if state is stTryAsExplicitTag:
+                if (tagSet and
+                        tagSet[0].tagFormat == tag.tagFormatConstructed and
+                        tagSet[0].tagClass != tag.tagClassUniversal):
+                    # Assume explicit tagging
+                    concreteDecoder = rawPayloadDecoder
+                    state = stDecodeValue
+
+                else:
+                    concreteDecoder = None
+                    state = self.defaultErrorState
+
+                if LOG:
+                    LOG('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as failure'))
+
+            if state is stDumpRawValue:
+                concreteDecoder = self.defaultRawDecoder
+
+                if LOG:
+                    LOG('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__)
+
+                state = stDecodeValue
+
+            if state is stErrorCondition:
+                raise error.PyAsn1Error(
+                    '%s not in asn1Spec: %r' % (tagSet, asn1Spec)
+                )
+
+        if LOG:
+            debug.scope.pop()
+            LOG('decoder left scope %s, call completed' % debug.scope)
+
+        yield value
+
+
+class StreamingDecoder(object):
+    """Create an iterator that turns BER/CER/DER byte stream into ASN.1 objects.
+
+    On each iteration, consume whatever BER/CER/DER serialization is
+    available in the `substrate` stream-like object and turns it into
+    one or more, possibly nested, ASN.1 objects.
+
+    Parameters
+    ----------
+    substrate: :py:class:`file`, :py:class:`io.BytesIO`
+        BER/CER/DER serialization in form of a byte stream
+
+    Keyword Args
+    ------------
+    asn1Spec: :py:class:`~pyasn1.type.base.PyAsn1Item`
+        A pyasn1 type object to act as a template guiding the decoder.
+        Depending on the ASN.1 structure being decoded, `asn1Spec` may
+        or may not be required. One of the reasons why `asn1Spec` may
+        me required is that ASN.1 structure is encoded in the *IMPLICIT*
+        tagging mode.
+
+    Yields
+    ------
+    : :py:class:`~pyasn1.type.base.PyAsn1Item`, :py:class:`~pyasn1.error.SubstrateUnderrunError`
+        Decoded ASN.1 object (possibly, nested) or
+        :py:class:`~pyasn1.error.SubstrateUnderrunError` object indicating
+        insufficient BER/CER/DER serialization on input to fully recover ASN.1
+        objects from it.
+        
+        In the latter case the caller is advised to ensure some more data in
+        the input stream, then call the iterator again. The decoder will resume
+        the decoding process using the newly arrived data.
+
+        The `context` property of :py:class:`~pyasn1.error.SubstrateUnderrunError`
+        object might hold a reference to the partially populated ASN.1 object
+        being reconstructed.
+
+    Raises
+    ------
+    ~pyasn1.error.PyAsn1Error, ~pyasn1.error.EndOfStreamError
+        `PyAsn1Error` on deserialization error, `EndOfStreamError` on
+         premature stream closure.
+
+    Examples
+    --------
+    Decode BER serialisation without ASN.1 schema
+
+    .. code-block:: pycon
+
+        >>> stream = io.BytesIO(
+        ...    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+        >>>
+        >>> for asn1Object in StreamingDecoder(stream):
+        ...     print(asn1Object)
+        >>>
+        SequenceOf:
+         1 2 3
+
+    Decode BER serialisation with ASN.1 schema
+
+    .. code-block:: pycon
+
+        >>> stream = io.BytesIO(
+        ...    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+        >>>
+        >>> schema = SequenceOf(componentType=Integer())
+        >>>
+        >>> decoder = StreamingDecoder(stream, asn1Spec=schema)
+        >>> for asn1Object in decoder:
+        ...     print(asn1Object)
+        >>>
+        SequenceOf:
+         1 2 3
+    """
+
+    SINGLE_ITEM_DECODER = SingleItemDecoder
+
+    def __init__(self, substrate, asn1Spec=None, **options):
+        self._singleItemDecoder = self.SINGLE_ITEM_DECODER(**options)
+        self._substrate = asSeekableStream(substrate)
+        self._asn1Spec = asn1Spec
+        self._options = options
+
+    def __iter__(self):
+        while True:
+            for asn1Object in self._singleItemDecoder(
+                    self._substrate, self._asn1Spec, **self._options):
+                yield asn1Object
+
+            for chunk in isEndOfStream(self._substrate):
+                if isinstance(chunk, SubstrateUnderrunError):
+                    yield
+
+                break
+
+            if chunk:
+                break
+
+
+class Decoder(object):
+    """Create a BER decoder object.
+
+    Parse BER/CER/DER octet-stream into one, possibly nested, ASN.1 object.
+    """
+    STREAMING_DECODER = StreamingDecoder
+
+    @classmethod
+    def __call__(cls, substrate, asn1Spec=None, **options):
+        """Turns BER/CER/DER octet stream into an ASN.1 object.
+
+        Takes BER/CER/DER octet-stream in form of :py:class:`bytes`
+        and decode it into an ASN.1 object
+        (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+        may be a scalar or an arbitrary nested structure.
+
+        Parameters
+        ----------
+        substrate: :py:class:`bytes`
+            BER/CER/DER octet-stream to parse
+
+        Keyword Args
+        ------------
+        asn1Spec: :py:class:`~pyasn1.type.base.PyAsn1Item`
+            A pyasn1 type object (:py:class:`~pyasn1.type.base.PyAsn1Item`
+            derivative) to act as a template guiding the decoder.
+            Depending on the ASN.1 structure being decoded, `asn1Spec` may or
+            may not be required. Most common reason for it to require is that
+            ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+
+        substrateFun: :py:class:`Union[
+                Callable[[pyasn1.type.base.PyAsn1Item, bytes, int],
+                         Tuple[pyasn1.type.base.PyAsn1Item, bytes]],
+                Callable[[pyasn1.type.base.PyAsn1Item, io.BytesIO, int, dict],
+                         Generator[Union[pyasn1.type.base.PyAsn1Item,
+                                         pyasn1.error.SubstrateUnderrunError],
+                                   None, None]]
+            ]`
+            User callback meant to generalize special use cases like non-recursive or
+            partial decoding. A 3-arg non-streaming variant is supported for backwards
+            compatiblilty in addition to the newer 4-arg streaming variant.
+            The callback will receive the uninitialized object recovered from substrate
+            as 1st argument, the uninterpreted payload as 2nd argument, and the length
+            of the uninterpreted payload as 3rd argument. The streaming variant will
+            additionally receive the decode(..., **options) kwargs as 4th argument.
+            The non-streaming variant shall return an object that will be propagated
+            as decode() return value as 1st item, and the remainig payload for further
+            decode passes as 2nd item.
+            The streaming variant shall yield an object that will be propagated as
+            decode() return value, and leave the remaining payload in the stream.
+
+        Returns
+        -------
+        : :py:class:`tuple`
+            A tuple of :py:class:`~pyasn1.type.base.PyAsn1Item` object
+            recovered from BER/CER/DER substrate and the unprocessed trailing
+            portion of the `substrate` (may be empty)
+
+        Raises
+        ------
+        : :py:class:`~pyasn1.error.PyAsn1Error`
+            :py:class:`~pyasn1.error.SubstrateUnderrunError` on insufficient
+            input or :py:class:`~pyasn1.error.PyAsn1Error` on decoding error.
+
+        Examples
+        --------
+        Decode BER/CER/DER serialisation without ASN.1 schema
+
+        .. code-block:: pycon
+
+           >>> s, unprocessed = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+           >>> str(s)
+           SequenceOf:
+            1 2 3
+
+        Decode BER/CER/DER serialisation with ASN.1 schema
+
+        .. code-block:: pycon
+
+           >>> seq = SequenceOf(componentType=Integer())
+           >>> s, unprocessed = decode(
+                b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
+           >>> str(s)
+           SequenceOf:
+            1 2 3
+
+        """
+        substrate = asSeekableStream(substrate)
+
+        if "substrateFun" in options:
+            origSubstrateFun = options["substrateFun"]
+
+            def substrateFunWrapper(asn1Object, substrate, length, options=None):
+                """Support both 0.4 and 0.5 style APIs.
+
+                substrateFun API has changed in 0.5 for use with streaming decoders. To stay backwards compatible,
+                we first try if we received a streaming user callback. If that fails,we assume we've received a
+                non-streaming v0.4 user callback and convert it for streaming on the fly
+                """
+                try:
+                    substrate_gen = origSubstrateFun(asn1Object, substrate, length, options)
+                except TypeError as _value:
+                    if _value.__traceback__.tb_next:
+                        # Traceback depth > 1 means TypeError from inside user provided function
+                        raise
+                    # invariant maintained at Decoder.__call__ entry
+                    assert isinstance(substrate, io.BytesIO)  # nosec assert_used
+                    substrate_gen = Decoder._callSubstrateFunV4asV5(origSubstrateFun, asn1Object, substrate, length)
+                for value in substrate_gen:
+                    yield value
+
+            options["substrateFun"] = substrateFunWrapper
+
+        streamingDecoder = cls.STREAMING_DECODER(
+            substrate, asn1Spec, **options)
+
+        for asn1Object in streamingDecoder:
+            if isinstance(asn1Object, SubstrateUnderrunError):
+                raise error.SubstrateUnderrunError('Short substrate on input')
+
+            try:
+                tail = next(readFromStream(substrate))
+
+            except error.EndOfStreamError:
+                tail = b''
+
+            return asn1Object, tail
+
+    @staticmethod
+    def _callSubstrateFunV4asV5(substrateFunV4, asn1Object, substrate, length):
+        substrate_bytes = substrate.read()
+        if length == -1:
+            length = len(substrate_bytes)
+        value, nextSubstrate = substrateFunV4(asn1Object, substrate_bytes, length)
+        nbytes = substrate.write(nextSubstrate)
+        substrate.truncate()
+        substrate.seek(-nbytes, os.SEEK_CUR)
+        yield value
+
+#: Turns BER octet stream into an ASN.1 object.
+#:
+#: Takes BER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes`
+#:     BER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#:     being decoded, *asn1Spec* may or may not be required. Most common reason for
+#:     it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#:     A tuple of pyasn1 object recovered from BER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError
+#:     On decoding errors
+#:
+#: Notes
+#: -----
+#: This function is deprecated. Please use :py:class:`Decoder` or
+#: :py:class:`StreamingDecoder` class instance.
+#:
+#: Examples
+#: --------
+#: Decode BER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+#: Decode BER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+decode = Decoder()
+
+def __getattr__(attr: str):
+    if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
+        warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
+        return globals()[newAttr]
+    raise AttributeError(attr)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/encoder.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/encoder.py
new file mode 100644
index 00000000..d16fb1f8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/encoder.py
@@ -0,0 +1,954 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import sys
+import warnings
+
+from pyasn1 import debug
+from pyasn1 import error
+from pyasn1.codec.ber import eoo
+from pyasn1.compat import _MISSING
+from pyasn1.compat.integer import to_bytes
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['Encoder', 'encode']
+
+LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_ENCODER)
+
+
+class AbstractItemEncoder(object):
+    supportIndefLenMode = True
+
+    # An outcome of otherwise legit call `encodeFun(eoo.endOfOctets)`
+    eooIntegerSubstrate = (0, 0)
+    eooOctetsSubstrate = bytes(eooIntegerSubstrate)
+
+    # noinspection PyMethodMayBeStatic
+    def encodeTag(self, singleTag, isConstructed):
+        tagClass, tagFormat, tagId = singleTag
+        encodedTag = tagClass | tagFormat
+        if isConstructed:
+            encodedTag |= tag.tagFormatConstructed
+
+        if tagId < 31:
+            return encodedTag | tagId,
+
+        else:
+            substrate = tagId & 0x7f,
+
+            tagId >>= 7
+
+            while tagId:
+                substrate = (0x80 | (tagId & 0x7f),) + substrate
+                tagId >>= 7
+
+            return (encodedTag | 0x1F,) + substrate
+
+    def encodeLength(self, length, defMode):
+        if not defMode and self.supportIndefLenMode:
+            return (0x80,)
+
+        if length < 0x80:
+            return length,
+
+        else:
+            substrate = ()
+            while length:
+                substrate = (length & 0xff,) + substrate
+                length >>= 8
+
+            substrateLen = len(substrate)
+
+            if substrateLen > 126:
+                raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen)
+
+            return (0x80 | substrateLen,) + substrate
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        raise error.PyAsn1Error('Not implemented')
+
+    def encode(self, value, asn1Spec=None, encodeFun=None, **options):
+
+        if asn1Spec is None:
+            tagSet = value.tagSet
+        else:
+            tagSet = asn1Spec.tagSet
+
+        # untagged item?
+        if not tagSet:
+            substrate, isConstructed, isOctets = self.encodeValue(
+                value, asn1Spec, encodeFun, **options
+            )
+            return substrate
+
+        defMode = options.get('defMode', True)
+
+        substrate = b''
+
+        for idx, singleTag in enumerate(tagSet.superTags):
+
+            defModeOverride = defMode
+
+            # base tag?
+            if not idx:
+                try:
+                    substrate, isConstructed, isOctets = self.encodeValue(
+                        value, asn1Spec, encodeFun, **options
+                    )
+
+                except error.PyAsn1Error as exc:
+                    raise error.PyAsn1Error(
+                        'Error encoding %r: %s' % (value, exc))
+
+                if LOG:
+                    LOG('encoded %svalue %s into %s' % (
+                        isConstructed and 'constructed ' or '', value, substrate
+                    ))
+
+                if not substrate and isConstructed and options.get('ifNotEmpty', False):
+                    return substrate
+
+                if not isConstructed:
+                    defModeOverride = True
+
+                    if LOG:
+                        LOG('overridden encoding mode into definitive for primitive type')
+
+            header = self.encodeTag(singleTag, isConstructed)
+
+            if LOG:
+                LOG('encoded %stag %s into %s' % (
+                    isConstructed and 'constructed ' or '',
+                    singleTag, debug.hexdump(bytes(header))))
+
+            header += self.encodeLength(len(substrate), defModeOverride)
+
+            if LOG:
+                LOG('encoded %s octets (tag + payload) into %s' % (
+                    len(substrate), debug.hexdump(bytes(header))))
+
+            if isOctets:
+                substrate = bytes(header) + substrate
+
+                if not defModeOverride:
+                    substrate += self.eooOctetsSubstrate
+
+            else:
+                substrate = header + substrate
+
+                if not defModeOverride:
+                    substrate += self.eooIntegerSubstrate
+
+        if not isOctets:
+            substrate = bytes(substrate)
+
+        return substrate
+
+
+class EndOfOctetsEncoder(AbstractItemEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        return b'', False, True
+
+
+class BooleanEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        return value and (1,) or (0,), False, False
+
+
+class IntegerEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+    supportCompactZero = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if value == 0:
+            if LOG:
+                LOG('encoding %spayload for zero INTEGER' % (
+                    self.supportCompactZero and 'no ' or ''
+                ))
+
+            # de-facto way to encode zero
+            if self.supportCompactZero:
+                return (), False, False
+            else:
+                return (0,), False, False
+
+        return to_bytes(int(value), signed=True), False, True
+
+
+class BitStringEncoder(AbstractItemEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is not None:
+            # TODO: try to avoid ASN.1 schema instantiation
+            value = asn1Spec.clone(value)
+
+        valueLength = len(value)
+        if valueLength % 8:
+            alignedValue = value << (8 - valueLength % 8)
+        else:
+            alignedValue = value
+
+        maxChunkSize = options.get('maxChunkSize', 0)
+        if not maxChunkSize or len(alignedValue) <= maxChunkSize * 8:
+            substrate = alignedValue.asOctets()
+            return bytes((len(substrate) * 8 - valueLength,)) + substrate, False, True
+
+        if LOG:
+            LOG('encoding into up to %s-octet chunks' % maxChunkSize)
+
+        baseTag = value.tagSet.baseTag
+
+        # strip off explicit tags
+        if baseTag:
+            tagSet = tag.TagSet(baseTag, baseTag)
+
+        else:
+            tagSet = tag.TagSet()
+
+        alignedValue = alignedValue.clone(tagSet=tagSet)
+
+        stop = 0
+        substrate = b''
+        while stop < valueLength:
+            start = stop
+            stop = min(start + maxChunkSize * 8, valueLength)
+            substrate += encodeFun(alignedValue[start:stop], asn1Spec, **options)
+
+        return substrate, True, True
+
+
+class OctetStringEncoder(AbstractItemEncoder):
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+        if asn1Spec is None:
+            substrate = value.asOctets()
+
+        elif not isinstance(value, bytes):
+            substrate = asn1Spec.clone(value).asOctets()
+
+        else:
+            substrate = value
+
+        maxChunkSize = options.get('maxChunkSize', 0)
+
+        if not maxChunkSize or len(substrate) <= maxChunkSize:
+            return substrate, False, True
+
+        if LOG:
+            LOG('encoding into up to %s-octet chunks' % maxChunkSize)
+
+        # strip off explicit tags for inner chunks
+
+        if asn1Spec is None:
+            baseTag = value.tagSet.baseTag
+
+            # strip off explicit tags
+            if baseTag:
+                tagSet = tag.TagSet(baseTag, baseTag)
+
+            else:
+                tagSet = tag.TagSet()
+
+            asn1Spec = value.clone(tagSet=tagSet)
+
+        elif not isinstance(value, bytes):
+            baseTag = asn1Spec.tagSet.baseTag
+
+            # strip off explicit tags
+            if baseTag:
+                tagSet = tag.TagSet(baseTag, baseTag)
+
+            else:
+                tagSet = tag.TagSet()
+
+            asn1Spec = asn1Spec.clone(tagSet=tagSet)
+
+        pos = 0
+        substrate = b''
+
+        while True:
+            chunk = value[pos:pos + maxChunkSize]
+            if not chunk:
+                break
+
+            substrate += encodeFun(chunk, asn1Spec, **options)
+            pos += maxChunkSize
+
+        return substrate, True, True
+
+
+class NullEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        return b'', False, True
+
+
+class ObjectIdentifierEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is not None:
+            value = asn1Spec.clone(value)
+
+        oid = value.asTuple()
+
+        # Build the first pair
+        try:
+            first = oid[0]
+            second = oid[1]
+
+        except IndexError:
+            raise error.PyAsn1Error('Short OID %s' % (value,))
+
+        if 0 <= second <= 39:
+            if first == 1:
+                oid = (second + 40,) + oid[2:]
+            elif first == 0:
+                oid = (second,) + oid[2:]
+            elif first == 2:
+                oid = (second + 80,) + oid[2:]
+            else:
+                raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
+
+        elif first == 2:
+            oid = (second + 80,) + oid[2:]
+
+        else:
+            raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
+
+        octets = ()
+
+        # Cycle through subIds
+        for subOid in oid:
+            if 0 <= subOid <= 127:
+                # Optimize for the common case
+                octets += (subOid,)
+
+            elif subOid > 127:
+                # Pack large Sub-Object IDs
+                res = (subOid & 0x7f,)
+                subOid >>= 7
+
+                while subOid:
+                    res = (0x80 | (subOid & 0x7f),) + res
+                    subOid >>= 7
+
+                # Add packed Sub-Object ID to resulted Object ID
+                octets += res
+
+            else:
+                raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value))
+
+        return octets, False, False
+
+
+class RelativeOIDEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is not None:
+            value = asn1Spec.clone(value)
+
+        octets = ()
+
+        # Cycle through subIds
+        for subOid in value.asTuple():
+            if 0 <= subOid <= 127:
+                # Optimize for the common case
+                octets += (subOid,)
+
+            elif subOid > 127:
+                # Pack large Sub-Object IDs
+                res = (subOid & 0x7f,)
+                subOid >>= 7
+
+                while subOid:
+                    res = (0x80 | (subOid & 0x7f),) + res
+                    subOid >>= 7
+
+                # Add packed Sub-Object ID to resulted RELATIVE-OID
+                octets += res
+
+            else:
+                raise error.PyAsn1Error('Negative RELATIVE-OID arc %s at %s' % (subOid, value))
+
+        return octets, False, False
+
+
+class RealEncoder(AbstractItemEncoder):
+    supportIndefLenMode = False
+    binEncBase = 2  # set to None to choose encoding base automatically
+
+    @staticmethod
+    def _dropFloatingPoint(m, encbase, e):
+        ms, es = 1, 1
+        if m < 0:
+            ms = -1  # mantissa sign
+
+        if e < 0:
+            es = -1  # exponent sign
+
+        m *= ms
+
+        if encbase == 8:
+            m *= 2 ** (abs(e) % 3 * es)
+            e = abs(e) // 3 * es
+
+        elif encbase == 16:
+            m *= 2 ** (abs(e) % 4 * es)
+            e = abs(e) // 4 * es
+
+        while True:
+            if int(m) != m:
+                m *= encbase
+                e -= 1
+                continue
+            break
+
+        return ms, int(m), encbase, e
+
+    def _chooseEncBase(self, value):
+        m, b, e = value
+        encBase = [2, 8, 16]
+        if value.binEncBase in encBase:
+            return self._dropFloatingPoint(m, value.binEncBase, e)
+
+        elif self.binEncBase in encBase:
+            return self._dropFloatingPoint(m, self.binEncBase, e)
+
+        # auto choosing base 2/8/16
+        mantissa = [m, m, m]
+        exponent = [e, e, e]
+        sign = 1
+        encbase = 2
+        e = float('inf')
+
+        for i in range(3):
+            (sign,
+             mantissa[i],
+             encBase[i],
+             exponent[i]) = self._dropFloatingPoint(mantissa[i], encBase[i], exponent[i])
+
+            if abs(exponent[i]) < abs(e) or (abs(exponent[i]) == abs(e) and mantissa[i] < m):
+                e = exponent[i]
+                m = int(mantissa[i])
+                encbase = encBase[i]
+
+        if LOG:
+            LOG('automatically chosen REAL encoding base %s, sign %s, mantissa %s, '
+                'exponent %s' % (encbase, sign, m, e))
+
+        return sign, m, encbase, e
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is not None:
+            value = asn1Spec.clone(value)
+
+        if value.isPlusInf:
+            return (0x40,), False, False
+
+        if value.isMinusInf:
+            return (0x41,), False, False
+
+        m, b, e = value
+
+        if not m:
+            return b'', False, True
+
+        if b == 10:
+            if LOG:
+                LOG('encoding REAL into character form')
+
+            return b'\x03%dE%s%d' % (m, e == 0 and b'+' or b'', e), False, True
+
+        elif b == 2:
+            fo = 0x80  # binary encoding
+            ms, m, encbase, e = self._chooseEncBase(value)
+
+            if ms < 0:  # mantissa sign
+                fo |= 0x40  # sign bit
+
+            # exponent & mantissa normalization
+            if encbase == 2:
+                while m & 0x1 == 0:
+                    m >>= 1
+                    e += 1
+
+            elif encbase == 8:
+                while m & 0x7 == 0:
+                    m >>= 3
+                    e += 1
+                fo |= 0x10
+
+            else:  # encbase = 16
+                while m & 0xf == 0:
+                    m >>= 4
+                    e += 1
+                fo |= 0x20
+
+            sf = 0  # scale factor
+
+            while m & 0x1 == 0:
+                m >>= 1
+                sf += 1
+
+            if sf > 3:
+                raise error.PyAsn1Error('Scale factor overflow')  # bug if raised
+
+            fo |= sf << 2
+            eo = b''
+            if e == 0 or e == -1:
+                eo = bytes((e & 0xff,))
+
+            else:
+                while e not in (0, -1):
+                    eo = bytes((e & 0xff,)) + eo
+                    e >>= 8
+
+                if e == 0 and eo and eo[0] & 0x80:
+                    eo = bytes((0,)) + eo
+
+                if e == -1 and eo and not (eo[0] & 0x80):
+                    eo = bytes((0xff,)) + eo
+
+            n = len(eo)
+            if n > 0xff:
+                raise error.PyAsn1Error('Real exponent overflow')
+
+            if n == 1:
+                pass
+
+            elif n == 2:
+                fo |= 1
+
+            elif n == 3:
+                fo |= 2
+
+            else:
+                fo |= 3
+                eo = bytes((n & 0xff,)) + eo
+
+            po = b''
+
+            while m:
+                po = bytes((m & 0xff,)) + po
+                m >>= 8
+
+            substrate = bytes((fo,)) + eo + po
+
+            return substrate, False, True
+
+        else:
+            raise error.PyAsn1Error('Prohibited Real base %s' % b)
+
+
+class SequenceEncoder(AbstractItemEncoder):
+    omitEmptyOptionals = False
+
+    # TODO: handling three flavors of input is too much -- split over codecs
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+        substrate = b''
+
+        omitEmptyOptionals = options.get(
+            'omitEmptyOptionals', self.omitEmptyOptionals)
+
+        if LOG:
+            LOG('%sencoding empty OPTIONAL components' % (
+                    omitEmptyOptionals and 'not ' or ''))
+
+        if asn1Spec is None:
+            # instance of ASN.1 schema
+            inconsistency = value.isInconsistent
+            if inconsistency:
+                raise error.PyAsn1Error(
+                    f"ASN.1 object {value.__class__.__name__} is inconsistent")
+
+            namedTypes = value.componentType
+
+            for idx, component in enumerate(value.values()):
+                if namedTypes:
+                    namedType = namedTypes[idx]
+
+                    if namedType.isOptional and not component.isValue:
+                        if LOG:
+                            LOG('not encoding OPTIONAL component %r' % (namedType,))
+                        continue
+
+                    if namedType.isDefaulted and component == namedType.asn1Object:
+                        if LOG:
+                            LOG('not encoding DEFAULT component %r' % (namedType,))
+                        continue
+
+                    if omitEmptyOptionals:
+                        options.update(ifNotEmpty=namedType.isOptional)
+
+                # wrap open type blob if needed
+                if namedTypes and namedType.openType:
+
+                    wrapType = namedType.asn1Object
+
+                    if wrapType.typeId in (
+                            univ.SetOf.typeId, univ.SequenceOf.typeId):
+
+                        substrate += encodeFun(
+                                component, asn1Spec,
+                                **dict(options, wrapType=wrapType.componentType))
+
+                    else:
+                        chunk = encodeFun(component, asn1Spec, **options)
+
+                        if wrapType.isSameTypeWith(component):
+                            substrate += chunk
+
+                        else:
+                            substrate += encodeFun(chunk, wrapType, **options)
+
+                            if LOG:
+                                LOG('wrapped with wrap type %r' % (wrapType,))
+
+                else:
+                    substrate += encodeFun(component, asn1Spec, **options)
+
+        else:
+            # bare Python value + ASN.1 schema
+            for idx, namedType in enumerate(asn1Spec.componentType.namedTypes):
+
+                try:
+                    component = value[namedType.name]
+
+                except KeyError:
+                    raise error.PyAsn1Error('Component name "%s" not found in %r' % (
+                        namedType.name, value))
+
+                if namedType.isOptional and namedType.name not in value:
+                    if LOG:
+                        LOG('not encoding OPTIONAL component %r' % (namedType,))
+                    continue
+
+                if namedType.isDefaulted and component == namedType.asn1Object:
+                    if LOG:
+                        LOG('not encoding DEFAULT component %r' % (namedType,))
+                    continue
+
+                if omitEmptyOptionals:
+                    options.update(ifNotEmpty=namedType.isOptional)
+
+                componentSpec = namedType.asn1Object
+
+                # wrap open type blob if needed
+                if namedType.openType:
+
+                    if componentSpec.typeId in (
+                            univ.SetOf.typeId, univ.SequenceOf.typeId):
+
+                        substrate += encodeFun(
+                                component, componentSpec,
+                                **dict(options, wrapType=componentSpec.componentType))
+
+                    else:
+                        chunk = encodeFun(component, componentSpec, **options)
+
+                        if componentSpec.isSameTypeWith(component):
+                            substrate += chunk
+
+                        else:
+                            substrate += encodeFun(chunk, componentSpec, **options)
+
+                            if LOG:
+                                LOG('wrapped with wrap type %r' % (componentSpec,))
+
+                else:
+                    substrate += encodeFun(component, componentSpec, **options)
+
+        return substrate, True, True
+
+
+class SequenceOfEncoder(AbstractItemEncoder):
+    def _encodeComponents(self, value, asn1Spec, encodeFun, **options):
+
+        if asn1Spec is None:
+            inconsistency = value.isInconsistent
+            if inconsistency:
+                raise error.PyAsn1Error(
+                    f"ASN.1 object {value.__class__.__name__} is inconsistent")
+
+        else:
+            asn1Spec = asn1Spec.componentType
+
+        chunks = []
+
+        wrapType = options.pop('wrapType', None)
+
+        for idx, component in enumerate(value):
+            chunk = encodeFun(component, asn1Spec, **options)
+
+            if (wrapType is not None and
+                    not wrapType.isSameTypeWith(component)):
+                # wrap encoded value with wrapper container (e.g. ANY)
+                chunk = encodeFun(chunk, wrapType, **options)
+
+                if LOG:
+                    LOG('wrapped with wrap type %r' % (wrapType,))
+
+            chunks.append(chunk)
+
+        return chunks
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        chunks = self._encodeComponents(
+            value, asn1Spec, encodeFun, **options)
+
+        return b''.join(chunks), True, True
+
+
+class ChoiceEncoder(AbstractItemEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is None:
+            component = value.getComponent()
+        else:
+            names = [namedType.name for namedType in asn1Spec.componentType.namedTypes
+                     if namedType.name in value]
+            if len(names) != 1:
+                raise error.PyAsn1Error('%s components for Choice at %r' % (len(names) and 'Multiple ' or 'None ', value))
+
+            name = names[0]
+
+            component = value[name]
+            asn1Spec = asn1Spec[name]
+
+        return encodeFun(component, asn1Spec, **options), True, True
+
+
+class AnyEncoder(OctetStringEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if asn1Spec is None:
+            value = value.asOctets()
+        elif not isinstance(value, bytes):
+            value = asn1Spec.clone(value).asOctets()
+
+        return value, not options.get('defMode', True), True
+
+
+TAG_MAP = {
+    eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
+    univ.Boolean.tagSet: BooleanEncoder(),
+    univ.Integer.tagSet: IntegerEncoder(),
+    univ.BitString.tagSet: BitStringEncoder(),
+    univ.OctetString.tagSet: OctetStringEncoder(),
+    univ.Null.tagSet: NullEncoder(),
+    univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
+    univ.RelativeOID.tagSet: RelativeOIDEncoder(),
+    univ.Enumerated.tagSet: IntegerEncoder(),
+    univ.Real.tagSet: RealEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.SequenceOf.tagSet: SequenceOfEncoder(),
+    univ.SetOf.tagSet: SequenceOfEncoder(),
+    univ.Choice.tagSet: ChoiceEncoder(),
+    # character string types
+    char.UTF8String.tagSet: OctetStringEncoder(),
+    char.NumericString.tagSet: OctetStringEncoder(),
+    char.PrintableString.tagSet: OctetStringEncoder(),
+    char.TeletexString.tagSet: OctetStringEncoder(),
+    char.VideotexString.tagSet: OctetStringEncoder(),
+    char.IA5String.tagSet: OctetStringEncoder(),
+    char.GraphicString.tagSet: OctetStringEncoder(),
+    char.VisibleString.tagSet: OctetStringEncoder(),
+    char.GeneralString.tagSet: OctetStringEncoder(),
+    char.UniversalString.tagSet: OctetStringEncoder(),
+    char.BMPString.tagSet: OctetStringEncoder(),
+    # useful types
+    useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
+    useful.GeneralizedTime.tagSet: OctetStringEncoder(),
+    useful.UTCTime.tagSet: OctetStringEncoder()
+}
+
+# Put in ambiguous & non-ambiguous types for faster codec lookup
+TYPE_MAP = {
+    univ.Boolean.typeId: BooleanEncoder(),
+    univ.Integer.typeId: IntegerEncoder(),
+    univ.BitString.typeId: BitStringEncoder(),
+    univ.OctetString.typeId: OctetStringEncoder(),
+    univ.Null.typeId: NullEncoder(),
+    univ.ObjectIdentifier.typeId: ObjectIdentifierEncoder(),
+    univ.RelativeOID.typeId: RelativeOIDEncoder(),
+    univ.Enumerated.typeId: IntegerEncoder(),
+    univ.Real.typeId: RealEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.Set.typeId: SequenceEncoder(),
+    univ.SetOf.typeId: SequenceOfEncoder(),
+    univ.Sequence.typeId: SequenceEncoder(),
+    univ.SequenceOf.typeId: SequenceOfEncoder(),
+    univ.Choice.typeId: ChoiceEncoder(),
+    univ.Any.typeId: AnyEncoder(),
+    # character string types
+    char.UTF8String.typeId: OctetStringEncoder(),
+    char.NumericString.typeId: OctetStringEncoder(),
+    char.PrintableString.typeId: OctetStringEncoder(),
+    char.TeletexString.typeId: OctetStringEncoder(),
+    char.VideotexString.typeId: OctetStringEncoder(),
+    char.IA5String.typeId: OctetStringEncoder(),
+    char.GraphicString.typeId: OctetStringEncoder(),
+    char.VisibleString.typeId: OctetStringEncoder(),
+    char.GeneralString.typeId: OctetStringEncoder(),
+    char.UniversalString.typeId: OctetStringEncoder(),
+    char.BMPString.typeId: OctetStringEncoder(),
+    # useful types
+    useful.ObjectDescriptor.typeId: OctetStringEncoder(),
+    useful.GeneralizedTime.typeId: OctetStringEncoder(),
+    useful.UTCTime.typeId: OctetStringEncoder()
+}
+
+
+class SingleItemEncoder(object):
+    fixedDefLengthMode = None
+    fixedChunkSize = None
+
+    TAG_MAP = TAG_MAP
+    TYPE_MAP = TYPE_MAP
+
+    def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored):
+        self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP
+        self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP
+
+    def __call__(self, value, asn1Spec=None, **options):
+        try:
+            if asn1Spec is None:
+                typeId = value.typeId
+            else:
+                typeId = asn1Spec.typeId
+
+        except AttributeError:
+            raise error.PyAsn1Error('Value %r is not ASN.1 type instance '
+                                    'and "asn1Spec" not given' % (value,))
+
+        if LOG:
+            LOG('encoder called in %sdef mode, chunk size %s for type %s, '
+                'value:\n%s' % (not options.get('defMode', True) and 'in' or '',
+                                options.get('maxChunkSize', 0),
+                                asn1Spec is None and value.prettyPrintType() or
+                                asn1Spec.prettyPrintType(), value))
+
+        if self.fixedDefLengthMode is not None:
+            options.update(defMode=self.fixedDefLengthMode)
+
+        if self.fixedChunkSize is not None:
+            options.update(maxChunkSize=self.fixedChunkSize)
+
+        try:
+            concreteEncoder = self._typeMap[typeId]
+
+            if LOG:
+                LOG('using value codec %s chosen by type ID '
+                    '%s' % (concreteEncoder.__class__.__name__, typeId))
+
+        except KeyError:
+            if asn1Spec is None:
+                tagSet = value.tagSet
+            else:
+                tagSet = asn1Spec.tagSet
+
+            # use base type for codec lookup to recover untagged types
+            baseTagSet = tag.TagSet(tagSet.baseTag, tagSet.baseTag)
+
+            try:
+                concreteEncoder = self._tagMap[baseTagSet]
+
+            except KeyError:
+                raise error.PyAsn1Error('No encoder for %r (%s)' % (value, tagSet))
+
+            if LOG:
+                LOG('using value codec %s chosen by tagSet '
+                    '%s' % (concreteEncoder.__class__.__name__, tagSet))
+
+        substrate = concreteEncoder.encode(value, asn1Spec, self, **options)
+
+        if LOG:
+            LOG('codec %s built %s octets of substrate: %s\nencoder '
+                'completed' % (concreteEncoder, len(substrate),
+                               debug.hexdump(substrate)))
+
+        return substrate
+
+
+class Encoder(object):
+    SINGLE_ITEM_ENCODER = SingleItemEncoder
+
+    def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **options):
+        self._singleItemEncoder = self.SINGLE_ITEM_ENCODER(
+            tagMap=tagMap, typeMap=typeMap, **options
+        )
+
+    def __call__(self, pyObject, asn1Spec=None, **options):
+        return self._singleItemEncoder(
+            pyObject, asn1Spec=asn1Spec, **options)
+
+
+#: Turns ASN.1 object into BER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a BER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#:     parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#:     Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: defMode: :py:class:`bool`
+#:     If :obj:`False`, produces indefinite length encoding
+#:
+#: maxChunkSize: :py:class:`int`
+#:     Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes`
+#:     Given ASN.1 object encoded into BER octetstream
+#:
+#: Raises
+#: ------
+#: ~pyasn1.error.PyAsn1Error
+#:     On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into BER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> encode([1, 2, 3], asn1Spec=seq)
+#:    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+#: Encode ASN.1 value object into BER
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> seq.extend([1, 2, 3])
+#:    >>> encode(seq)
+#:    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+encode = Encoder()
+
+def __getattr__(attr: str):
+    if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
+        warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
+        return globals()[newAttr]
+    raise AttributeError(attr)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/eoo.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/eoo.py
new file mode 100644
index 00000000..8c91a3d2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/ber/eoo.py
@@ -0,0 +1,28 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+from pyasn1.type import base
+from pyasn1.type import tag
+
+__all__ = ['endOfOctets']
+
+
+class EndOfOctets(base.SimpleAsn1Type):
+    defaultValue = 0
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
+    )
+
+    _instance = None
+
+    def __new__(cls, *args, **kwargs):
+        if cls._instance is None:
+            cls._instance = object.__new__(cls, *args, **kwargs)
+
+        return cls._instance
+
+
+endOfOctets = EndOfOctets()
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/__init__.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/__init__.py
new file mode 100644
index 00000000..8c3066b2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/decoder.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/decoder.py
new file mode 100644
index 00000000..d6890f01
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/decoder.py
@@ -0,0 +1,149 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import warnings
+
+from pyasn1 import error
+from pyasn1.codec.streaming import readFromStream
+from pyasn1.codec.ber import decoder
+from pyasn1.type import univ
+
+__all__ = ['decode', 'StreamingDecoder']
+
+SubstrateUnderrunError = error.SubstrateUnderrunError
+
+
+class BooleanPayloadDecoder(decoder.AbstractSimplePayloadDecoder):
+    protoComponent = univ.Boolean(0)
+
+    def valueDecoder(self, substrate, asn1Spec,
+                     tagSet=None, length=None, state=None,
+                     decodeFun=None, substrateFun=None,
+                     **options):
+
+        if length != 1:
+            raise error.PyAsn1Error('Not single-octet Boolean payload')
+
+        for chunk in readFromStream(substrate, length, options):
+            if isinstance(chunk, SubstrateUnderrunError):
+                yield chunk
+
+        byte = chunk[0]
+
+        # CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while
+        # BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1 
+        # in https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
+        if byte == 0xff:
+            value = 1
+
+        elif byte == 0x00:
+            value = 0
+
+        else:
+            raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
+
+        yield self._createComponent(asn1Spec, tagSet, value, **options)
+
+
+# TODO: prohibit non-canonical encoding
+BitStringPayloadDecoder = decoder.BitStringPayloadDecoder
+OctetStringPayloadDecoder = decoder.OctetStringPayloadDecoder
+RealPayloadDecoder = decoder.RealPayloadDecoder
+
+TAG_MAP = decoder.TAG_MAP.copy()
+TAG_MAP.update(
+    {univ.Boolean.tagSet: BooleanPayloadDecoder(),
+     univ.BitString.tagSet: BitStringPayloadDecoder(),
+     univ.OctetString.tagSet: OctetStringPayloadDecoder(),
+     univ.Real.tagSet: RealPayloadDecoder()}
+)
+
+TYPE_MAP = decoder.TYPE_MAP.copy()
+
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in TAG_MAP.values():
+    if typeDecoder.protoComponent is not None:
+        typeId = typeDecoder.protoComponent.__class__.typeId
+        if typeId is not None and typeId not in TYPE_MAP:
+            TYPE_MAP[typeId] = typeDecoder
+
+
+class SingleItemDecoder(decoder.SingleItemDecoder):
+    __doc__ = decoder.SingleItemDecoder.__doc__
+
+    TAG_MAP = TAG_MAP
+    TYPE_MAP = TYPE_MAP
+
+
+class StreamingDecoder(decoder.StreamingDecoder):
+    __doc__ = decoder.StreamingDecoder.__doc__
+
+    SINGLE_ITEM_DECODER = SingleItemDecoder
+
+
+class Decoder(decoder.Decoder):
+    __doc__ = decoder.Decoder.__doc__
+
+    STREAMING_DECODER = StreamingDecoder
+
+
+#: Turns CER octet stream into an ASN.1 object.
+#:
+#: Takes CER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes`
+#:     CER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#:     being decoded, *asn1Spec* may or may not be required. Most common reason for
+#:     it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#:     A tuple of pyasn1 object recovered from CER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError
+#:     On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode CER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00')
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+#: Decode CER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00', asn1Spec=seq)
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+decode = Decoder()
+
+def __getattr__(attr: str):
+    if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
+        warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
+        return globals()[newAttr]
+    raise AttributeError(attr)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/encoder.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/encoder.py
new file mode 100644
index 00000000..e16e9ece
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/cer/encoder.py
@@ -0,0 +1,331 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import warnings
+
+from pyasn1 import error
+from pyasn1.codec.ber import encoder
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['Encoder', 'encode']
+
+
+class BooleanEncoder(encoder.IntegerEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        if value == 0:
+            substrate = (0,)
+        else:
+            substrate = (255,)
+        return substrate, False, False
+
+
+class RealEncoder(encoder.RealEncoder):
+    def _chooseEncBase(self, value):
+        m, b, e = value
+        return self._dropFloatingPoint(m, b, e)
+
+
+# specialized GeneralStringEncoder here
+
+class TimeEncoderMixIn(object):
+    Z_CHAR = ord('Z')
+    PLUS_CHAR = ord('+')
+    MINUS_CHAR = ord('-')
+    COMMA_CHAR = ord(',')
+    DOT_CHAR = ord('.')
+    ZERO_CHAR = ord('0')
+
+    MIN_LENGTH = 12
+    MAX_LENGTH = 19
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        # CER encoding constraints:
+        # - minutes are mandatory, seconds are optional
+        # - sub-seconds must NOT be zero / no meaningless zeros
+        # - no hanging fraction dot
+        # - time in UTC (Z)
+        # - only dot is allowed for fractions
+
+        if asn1Spec is not None:
+            value = asn1Spec.clone(value)
+
+        numbers = value.asNumbers()
+
+        if self.PLUS_CHAR in numbers or self.MINUS_CHAR in numbers:
+            raise error.PyAsn1Error('Must be UTC time: %r' % value)
+
+        if numbers[-1] != self.Z_CHAR:
+            raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % value)
+
+        if self.COMMA_CHAR in numbers:
+            raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value)
+
+        if self.DOT_CHAR in numbers:
+
+            isModified = False
+
+            numbers = list(numbers)
+
+            searchIndex = min(numbers.index(self.DOT_CHAR) + 4, len(numbers) - 1)
+
+            while numbers[searchIndex] != self.DOT_CHAR:
+                if numbers[searchIndex] == self.ZERO_CHAR:
+                    del numbers[searchIndex]
+                    isModified = True
+
+                searchIndex -= 1
+
+            searchIndex += 1
+
+            if searchIndex < len(numbers):
+                if numbers[searchIndex] == self.Z_CHAR:
+                    # drop hanging comma
+                    del numbers[searchIndex - 1]
+                    isModified = True
+
+            if isModified:
+                value = value.clone(numbers)
+
+        if not self.MIN_LENGTH < len(numbers) < self.MAX_LENGTH:
+            raise error.PyAsn1Error('Length constraint violated: %r' % value)
+
+        options.update(maxChunkSize=1000)
+
+        return encoder.OctetStringEncoder.encodeValue(
+            self, value, asn1Spec, encodeFun, **options
+        )
+
+
+class GeneralizedTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
+    MIN_LENGTH = 12
+    MAX_LENGTH = 20
+
+
+class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
+    MIN_LENGTH = 10
+    MAX_LENGTH = 14
+
+
+class SetOfEncoder(encoder.SequenceOfEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+        chunks = self._encodeComponents(
+            value, asn1Spec, encodeFun, **options)
+
+        # sort by serialised and padded components
+        if len(chunks) > 1:
+            zero = b'\x00'
+            maxLen = max(map(len, chunks))
+            paddedChunks = [
+                (x.ljust(maxLen, zero), x) for x in chunks
+            ]
+            paddedChunks.sort(key=lambda x: x[0])
+
+            chunks = [x[1] for x in paddedChunks]
+
+        return b''.join(chunks), True, True
+
+
+class SequenceOfEncoder(encoder.SequenceOfEncoder):
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+        if options.get('ifNotEmpty', False) and not len(value):
+            return b'', True, True
+
+        chunks = self._encodeComponents(
+            value, asn1Spec, encodeFun, **options)
+
+        return b''.join(chunks), True, True
+
+
+class SetEncoder(encoder.SequenceEncoder):
+    @staticmethod
+    def _componentSortKey(componentAndType):
+        """Sort SET components by tag
+
+        Sort regardless of the Choice value (static sort)
+        """
+        component, asn1Spec = componentAndType
+
+        if asn1Spec is None:
+            asn1Spec = component
+
+        if asn1Spec.typeId == univ.Choice.typeId and not asn1Spec.tagSet:
+            if asn1Spec.tagSet:
+                return asn1Spec.tagSet
+            else:
+                return asn1Spec.componentType.minTagSet
+        else:
+            return asn1Spec.tagSet
+
+    def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+        substrate = b''
+
+        comps = []
+        compsMap = {}
+
+        if asn1Spec is None:
+            # instance of ASN.1 schema
+            inconsistency = value.isInconsistent
+            if inconsistency:
+                raise error.PyAsn1Error(
+                    f"ASN.1 object {value.__class__.__name__} is inconsistent")
+
+            namedTypes = value.componentType
+
+            for idx, component in enumerate(value.values()):
+                if namedTypes:
+                    namedType = namedTypes[idx]
+
+                    if namedType.isOptional and not component.isValue:
+                            continue
+
+                    if namedType.isDefaulted and component == namedType.asn1Object:
+                            continue
+
+                    compsMap[id(component)] = namedType
+
+                else:
+                    compsMap[id(component)] = None
+
+                comps.append((component, asn1Spec))
+
+        else:
+            # bare Python value + ASN.1 schema
+            for idx, namedType in enumerate(asn1Spec.componentType.namedTypes):
+
+                try:
+                    component = value[namedType.name]
+
+                except KeyError:
+                    raise error.PyAsn1Error('Component name "%s" not found in %r' % (namedType.name, value))
+
+                if namedType.isOptional and namedType.name not in value:
+                    continue
+
+                if namedType.isDefaulted and component == namedType.asn1Object:
+                    continue
+
+                compsMap[id(component)] = namedType
+                comps.append((component, asn1Spec[idx]))
+
+        for comp, compType in sorted(comps, key=self._componentSortKey):
+            namedType = compsMap[id(comp)]
+
+            if namedType:
+                options.update(ifNotEmpty=namedType.isOptional)
+
+            chunk = encodeFun(comp, compType, **options)
+
+            # wrap open type blob if needed
+            if namedType and namedType.openType:
+                wrapType = namedType.asn1Object
+                if wrapType.tagSet and not wrapType.isSameTypeWith(comp):
+                    chunk = encodeFun(chunk, wrapType, **options)
+
+            substrate += chunk
+
+        return substrate, True, True
+
+
+class SequenceEncoder(encoder.SequenceEncoder):
+    omitEmptyOptionals = True
+
+
+TAG_MAP = encoder.TAG_MAP.copy()
+
+TAG_MAP.update({
+    univ.Boolean.tagSet: BooleanEncoder(),
+    univ.Real.tagSet: RealEncoder(),
+    useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
+    useful.UTCTime.tagSet: UTCTimeEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.SetOf.tagSet: SetOfEncoder(),
+    univ.Sequence.typeId: SequenceEncoder()
+})
+
+TYPE_MAP = encoder.TYPE_MAP.copy()
+
+TYPE_MAP.update({
+    univ.Boolean.typeId: BooleanEncoder(),
+    univ.Real.typeId: RealEncoder(),
+    useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
+    useful.UTCTime.typeId: UTCTimeEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.Set.typeId: SetEncoder(),
+    univ.SetOf.typeId: SetOfEncoder(),
+    univ.Sequence.typeId: SequenceEncoder(),
+    univ.SequenceOf.typeId: SequenceOfEncoder()
+})
+
+
+class SingleItemEncoder(encoder.SingleItemEncoder):
+    fixedDefLengthMode = False
+    fixedChunkSize = 1000
+
+    TAG_MAP = TAG_MAP
+    TYPE_MAP = TYPE_MAP
+
+
+class Encoder(encoder.Encoder):
+    SINGLE_ITEM_ENCODER = SingleItemEncoder
+
+
+#: Turns ASN.1 object into CER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a CER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#:     parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#:     Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes`
+#:     Given ASN.1 object encoded into BER octet-stream
+#:
+#: Raises
+#: ------
+#: ~pyasn1.error.PyAsn1Error
+#:     On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into CER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> encode([1, 2, 3], asn1Spec=seq)
+#:    b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
+#:
+#: Encode ASN.1 value object into CER
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> seq.extend([1, 2, 3])
+#:    >>> encode(seq)
+#:    b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
+#:
+encode = Encoder()
+
+# EncoderFactory queries class instance and builds a map of tags -> encoders
+
+def __getattr__(attr: str):
+    if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
+        warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
+        return globals()[newAttr]
+    raise AttributeError(attr)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/der/__init__.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/der/__init__.py
new file mode 100644
index 00000000..8c3066b2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/der/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/der/decoder.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/der/decoder.py
new file mode 100644
index 00000000..d31a8565
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/der/decoder.py
@@ -0,0 +1,120 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import warnings
+
+from pyasn1.codec.cer import decoder
+from pyasn1.type import univ
+
+__all__ = ['decode', 'StreamingDecoder']
+
+
+class BitStringPayloadDecoder(decoder.BitStringPayloadDecoder):
+    supportConstructedForm = False
+
+
+class OctetStringPayloadDecoder(decoder.OctetStringPayloadDecoder):
+    supportConstructedForm = False
+
+
+# TODO: prohibit non-canonical encoding
+RealPayloadDecoder = decoder.RealPayloadDecoder
+
+TAG_MAP = decoder.TAG_MAP.copy()
+TAG_MAP.update(
+    {univ.BitString.tagSet: BitStringPayloadDecoder(),
+     univ.OctetString.tagSet: OctetStringPayloadDecoder(),
+     univ.Real.tagSet: RealPayloadDecoder()}
+)
+
+TYPE_MAP = decoder.TYPE_MAP.copy()
+
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in TAG_MAP.values():
+    if typeDecoder.protoComponent is not None:
+        typeId = typeDecoder.protoComponent.__class__.typeId
+        if typeId is not None and typeId not in TYPE_MAP:
+            TYPE_MAP[typeId] = typeDecoder
+
+
+class SingleItemDecoder(decoder.SingleItemDecoder):
+    __doc__ = decoder.SingleItemDecoder.__doc__
+
+    TAG_MAP = TAG_MAP
+    TYPE_MAP = TYPE_MAP
+
+    supportIndefLength = False
+
+
+class StreamingDecoder(decoder.StreamingDecoder):
+    __doc__ = decoder.StreamingDecoder.__doc__
+
+    SINGLE_ITEM_DECODER = SingleItemDecoder
+
+
+class Decoder(decoder.Decoder):
+    __doc__ = decoder.Decoder.__doc__
+
+    STREAMING_DECODER = StreamingDecoder
+
+
+#: Turns DER octet stream into an ASN.1 object.
+#:
+#: Takes DER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes`
+#:     DER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#:     being decoded, *asn1Spec* may or may not be required. Most common reason for
+#:     it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#:     A tuple of pyasn1 object recovered from DER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError
+#:     On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode DER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+#: Decode DER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+decode = Decoder()
+
+def __getattr__(attr: str):
+    if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
+        warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
+        return globals()[newAttr]
+    raise AttributeError(attr)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/der/encoder.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/der/encoder.py
new file mode 100644
index 00000000..f9a36102
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/der/encoder.py
@@ -0,0 +1,126 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import warnings
+
+from pyasn1 import error
+from pyasn1.codec.cer import encoder
+from pyasn1.type import univ
+
+__all__ = ['Encoder', 'encode']
+
+
+class SetEncoder(encoder.SetEncoder):
+    @staticmethod
+    def _componentSortKey(componentAndType):
+        """Sort SET components by tag
+
+        Sort depending on the actual Choice value (dynamic sort)
+        """
+        component, asn1Spec = componentAndType
+
+        if asn1Spec is None:
+            compType = component
+        else:
+            compType = asn1Spec
+
+        if compType.typeId == univ.Choice.typeId and not compType.tagSet:
+            if asn1Spec is None:
+                return component.getComponent().tagSet
+            else:
+                # TODO: move out of sorting key function
+                names = [namedType.name for namedType in asn1Spec.componentType.namedTypes
+                         if namedType.name in component]
+                if len(names) != 1:
+                    raise error.PyAsn1Error(
+                        '%s components for Choice at %r' % (len(names) and 'Multiple ' or 'None ', component))
+
+                # TODO: support nested CHOICE ordering
+                return asn1Spec[names[0]].tagSet
+
+        else:
+            return compType.tagSet
+
+
+TAG_MAP = encoder.TAG_MAP.copy()
+
+TAG_MAP.update({
+    # Set & SetOf have same tags
+    univ.Set.tagSet: SetEncoder()
+})
+
+TYPE_MAP = encoder.TYPE_MAP.copy()
+
+TYPE_MAP.update({
+    # Set & SetOf have same tags
+    univ.Set.typeId: SetEncoder()
+})
+
+
+class SingleItemEncoder(encoder.SingleItemEncoder):
+    fixedDefLengthMode = True
+    fixedChunkSize = 0
+
+    TAG_MAP = TAG_MAP
+    TYPE_MAP = TYPE_MAP
+
+
+class Encoder(encoder.Encoder):
+    SINGLE_ITEM_ENCODER = SingleItemEncoder
+
+
+#: Turns ASN.1 object into DER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a DER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#:     parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#:     Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes`
+#:     Given ASN.1 object encoded into BER octet-stream
+#:
+#: Raises
+#: ------
+#: ~pyasn1.error.PyAsn1Error
+#:     On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into DER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> encode([1, 2, 3], asn1Spec=seq)
+#:    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+#: Encode ASN.1 value object into DER
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> seq.extend([1, 2, 3])
+#:    >>> encode(seq)
+#:    b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+encode = Encoder()
+
+def __getattr__(attr: str):
+    if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
+        warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
+        return globals()[newAttr]
+    raise AttributeError(attr)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/native/__init__.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/native/__init__.py
new file mode 100644
index 00000000..8c3066b2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/native/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/native/decoder.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/native/decoder.py
new file mode 100644
index 00000000..9ac01ff6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/native/decoder.py
@@ -0,0 +1,244 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import warnings
+
+from pyasn1 import debug
+from pyasn1 import error
+from pyasn1.compat import _MISSING
+from pyasn1.type import base
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['decode']
+
+LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER)
+
+
+class AbstractScalarPayloadDecoder(object):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        return asn1Spec.clone(pyObject)
+
+
+class BitStringPayloadDecoder(AbstractScalarPayloadDecoder):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        return asn1Spec.clone(univ.BitString.fromBinaryString(pyObject))
+
+
+class SequenceOrSetPayloadDecoder(object):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        asn1Value = asn1Spec.clone()
+
+        componentsTypes = asn1Spec.componentType
+
+        for field in asn1Value:
+            if field in pyObject:
+                asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options)
+
+        return asn1Value
+
+
+class SequenceOfOrSetOfPayloadDecoder(object):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        asn1Value = asn1Spec.clone()
+
+        for pyValue in pyObject:
+            asn1Value.append(decodeFun(pyValue, asn1Spec.componentType), **options)
+
+        return asn1Value
+
+
+class ChoicePayloadDecoder(object):
+    def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
+        asn1Value = asn1Spec.clone()
+
+        componentsTypes = asn1Spec.componentType
+
+        for field in pyObject:
+            if field in componentsTypes:
+                asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options)
+                break
+
+        return asn1Value
+
+
+TAG_MAP = {
+    univ.Integer.tagSet: AbstractScalarPayloadDecoder(),
+    univ.Boolean.tagSet: AbstractScalarPayloadDecoder(),
+    univ.BitString.tagSet: BitStringPayloadDecoder(),
+    univ.OctetString.tagSet: AbstractScalarPayloadDecoder(),
+    univ.Null.tagSet: AbstractScalarPayloadDecoder(),
+    univ.ObjectIdentifier.tagSet: AbstractScalarPayloadDecoder(),
+    univ.RelativeOID.tagSet: AbstractScalarPayloadDecoder(),
+    univ.Enumerated.tagSet: AbstractScalarPayloadDecoder(),
+    univ.Real.tagSet: AbstractScalarPayloadDecoder(),
+    univ.Sequence.tagSet: SequenceOrSetPayloadDecoder(),  # conflicts with SequenceOf
+    univ.Set.tagSet: SequenceOrSetPayloadDecoder(),  # conflicts with SetOf
+    univ.Choice.tagSet: ChoicePayloadDecoder(),  # conflicts with Any
+    # character string types
+    char.UTF8String.tagSet: AbstractScalarPayloadDecoder(),
+    char.NumericString.tagSet: AbstractScalarPayloadDecoder(),
+    char.PrintableString.tagSet: AbstractScalarPayloadDecoder(),
+    char.TeletexString.tagSet: AbstractScalarPayloadDecoder(),
+    char.VideotexString.tagSet: AbstractScalarPayloadDecoder(),
+    char.IA5String.tagSet: AbstractScalarPayloadDecoder(),
+    char.GraphicString.tagSet: AbstractScalarPayloadDecoder(),
+    char.VisibleString.tagSet: AbstractScalarPayloadDecoder(),
+    char.GeneralString.tagSet: AbstractScalarPayloadDecoder(),
+    char.UniversalString.tagSet: AbstractScalarPayloadDecoder(),
+    char.BMPString.tagSet: AbstractScalarPayloadDecoder(),
+    # useful types
+    useful.ObjectDescriptor.tagSet: AbstractScalarPayloadDecoder(),
+    useful.GeneralizedTime.tagSet: AbstractScalarPayloadDecoder(),
+    useful.UTCTime.tagSet: AbstractScalarPayloadDecoder()
+}
+
+# Put in ambiguous & non-ambiguous types for faster codec lookup
+TYPE_MAP = {
+    univ.Integer.typeId: AbstractScalarPayloadDecoder(),
+    univ.Boolean.typeId: AbstractScalarPayloadDecoder(),
+    univ.BitString.typeId: BitStringPayloadDecoder(),
+    univ.OctetString.typeId: AbstractScalarPayloadDecoder(),
+    univ.Null.typeId: AbstractScalarPayloadDecoder(),
+    univ.ObjectIdentifier.typeId: AbstractScalarPayloadDecoder(),
+    univ.RelativeOID.typeId: AbstractScalarPayloadDecoder(),
+    univ.Enumerated.typeId: AbstractScalarPayloadDecoder(),
+    univ.Real.typeId: AbstractScalarPayloadDecoder(),
+    # ambiguous base types
+    univ.Set.typeId: SequenceOrSetPayloadDecoder(),
+    univ.SetOf.typeId: SequenceOfOrSetOfPayloadDecoder(),
+    univ.Sequence.typeId: SequenceOrSetPayloadDecoder(),
+    univ.SequenceOf.typeId: SequenceOfOrSetOfPayloadDecoder(),
+    univ.Choice.typeId: ChoicePayloadDecoder(),
+    univ.Any.typeId: AbstractScalarPayloadDecoder(),
+    # character string types
+    char.UTF8String.typeId: AbstractScalarPayloadDecoder(),
+    char.NumericString.typeId: AbstractScalarPayloadDecoder(),
+    char.PrintableString.typeId: AbstractScalarPayloadDecoder(),
+    char.TeletexString.typeId: AbstractScalarPayloadDecoder(),
+    char.VideotexString.typeId: AbstractScalarPayloadDecoder(),
+    char.IA5String.typeId: AbstractScalarPayloadDecoder(),
+    char.GraphicString.typeId: AbstractScalarPayloadDecoder(),
+    char.VisibleString.typeId: AbstractScalarPayloadDecoder(),
+    char.GeneralString.typeId: AbstractScalarPayloadDecoder(),
+    char.UniversalString.typeId: AbstractScalarPayloadDecoder(),
+    char.BMPString.typeId: AbstractScalarPayloadDecoder(),
+    # useful types
+    useful.ObjectDescriptor.typeId: AbstractScalarPayloadDecoder(),
+    useful.GeneralizedTime.typeId: AbstractScalarPayloadDecoder(),
+    useful.UTCTime.typeId: AbstractScalarPayloadDecoder()
+}
+
+
+class SingleItemDecoder(object):
+
+    TAG_MAP = TAG_MAP
+    TYPE_MAP = TYPE_MAP
+
+    def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored):
+        self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP
+        self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP
+
+    def __call__(self, pyObject, asn1Spec, **options):
+
+        if LOG:
+            debug.scope.push(type(pyObject).__name__)
+            LOG('decoder called at scope %s, working with '
+                'type %s' % (debug.scope, type(pyObject).__name__))
+
+        if asn1Spec is None or not isinstance(asn1Spec, base.Asn1Item):
+            raise error.PyAsn1Error(
+                'asn1Spec is not valid (should be an instance of an ASN.1 '
+                'Item, not %s)' % asn1Spec.__class__.__name__)
+
+        try:
+            valueDecoder = self._typeMap[asn1Spec.typeId]
+
+        except KeyError:
+            # use base type for codec lookup to recover untagged types
+            baseTagSet = tag.TagSet(asn1Spec.tagSet.baseTag, asn1Spec.tagSet.baseTag)
+
+            try:
+                valueDecoder = self._tagMap[baseTagSet]
+
+            except KeyError:
+                raise error.PyAsn1Error('Unknown ASN.1 tag %s' % asn1Spec.tagSet)
+
+        if LOG:
+            LOG('calling decoder %s on Python type %s '
+                '<%s>' % (type(valueDecoder).__name__,
+                          type(pyObject).__name__, repr(pyObject)))
+
+        value = valueDecoder(pyObject, asn1Spec, self, **options)
+
+        if LOG:
+            LOG('decoder %s produced ASN.1 type %s '
+                '<%s>' % (type(valueDecoder).__name__,
+                          type(value).__name__, repr(value)))
+            debug.scope.pop()
+
+        return value
+
+
+class Decoder(object):
+    SINGLE_ITEM_DECODER = SingleItemDecoder
+
+    def __init__(self, **options):
+        self._singleItemDecoder = self.SINGLE_ITEM_DECODER(**options)
+
+    def __call__(self, pyObject, asn1Spec=None, **kwargs):
+        return self._singleItemDecoder(pyObject, asn1Spec=asn1Spec, **kwargs)
+
+
+#: Turns Python objects of built-in types into ASN.1 objects.
+#:
+#: Takes Python objects of built-in types and turns them into a tree of
+#: ASN.1 objects (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: pyObject: :py:class:`object`
+#:     A scalar or nested Python objects
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A pyasn1 type object to act as a template guiding the decoder. It is required
+#:     for successful interpretation of Python objects mapping into their ASN.1
+#:     representations.
+#:
+#: Returns
+#: -------
+#: : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:     A scalar or constructed pyasn1 object
+#:
+#: Raises
+#: ------
+#: ~pyasn1.error.PyAsn1Error
+#:     On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode native Python object into ASN.1 objects with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> s, _ = decode([1, 2, 3], asn1Spec=seq)
+#:    >>> str(s)
+#:    SequenceOf:
+#:     1 2 3
+#:
+decode = Decoder()
+
+def __getattr__(attr: str):
+    if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
+        warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
+        return globals()[newAttr]
+    raise AttributeError(attr)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/native/encoder.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/native/encoder.py
new file mode 100644
index 00000000..6219ae45
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/native/encoder.py
@@ -0,0 +1,285 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+from collections import OrderedDict
+import warnings
+
+from pyasn1 import debug
+from pyasn1 import error
+from pyasn1.compat import _MISSING
+from pyasn1.type import base
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['encode']
+
+LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_ENCODER)
+
+
+class AbstractItemEncoder(object):
+    def encode(self, value, encodeFun, **options):
+        raise error.PyAsn1Error('Not implemented')
+
+
+class BooleanEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return bool(value)
+
+
+class IntegerEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return int(value)
+
+
+class BitStringEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return str(value)
+
+
+class OctetStringEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return value.asOctets()
+
+
+class TextStringEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return str(value)
+
+
+class NullEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return None
+
+
+class ObjectIdentifierEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return str(value)
+
+
+class RelativeOIDEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return str(value)
+
+
+class RealEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return float(value)
+
+
+class SetEncoder(AbstractItemEncoder):
+    protoDict = dict
+
+    def encode(self, value, encodeFun, **options):
+        inconsistency = value.isInconsistent
+        if inconsistency:
+            raise error.PyAsn1Error(
+                f"ASN.1 object {value.__class__.__name__} is inconsistent")
+
+        namedTypes = value.componentType
+        substrate = self.protoDict()
+
+        for idx, (key, subValue) in enumerate(value.items()):
+            if namedTypes and namedTypes[idx].isOptional and not value[idx].isValue:
+                continue
+            substrate[key] = encodeFun(subValue, **options)
+        return substrate
+
+
+class SequenceEncoder(SetEncoder):
+    protoDict = OrderedDict
+
+
+class SequenceOfEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        inconsistency = value.isInconsistent
+        if inconsistency:
+            raise error.PyAsn1Error(
+                f"ASN.1 object {value.__class__.__name__} is inconsistent")
+        return [encodeFun(x, **options) for x in value]
+
+
+class ChoiceEncoder(SequenceEncoder):
+    pass
+
+
+class AnyEncoder(AbstractItemEncoder):
+    def encode(self, value, encodeFun, **options):
+        return value.asOctets()
+
+
+TAG_MAP = {
+    univ.Boolean.tagSet: BooleanEncoder(),
+    univ.Integer.tagSet: IntegerEncoder(),
+    univ.BitString.tagSet: BitStringEncoder(),
+    univ.OctetString.tagSet: OctetStringEncoder(),
+    univ.Null.tagSet: NullEncoder(),
+    univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
+    univ.RelativeOID.tagSet: RelativeOIDEncoder(),
+    univ.Enumerated.tagSet: IntegerEncoder(),
+    univ.Real.tagSet: RealEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.SequenceOf.tagSet: SequenceOfEncoder(),
+    univ.SetOf.tagSet: SequenceOfEncoder(),
+    univ.Choice.tagSet: ChoiceEncoder(),
+    # character string types
+    char.UTF8String.tagSet: TextStringEncoder(),
+    char.NumericString.tagSet: TextStringEncoder(),
+    char.PrintableString.tagSet: TextStringEncoder(),
+    char.TeletexString.tagSet: TextStringEncoder(),
+    char.VideotexString.tagSet: TextStringEncoder(),
+    char.IA5String.tagSet: TextStringEncoder(),
+    char.GraphicString.tagSet: TextStringEncoder(),
+    char.VisibleString.tagSet: TextStringEncoder(),
+    char.GeneralString.tagSet: TextStringEncoder(),
+    char.UniversalString.tagSet: TextStringEncoder(),
+    char.BMPString.tagSet: TextStringEncoder(),
+    # useful types
+    useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
+    useful.GeneralizedTime.tagSet: OctetStringEncoder(),
+    useful.UTCTime.tagSet: OctetStringEncoder()
+}
+
+# Put in ambiguous & non-ambiguous types for faster codec lookup
+TYPE_MAP = {
+    univ.Boolean.typeId: BooleanEncoder(),
+    univ.Integer.typeId: IntegerEncoder(),
+    univ.BitString.typeId: BitStringEncoder(),
+    univ.OctetString.typeId: OctetStringEncoder(),
+    univ.Null.typeId: NullEncoder(),
+    univ.ObjectIdentifier.typeId: ObjectIdentifierEncoder(),
+    univ.RelativeOID.typeId: RelativeOIDEncoder(),
+    univ.Enumerated.typeId: IntegerEncoder(),
+    univ.Real.typeId: RealEncoder(),
+    # Sequence & Set have same tags as SequenceOf & SetOf
+    univ.Set.typeId: SetEncoder(),
+    univ.SetOf.typeId: SequenceOfEncoder(),
+    univ.Sequence.typeId: SequenceEncoder(),
+    univ.SequenceOf.typeId: SequenceOfEncoder(),
+    univ.Choice.typeId: ChoiceEncoder(),
+    univ.Any.typeId: AnyEncoder(),
+    # character string types
+    char.UTF8String.typeId: OctetStringEncoder(),
+    char.NumericString.typeId: OctetStringEncoder(),
+    char.PrintableString.typeId: OctetStringEncoder(),
+    char.TeletexString.typeId: OctetStringEncoder(),
+    char.VideotexString.typeId: OctetStringEncoder(),
+    char.IA5String.typeId: OctetStringEncoder(),
+    char.GraphicString.typeId: OctetStringEncoder(),
+    char.VisibleString.typeId: OctetStringEncoder(),
+    char.GeneralString.typeId: OctetStringEncoder(),
+    char.UniversalString.typeId: OctetStringEncoder(),
+    char.BMPString.typeId: OctetStringEncoder(),
+    # useful types
+    useful.ObjectDescriptor.typeId: OctetStringEncoder(),
+    useful.GeneralizedTime.typeId: OctetStringEncoder(),
+    useful.UTCTime.typeId: OctetStringEncoder()
+}
+
+
+class SingleItemEncoder(object):
+
+    TAG_MAP = TAG_MAP
+    TYPE_MAP = TYPE_MAP
+
+    def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored):
+        self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP
+        self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP
+
+    def __call__(self, value, **options):
+        if not isinstance(value, base.Asn1Item):
+            raise error.PyAsn1Error(
+                'value is not valid (should be an instance of an ASN.1 Item)')
+
+        if LOG:
+            debug.scope.push(type(value).__name__)
+            LOG('encoder called for type %s '
+                '<%s>' % (type(value).__name__, value.prettyPrint()))
+
+        tagSet = value.tagSet
+
+        try:
+            concreteEncoder = self._typeMap[value.typeId]
+
+        except KeyError:
+            # use base type for codec lookup to recover untagged types
+            baseTagSet = tag.TagSet(
+                value.tagSet.baseTag, value.tagSet.baseTag)
+
+            try:
+                concreteEncoder = self._tagMap[baseTagSet]
+
+            except KeyError:
+                raise error.PyAsn1Error('No encoder for %s' % (value,))
+
+        if LOG:
+            LOG('using value codec %s chosen by '
+                '%s' % (concreteEncoder.__class__.__name__, tagSet))
+
+        pyObject = concreteEncoder.encode(value, self, **options)
+
+        if LOG:
+            LOG('encoder %s produced: '
+                '%s' % (type(concreteEncoder).__name__, repr(pyObject)))
+            debug.scope.pop()
+
+        return pyObject
+
+
+class Encoder(object):
+    SINGLE_ITEM_ENCODER = SingleItemEncoder
+
+    def __init__(self, **options):
+        self._singleItemEncoder = self.SINGLE_ITEM_ENCODER(**options)
+
+    def __call__(self, pyObject, asn1Spec=None, **options):
+        return self._singleItemEncoder(
+            pyObject, asn1Spec=asn1Spec, **options)
+
+
+#: Turns ASN.1 object into a Python built-in type object(s).
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a Python built-in type or a tree
+#: of those.
+#:
+#: One exception is that instead of :py:class:`dict`, the :py:class:`OrderedDict`
+#: is used to preserve ordering of the components in ASN.1 SEQUENCE.
+#:
+#: Parameters
+#: ----------
+#  asn1Value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#:     pyasn1 object to encode (or a tree of them)
+#:
+#: Returns
+#: -------
+#: : :py:class:`object`
+#:     Python built-in type instance (or a tree of them)
+#:
+#: Raises
+#: ------
+#: ~pyasn1.error.PyAsn1Error
+#:     On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode ASN.1 value object into native Python types
+#:
+#: .. code-block:: pycon
+#:
+#:    >>> seq = SequenceOf(componentType=Integer())
+#:    >>> seq.extend([1, 2, 3])
+#:    >>> encode(seq)
+#:    [1, 2, 3]
+#:
+encode = SingleItemEncoder()
+
+def __getattr__(attr: str):
+    if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
+        warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
+        return globals()[newAttr]
+    raise AttributeError(attr)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/codec/streaming.py b/.venv/lib/python3.12/site-packages/pyasn1/codec/streaming.py
new file mode 100644
index 00000000..c35f2489
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/codec/streaming.py
@@ -0,0 +1,234 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import io
+import os
+
+from pyasn1 import error
+from pyasn1.type import univ
+
+class CachingStreamWrapper(io.IOBase):
+    """Wrapper around non-seekable streams.
+
+    Note that the implementation is tied to the decoder,
+    not checking for dangerous arguments for the sake
+    of performance.
+
+    The read bytes are kept in an internal cache until
+    setting _markedPosition which may reset the cache.
+    """
+    def __init__(self, raw):
+        self._raw = raw
+        self._cache = io.BytesIO()
+        self._markedPosition = 0
+
+    def peek(self, n):
+        result = self.read(n)
+        self._cache.seek(-len(result), os.SEEK_CUR)
+        return result
+
+    def seekable(self):
+        return True
+
+    def seek(self, n=-1, whence=os.SEEK_SET):
+        # Note that this not safe for seeking forward.
+        return self._cache.seek(n, whence)
+
+    def read(self, n=-1):
+        read_from_cache = self._cache.read(n)
+        if n != -1:
+            n -= len(read_from_cache)
+            if not n:  # 0 bytes left to read
+                return read_from_cache
+
+        read_from_raw = self._raw.read(n)
+
+        self._cache.write(read_from_raw)
+
+        return read_from_cache + read_from_raw
+
+    @property
+    def markedPosition(self):
+        """Position where the currently processed element starts.
+
+        This is used for back-tracking in SingleItemDecoder.__call__
+        and (indefLen)ValueDecoder and should not be used for other purposes.
+        The client is not supposed to ever seek before this position.
+        """
+        return self._markedPosition
+
+    @markedPosition.setter
+    def markedPosition(self, value):
+        # By setting the value, we ensure we won't seek back before it.
+        # `value` should be the same as the current position
+        # We don't check for this for performance reasons.
+        self._markedPosition = value
+
+        # Whenever we set _marked_position, we know for sure
+        # that we will not return back, and thus it is
+        # safe to drop all cached data.
+        if self._cache.tell() > io.DEFAULT_BUFFER_SIZE:
+            self._cache = io.BytesIO(self._cache.read())
+            self._markedPosition = 0
+
+    def tell(self):
+        return self._cache.tell()
+
+
+def asSeekableStream(substrate):
+    """Convert object to seekable byte-stream.
+
+    Parameters
+    ----------
+    substrate: :py:class:`bytes` or :py:class:`io.IOBase` or :py:class:`univ.OctetString`
+
+    Returns
+    -------
+    : :py:class:`io.IOBase`
+
+    Raises
+    ------
+    : :py:class:`~pyasn1.error.PyAsn1Error`
+        If the supplied substrate cannot be converted to a seekable stream.
+    """
+    if isinstance(substrate, io.BytesIO):
+        return substrate
+
+    elif isinstance(substrate, bytes):
+        return io.BytesIO(substrate)
+
+    elif isinstance(substrate, univ.OctetString):
+        return io.BytesIO(substrate.asOctets())
+
+    try:
+        if substrate.seekable():  # Will fail for most invalid types
+            return substrate
+        else:
+            return CachingStreamWrapper(substrate)
+
+    except AttributeError:
+        raise error.UnsupportedSubstrateError(
+            "Cannot convert " + substrate.__class__.__name__ +
+            " to a seekable bit stream.")
+
+
+def isEndOfStream(substrate):
+    """Check whether we have reached the end of a stream.
+
+    Although it is more effective to read and catch exceptions, this
+    function
+
+    Parameters
+    ----------
+    substrate: :py:class:`IOBase`
+        Stream to check
+
+    Returns
+    -------
+    : :py:class:`bool`
+    """
+    if isinstance(substrate, io.BytesIO):
+        cp = substrate.tell()
+        substrate.seek(0, os.SEEK_END)
+        result = substrate.tell() == cp
+        substrate.seek(cp, os.SEEK_SET)
+        yield result
+
+    else:
+        received = substrate.read(1)
+        if received is None:
+            yield
+
+        if received:
+            substrate.seek(-1, os.SEEK_CUR)
+
+        yield not received
+
+
+def peekIntoStream(substrate, size=-1):
+    """Peek into stream.
+
+    Parameters
+    ----------
+    substrate: :py:class:`IOBase`
+        Stream to read from.
+
+    size: :py:class:`int`
+        How many bytes to peek (-1 = all available)
+
+    Returns
+    -------
+    : :py:class:`bytes` or :py:class:`str`
+        The return type depends on Python major version
+    """
+    if hasattr(substrate, "peek"):
+        received = substrate.peek(size)
+        if received is None:
+            yield
+
+        while len(received) < size:
+            yield
+
+        yield received
+
+    else:
+        current_position = substrate.tell()
+        try:
+            for chunk in readFromStream(substrate, size):
+                yield chunk
+
+        finally:
+            substrate.seek(current_position)
+
+
+def readFromStream(substrate, size=-1, context=None):
+    """Read from the stream.
+
+    Parameters
+    ----------
+    substrate: :py:class:`IOBase`
+        Stream to read from.
+
+    Keyword parameters
+    ------------------
+    size: :py:class:`int`
+        How many bytes to read (-1 = all available)
+
+    context: :py:class:`dict`
+        Opaque caller context will be attached to exception objects created
+        by this function.
+
+    Yields
+    ------
+    : :py:class:`bytes` or :py:class:`str` or :py:class:`SubstrateUnderrunError`
+        Read data or :py:class:`~pyasn1.error.SubstrateUnderrunError`
+        object if no `size` bytes is readily available in the stream. The
+        data type depends on Python major version
+
+    Raises
+    ------
+    : :py:class:`~pyasn1.error.EndOfStreamError`
+        Input stream is exhausted
+    """
+    while True:
+        # this will block unless stream is non-blocking
+        received = substrate.read(size)
+        if received is None:  # non-blocking stream can do this
+            yield error.SubstrateUnderrunError(context=context)
+
+        elif not received and size != 0:  # end-of-stream
+            raise error.EndOfStreamError(context=context)
+
+        elif len(received) < size:
+            substrate.seek(-len(received), os.SEEK_CUR)
+
+            # behave like a non-blocking stream
+            yield error.SubstrateUnderrunError(context=context)
+
+        else:
+            break
+
+    yield received
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/compat/__init__.py b/.venv/lib/python3.12/site-packages/pyasn1/compat/__init__.py
new file mode 100644
index 00000000..d3e676ac
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/compat/__init__.py
@@ -0,0 +1,4 @@
+# This file is necessary to make this directory a package.
+
+# sentinal for missing argument
+_MISSING = object()
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/compat/integer.py b/.venv/lib/python3.12/site-packages/pyasn1/compat/integer.py
new file mode 100644
index 00000000..306210e5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/compat/integer.py
@@ -0,0 +1,13 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+def to_bytes(value, signed=False, length=0):
+    length = max(value.bit_length(), length)
+
+    if signed and length % 8 == 0:
+        length += 1
+
+    return value.to_bytes(length // 8 + (length % 8 and 1 or 0), 'big', signed=signed)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/debug.py b/.venv/lib/python3.12/site-packages/pyasn1/debug.py
new file mode 100644
index 00000000..07194235
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/debug.py
@@ -0,0 +1,146 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import logging
+import sys
+
+from pyasn1 import __version__
+from pyasn1 import error
+
+__all__ = ['Debug', 'setLogger', 'hexdump']
+
+DEBUG_NONE = 0x0000
+DEBUG_ENCODER = 0x0001
+DEBUG_DECODER = 0x0002
+DEBUG_ALL = 0xffff
+
+FLAG_MAP = {
+    'none': DEBUG_NONE,
+    'encoder': DEBUG_ENCODER,
+    'decoder': DEBUG_DECODER,
+    'all': DEBUG_ALL
+}
+
+LOGGEE_MAP = {}
+
+
+class Printer(object):
+    # noinspection PyShadowingNames
+    def __init__(self, logger=None, handler=None, formatter=None):
+        if logger is None:
+            logger = logging.getLogger('pyasn1')
+
+        logger.setLevel(logging.DEBUG)
+
+        if handler is None:
+            handler = logging.StreamHandler()
+
+        if formatter is None:
+            formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s')
+
+        handler.setFormatter(formatter)
+        handler.setLevel(logging.DEBUG)
+        logger.addHandler(handler)
+
+        self.__logger = logger
+
+    def __call__(self, msg):
+        self.__logger.debug(msg)
+
+    def __str__(self):
+        return '<python logging>'
+
+
+class Debug(object):
+    defaultPrinter = Printer()
+
+    def __init__(self, *flags, **options):
+        self._flags = DEBUG_NONE
+
+        if 'loggerName' in options:
+            # route our logs to parent logger
+            self._printer = Printer(
+                logger=logging.getLogger(options['loggerName']),
+                handler=logging.NullHandler()
+            )
+
+        elif 'printer' in options:
+            self._printer = options.get('printer')
+
+        else:
+            self._printer = self.defaultPrinter
+
+        self._printer('running pyasn1 %s, debug flags %s' % (__version__, ', '.join(flags)))
+
+        for flag in flags:
+            inverse = flag and flag[0] in ('!', '~')
+            if inverse:
+                flag = flag[1:]
+            try:
+                if inverse:
+                    self._flags &= ~FLAG_MAP[flag]
+                else:
+                    self._flags |= FLAG_MAP[flag]
+            except KeyError:
+                raise error.PyAsn1Error('bad debug flag %s' % flag)
+
+            self._printer("debug category '%s' %s" % (flag, inverse and 'disabled' or 'enabled'))
+
+    def __str__(self):
+        return 'logger %s, flags %x' % (self._printer, self._flags)
+
+    def __call__(self, msg):
+        self._printer(msg)
+
+    def __and__(self, flag):
+        return self._flags & flag
+
+    def __rand__(self, flag):
+        return flag & self._flags
+
+_LOG = DEBUG_NONE
+
+
+def setLogger(userLogger):
+    global _LOG
+
+    if userLogger:
+        _LOG = userLogger
+    else:
+        _LOG = DEBUG_NONE
+
+    # Update registered logging clients
+    for module, (name, flags) in LOGGEE_MAP.items():
+        setattr(module, name, _LOG & flags and _LOG or DEBUG_NONE)
+
+
+def registerLoggee(module, name='LOG', flags=DEBUG_NONE):
+    LOGGEE_MAP[sys.modules[module]] = name, flags
+    setLogger(_LOG)
+    return _LOG
+
+
+def hexdump(octets):
+    return ' '.join(
+        ['%s%.2X' % (n % 16 == 0 and ('\n%.5d: ' % n) or '', x)
+         for n, x in zip(range(len(octets)), octets)]
+    )
+
+
+class Scope(object):
+    def __init__(self):
+        self._list = []
+
+    def __str__(self): return '.'.join(self._list)
+
+    def push(self, token):
+        self._list.append(token)
+
+    def pop(self):
+        return self._list.pop()
+
+
+scope = Scope()
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/error.py b/.venv/lib/python3.12/site-packages/pyasn1/error.py
new file mode 100644
index 00000000..75c9a3f4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/error.py
@@ -0,0 +1,116 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+
+
+class PyAsn1Error(Exception):
+    """Base pyasn1 exception
+
+    `PyAsn1Error` is the base exception class (based on
+    :class:`Exception`) that represents all possible ASN.1 related
+    errors.
+
+    Parameters
+    ----------
+    args:
+        Opaque positional parameters
+
+    Keyword Args
+    ------------
+    kwargs:
+        Opaque keyword parameters
+
+    """
+    def __init__(self, *args, **kwargs):
+        self._args = args
+        self._kwargs = kwargs
+
+    @property
+    def context(self):
+        """Return exception context
+
+        When exception object is created, the caller can supply some opaque
+        context for the upper layers to better understand the cause of the
+        exception.
+
+        Returns
+        -------
+        : :py:class:`dict`
+            Dict holding context specific data
+        """
+        return self._kwargs.get('context', {})
+
+
+class ValueConstraintError(PyAsn1Error):
+    """ASN.1 type constraints violation exception
+
+    The `ValueConstraintError` exception indicates an ASN.1 value
+    constraint violation.
+
+    It might happen on value object instantiation (for scalar types) or on
+    serialization (for constructed types).
+    """
+
+
+class SubstrateUnderrunError(PyAsn1Error):
+    """ASN.1 data structure deserialization error
+
+    The `SubstrateUnderrunError` exception indicates insufficient serialised
+    data on input of a de-serialization codec.
+    """
+
+
+class EndOfStreamError(SubstrateUnderrunError):
+    """ASN.1 data structure deserialization error
+
+    The `EndOfStreamError` exception indicates the condition of the input
+    stream has been closed.
+    """
+
+
+class UnsupportedSubstrateError(PyAsn1Error):
+    """Unsupported substrate type to parse as ASN.1 data."""
+
+
+class PyAsn1UnicodeError(PyAsn1Error, UnicodeError):
+    """Unicode text processing error
+
+    The `PyAsn1UnicodeError` exception is a base class for errors relating to
+    unicode text de/serialization.
+
+    Apart from inheriting from :class:`PyAsn1Error`, it also inherits from
+    :class:`UnicodeError` to help the caller catching unicode-related errors.
+    """
+    def __init__(self, message, unicode_error=None):
+        if isinstance(unicode_error, UnicodeError):
+            UnicodeError.__init__(self, *unicode_error.args)
+        PyAsn1Error.__init__(self, message)
+
+
+class PyAsn1UnicodeDecodeError(PyAsn1UnicodeError, UnicodeDecodeError):
+    """Unicode text decoding error
+
+    The `PyAsn1UnicodeDecodeError` exception represents a failure to
+    deserialize unicode text.
+
+    Apart from inheriting from :class:`PyAsn1UnicodeError`, it also inherits
+    from :class:`UnicodeDecodeError` to help the caller catching unicode-related
+    errors.
+    """
+
+
+class PyAsn1UnicodeEncodeError(PyAsn1UnicodeError, UnicodeEncodeError):
+    """Unicode text encoding error
+
+    The `PyAsn1UnicodeEncodeError` exception represents a failure to
+    serialize unicode text.
+
+    Apart from inheriting from :class:`PyAsn1UnicodeError`, it also inherits
+    from :class:`UnicodeEncodeError` to help the caller catching
+    unicode-related errors.
+    """
+
+
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/__init__.py b/.venv/lib/python3.12/site-packages/pyasn1/type/__init__.py
new file mode 100644
index 00000000..8c3066b2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/base.py b/.venv/lib/python3.12/site-packages/pyasn1/type/base.py
new file mode 100644
index 00000000..aa86e520
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/base.py
@@ -0,0 +1,699 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import sys
+
+from pyasn1 import error
+from pyasn1.type import constraint
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+__all__ = ['Asn1Item', 'Asn1Type', 'SimpleAsn1Type',
+           'ConstructedAsn1Type']
+
+
+class Asn1Item(object):
+    @classmethod
+    def getTypeId(cls, increment=1):
+        try:
+            Asn1Item._typeCounter += increment
+        except AttributeError:
+            Asn1Item._typeCounter = increment
+        return Asn1Item._typeCounter
+
+
+class Asn1Type(Asn1Item):
+    """Base class for all classes representing ASN.1 types.
+
+    In the user code, |ASN.1| class is normally used only for telling
+    ASN.1 objects from others.
+
+    Note
+    ----
+    For as long as ASN.1 is concerned, a way to compare ASN.1 types
+    is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
+    """
+    #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing
+    #: ASN.1 tag(s) associated with |ASN.1| type.
+    tagSet = tag.TagSet()
+
+    #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+    #: object imposing constraints on initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = None
+
+    def __init__(self, **kwargs):
+        readOnly = {
+            'tagSet': self.tagSet,
+            'subtypeSpec': self.subtypeSpec
+        }
+
+        readOnly.update(kwargs)
+
+        self.__dict__.update(readOnly)
+
+        self._readOnly = readOnly
+
+    def __setattr__(self, name, value):
+        if name[0] != '_' and name in self._readOnly:
+            raise error.PyAsn1Error('read-only instance attribute "%s"' % name)
+
+        self.__dict__[name] = value
+
+    def __str__(self):
+        return self.prettyPrint()
+
+    @property
+    def readOnly(self):
+        return self._readOnly
+
+    @property
+    def effectiveTagSet(self):
+        """For |ASN.1| type is equivalent to *tagSet*
+        """
+        return self.tagSet  # used by untagged types
+
+    @property
+    def tagMap(self):
+        """Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
+        """
+        return tagmap.TagMap({self.tagSet: self})
+
+    def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
+        """Examine |ASN.1| type for equality with other ASN.1 type.
+
+        ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
+        (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
+        out ASN.1 types comparison.
+
+        Python class inheritance relationship is NOT considered.
+
+        Parameters
+        ----------
+        other: a pyasn1 type object
+            Class instance representing ASN.1 type.
+
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`True` if *other* is |ASN.1| type,
+            :obj:`False` otherwise.
+        """
+        return (self is other or
+                (not matchTags or self.tagSet == other.tagSet) and
+                (not matchConstraints or self.subtypeSpec == other.subtypeSpec))
+
+    def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
+        """Examine |ASN.1| type for subtype relationship with other ASN.1 type.
+
+        ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
+        (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
+        out ASN.1 types comparison.
+
+        Python class inheritance relationship is NOT considered.
+
+        Parameters
+        ----------
+            other: a pyasn1 type object
+                Class instance representing ASN.1 type.
+
+        Returns
+        -------
+            : :class:`bool`
+                :obj:`True` if *other* is a subtype of |ASN.1| type,
+                :obj:`False` otherwise.
+        """
+        return (not matchTags or
+                (self.tagSet.isSuperTagSetOf(other.tagSet)) and
+                 (not matchConstraints or self.subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
+
+    @staticmethod
+    def isNoValue(*values):
+        for value in values:
+            if value is not noValue:
+                return False
+        return True
+
+    def prettyPrint(self, scope=0):
+        raise NotImplementedError
+
+    # backward compatibility
+
+    def getTagSet(self):
+        return self.tagSet
+
+    def getEffectiveTagSet(self):
+        return self.effectiveTagSet
+
+    def getTagMap(self):
+        return self.tagMap
+
+    def getSubtypeSpec(self):
+        return self.subtypeSpec
+
+    # backward compatibility
+    def hasValue(self):
+        return self.isValue
+
+# Backward compatibility
+Asn1ItemBase = Asn1Type
+
+
+class NoValue(object):
+    """Create a singleton instance of NoValue class.
+
+    The *NoValue* sentinel object represents an instance of ASN.1 schema
+    object as opposed to ASN.1 value object.
+
+    Only ASN.1 schema-related operations can be performed on ASN.1
+    schema objects.
+
+    Warning
+    -------
+    Any operation attempted on the *noValue* object will raise the
+    *PyAsn1Error* exception.
+    """
+    skipMethods = {
+        '__slots__',
+        # attributes
+        '__getattribute__',
+        '__getattr__',
+        '__setattr__',
+        '__delattr__',
+        # class instance
+        '__class__',
+        '__init__',
+        '__del__',
+        '__new__',
+        '__repr__',
+        '__qualname__',
+        '__objclass__',
+        'im_class',
+        '__sizeof__',
+        # pickle protocol
+        '__reduce__',
+        '__reduce_ex__',
+        '__getnewargs__',
+        '__getinitargs__',
+        '__getstate__',
+        '__setstate__',
+    }
+
+    _instance = None
+
+    def __new__(cls):
+        if cls._instance is None:
+            def getPlug(name):
+                def plug(self, *args, **kw):
+                    raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % name)
+                return plug
+
+            op_names = [name
+                        for typ in (str, int, list, dict)
+                        for name in dir(typ)
+                        if (name not in cls.skipMethods and
+                            name.startswith('__') and
+                            name.endswith('__') and
+                            callable(getattr(typ, name)))]
+
+            for name in set(op_names):
+                setattr(cls, name, getPlug(name))
+
+            cls._instance = object.__new__(cls)
+
+        return cls._instance
+
+    def __getattr__(self, attr):
+        if attr in self.skipMethods:
+            raise AttributeError('Attribute %s not present' % attr)
+
+        raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % attr)
+
+    def __repr__(self):
+        return '<%s object>' % self.__class__.__name__
+
+
+noValue = NoValue()
+
+
+class SimpleAsn1Type(Asn1Type):
+    """Base class for all simple classes representing ASN.1 types.
+
+    ASN.1 distinguishes types by their ability to hold other objects.
+    Scalar types are known as *simple* in ASN.1.
+
+    In the user code, |ASN.1| class is normally used only for telling
+    ASN.1 objects from others.
+
+    Note
+    ----
+    For as long as ASN.1 is concerned, a way to compare ASN.1 types
+    is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
+    """
+    #: Default payload value
+    defaultValue = noValue
+
+    def __init__(self, value=noValue, **kwargs):
+        Asn1Type.__init__(self, **kwargs)
+        if value is noValue:
+            value = self.defaultValue
+        else:
+            value = self.prettyIn(value)
+            try:
+                self.subtypeSpec(value)
+
+            except error.PyAsn1Error as exValue:
+                raise type(exValue)('%s at %s' % (exValue, self.__class__.__name__))
+
+        self._value = value
+
+    def __repr__(self):
+        representation = '%s %s object' % (
+            self.__class__.__name__, self.isValue and 'value' or 'schema')
+
+        for attr, value in self.readOnly.items():
+            if value:
+                representation += ', %s %s' % (attr, value)
+
+        if self.isValue:
+            value = self.prettyPrint()
+            if len(value) > 32:
+                value = value[:16] + '...' + value[-16:]
+            representation += ', payload [%s]' % value
+
+        return '<%s>' % representation
+
+    def __eq__(self, other):
+        if self is other:
+            return True
+        return self._value == other
+
+    def __ne__(self, other):
+        return self._value != other
+
+    def __lt__(self, other):
+        return self._value < other
+
+    def __le__(self, other):
+        return self._value <= other
+
+    def __gt__(self, other):
+        return self._value > other
+
+    def __ge__(self, other):
+        return self._value >= other
+
+    def __bool__(self):
+        return bool(self._value)
+
+    def __hash__(self):
+        return hash(self._value)
+
+    @property
+    def isValue(self):
+        """Indicate that |ASN.1| object represents ASN.1 value.
+
+        If *isValue* is :obj:`False` then this object represents just
+        ASN.1 schema.
+
+        If *isValue* is :obj:`True` then, in addition to its ASN.1 schema
+        features, this object can also be used like a Python built-in object
+        (e.g. :class:`int`, :class:`str`, :class:`dict` etc.).
+
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`False` if object represents just ASN.1 schema.
+            :obj:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+        Note
+        ----
+        There is an important distinction between PyASN1 schema and value objects.
+        The PyASN1 schema objects can only participate in ASN.1 schema-related
+        operations (e.g. defining or testing the structure of the data). Most
+        obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+        encoding/decoding serialised ASN.1 contents.
+
+        The PyASN1 value objects can **additionally** participate in many operations
+        involving regular Python objects (e.g. arithmetic, comprehension etc).
+        """
+        return self._value is not noValue
+
+    def clone(self, value=noValue, **kwargs):
+        """Create a modified version of |ASN.1| schema or value object.
+
+        The `clone()` method accepts the same set arguments as |ASN.1|
+        class takes on instantiation except that all arguments
+        of the `clone()` method are optional.
+
+        Whatever arguments are supplied, they are used to create a copy
+        of `self` taking precedence over the ones used to instantiate `self`.
+
+        Note
+        ----
+        Due to the immutable nature of the |ASN.1| object, if no arguments
+        are supplied, no new |ASN.1| object will be created and `self` will
+        be returned instead.
+        """
+        if value is noValue:
+            if not kwargs:
+                return self
+
+            value = self._value
+
+        initializers = self.readOnly.copy()
+        initializers.update(kwargs)
+
+        return self.__class__(value, **initializers)
+
+    def subtype(self, value=noValue, **kwargs):
+        """Create a specialization of |ASN.1| schema or value object.
+
+        The subtype relationship between ASN.1 types has no correlation with
+        subtype relationship between Python types. ASN.1 type is mainly identified
+        by its tag(s) (:py:class:`~pyasn1.type.tag.TagSet`) and value range
+        constraints (:py:class:`~pyasn1.type.constraint.ConstraintsIntersection`).
+        These ASN.1 type properties are implemented as |ASN.1| attributes.  
+
+        The `subtype()` method accepts the same set arguments as |ASN.1|
+        class takes on instantiation except that all parameters
+        of the `subtype()` method are optional.
+
+        With the exception of the arguments described below, the rest of
+        supplied arguments they are used to create a copy of `self` taking
+        precedence over the ones used to instantiate `self`.
+
+        The following arguments to `subtype()` create a ASN.1 subtype out of
+        |ASN.1| type:
+
+        Other Parameters
+        ----------------
+        implicitTag: :py:class:`~pyasn1.type.tag.Tag`
+            Implicitly apply given ASN.1 tag object to `self`'s
+            :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+            new object's ASN.1 tag(s).
+
+        explicitTag: :py:class:`~pyasn1.type.tag.Tag`
+            Explicitly apply given ASN.1 tag object to `self`'s
+            :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+            new object's ASN.1 tag(s).
+
+        subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+            Add ASN.1 constraints object to one of the `self`'s, then
+            use the result as new object's ASN.1 constraints.
+
+        Returns
+        -------
+        :
+            new instance of |ASN.1| schema or value object
+
+        Note
+        ----
+        Due to the immutable nature of the |ASN.1| object, if no arguments
+        are supplied, no new |ASN.1| object will be created and `self` will
+        be returned instead.
+        """
+        if value is noValue:
+            if not kwargs:
+                return self
+
+            value = self._value
+
+        initializers = self.readOnly.copy()
+
+        implicitTag = kwargs.pop('implicitTag', None)
+        if implicitTag is not None:
+            initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
+
+        explicitTag = kwargs.pop('explicitTag', None)
+        if explicitTag is not None:
+            initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
+
+        for arg, option in kwargs.items():
+            initializers[arg] += option
+
+        return self.__class__(value, **initializers)
+
+    def prettyIn(self, value):
+        return value
+
+    def prettyOut(self, value):
+        return str(value)
+
+    def prettyPrint(self, scope=0):
+        return self.prettyOut(self._value)
+
+    def prettyPrintType(self, scope=0):
+        return '%s -> %s' % (self.tagSet, self.__class__.__name__)
+
+# Backward compatibility
+AbstractSimpleAsn1Item = SimpleAsn1Type
+
+#
+# Constructed types:
+# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
+# * ASN1 types and values are represened by Python class instances
+# * Value initialization is made for defaulted components only
+# * Primary method of component addressing is by-position. Data model for base
+#   type is Python sequence. Additional type-specific addressing methods
+#   may be implemented for particular types.
+# * SequenceOf and SetOf types do not implement any additional methods
+# * Sequence, Set and Choice types also implement by-identifier addressing
+# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing
+# * Sequence and Set types may include optional and defaulted
+#   components
+# * Constructed types hold a reference to component types used for value
+#   verification and ordering.
+# * Component type is a scalar type for SequenceOf/SetOf types and a list
+#   of types for Sequence/Set/Choice.
+#
+
+
+class ConstructedAsn1Type(Asn1Type):
+    """Base class for all constructed classes representing ASN.1 types.
+
+    ASN.1 distinguishes types by their ability to hold other objects.
+    Those "nesting" types are known as *constructed* in ASN.1.
+
+    In the user code, |ASN.1| class is normally used only for telling
+    ASN.1 objects from others.
+
+    Note
+    ----
+    For as long as ASN.1 is concerned, a way to compare ASN.1 types
+    is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
+    """
+
+    #: If :obj:`True`, requires exact component type matching,
+    #: otherwise subtype relation is only enforced
+    strictConstraints = False
+
+    componentType = None
+
+    # backward compatibility, unused
+    sizeSpec = constraint.ConstraintsIntersection()
+
+    def __init__(self, **kwargs):
+        readOnly = {
+            'componentType': self.componentType,
+            # backward compatibility, unused
+            'sizeSpec': self.sizeSpec
+        }
+
+        # backward compatibility: preserve legacy sizeSpec support
+        kwargs = self._moveSizeSpec(**kwargs)
+
+        readOnly.update(kwargs)
+
+        Asn1Type.__init__(self, **readOnly)
+
+    def _moveSizeSpec(self, **kwargs):
+        # backward compatibility, unused
+        sizeSpec = kwargs.pop('sizeSpec', self.sizeSpec)
+        if sizeSpec:
+            subtypeSpec = kwargs.pop('subtypeSpec', self.subtypeSpec)
+            if subtypeSpec:
+                subtypeSpec = sizeSpec
+
+            else:
+                subtypeSpec += sizeSpec
+
+            kwargs['subtypeSpec'] = subtypeSpec
+
+        return kwargs
+
+    def __repr__(self):
+        representation = '%s %s object' % (
+            self.__class__.__name__, self.isValue and 'value' or 'schema'
+        )
+
+        for attr, value in self.readOnly.items():
+            if value is not noValue:
+                representation += ', %s=%r' % (attr, value)
+
+        if self.isValue and self.components:
+            representation += ', payload [%s]' % ', '.join(
+                [repr(x) for x in self.components])
+
+        return '<%s>' % representation
+
+    def __eq__(self, other):
+        return self is other or self.components == other
+
+    def __ne__(self, other):
+        return self.components != other
+
+    def __lt__(self, other):
+        return self.components < other
+
+    def __le__(self, other):
+        return self.components <= other
+
+    def __gt__(self, other):
+        return self.components > other
+
+    def __ge__(self, other):
+        return self.components >= other
+
+    def __bool__(self):
+        return bool(self.components)
+
+    @property
+    def components(self):
+        raise error.PyAsn1Error('Method not implemented')
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        pass
+
+    def clone(self, **kwargs):
+        """Create a modified version of |ASN.1| schema object.
+
+        The `clone()` method accepts the same set arguments as |ASN.1|
+        class takes on instantiation except that all arguments
+        of the `clone()` method are optional.
+
+        Whatever arguments are supplied, they are used to create a copy
+        of `self` taking precedence over the ones used to instantiate `self`.
+
+        Possible values of `self` are never copied over thus `clone()` can
+        only create a new schema object.
+
+        Returns
+        -------
+        :
+            new instance of |ASN.1| type/value
+
+        Note
+        ----
+        Due to the mutable nature of the |ASN.1| object, even if no arguments
+        are supplied, a new |ASN.1| object will be created and returned.
+        """
+        cloneValueFlag = kwargs.pop('cloneValueFlag', False)
+
+        initializers = self.readOnly.copy()
+        initializers.update(kwargs)
+
+        clone = self.__class__(**initializers)
+
+        if cloneValueFlag:
+            self._cloneComponentValues(clone, cloneValueFlag)
+
+        return clone
+
+    def subtype(self, **kwargs):
+        """Create a specialization of |ASN.1| schema object.
+
+        The `subtype()` method accepts the same set arguments as |ASN.1|
+        class takes on instantiation except that all parameters
+        of the `subtype()` method are optional.
+
+        With the exception of the arguments described below, the rest of
+        supplied arguments they are used to create a copy of `self` taking
+        precedence over the ones used to instantiate `self`.
+
+        The following arguments to `subtype()` create a ASN.1 subtype out of
+        |ASN.1| type.
+
+        Other Parameters
+        ----------------
+        implicitTag: :py:class:`~pyasn1.type.tag.Tag`
+            Implicitly apply given ASN.1 tag object to `self`'s
+            :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+            new object's ASN.1 tag(s).
+
+        explicitTag: :py:class:`~pyasn1.type.tag.Tag`
+            Explicitly apply given ASN.1 tag object to `self`'s
+            :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+            new object's ASN.1 tag(s).
+
+        subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+            Add ASN.1 constraints object to one of the `self`'s, then
+            use the result as new object's ASN.1 constraints.
+
+
+        Returns
+        -------
+        :
+            new instance of |ASN.1| type/value
+
+        Note
+        ----
+        Due to the mutable nature of the |ASN.1| object, even if no arguments
+        are supplied, a new |ASN.1| object will be created and returned.
+        """
+
+        initializers = self.readOnly.copy()
+
+        cloneValueFlag = kwargs.pop('cloneValueFlag', False)
+
+        implicitTag = kwargs.pop('implicitTag', None)
+        if implicitTag is not None:
+            initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
+
+        explicitTag = kwargs.pop('explicitTag', None)
+        if explicitTag is not None:
+            initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
+
+        for arg, option in kwargs.items():
+            initializers[arg] += option
+
+        clone = self.__class__(**initializers)
+
+        if cloneValueFlag:
+            self._cloneComponentValues(clone, cloneValueFlag)
+
+        return clone
+
+    def getComponentByPosition(self, idx):
+        raise error.PyAsn1Error('Method not implemented')
+
+    def setComponentByPosition(self, idx, value, verifyConstraints=True):
+        raise error.PyAsn1Error('Method not implemented')
+
+    def setComponents(self, *args, **kwargs):
+        for idx, value in enumerate(args):
+            self[idx] = value
+        for k in kwargs:
+            self[k] = kwargs[k]
+        return self
+
+    # backward compatibility
+
+    def setDefaultComponents(self):
+        pass
+
+    def getComponentType(self):
+        return self.componentType
+
+    # backward compatibility, unused
+    def verifySizeSpec(self):
+        self.subtypeSpec(self)
+
+
+        # Backward compatibility
+AbstractConstructedAsn1Item = ConstructedAsn1Type
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/char.py b/.venv/lib/python3.12/site-packages/pyasn1/type/char.py
new file mode 100644
index 00000000..ec65f006
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/char.py
@@ -0,0 +1,288 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import sys
+
+from pyasn1 import error
+from pyasn1.type import tag
+from pyasn1.type import univ
+
+__all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString',
+           'IA5String', 'GraphicString', 'VisibleString', 'ISO646String',
+           'GeneralString', 'UniversalString', 'BMPString', 'UTF8String']
+
+NoValue = univ.NoValue
+noValue = univ.noValue
+
+
+class AbstractCharacterString(univ.OctetString):
+    """Creates |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`,
+    its objects are immutable and duck-type :class:`bytes`.
+    When used in octet-stream context, |ASN.1| type assumes
+    "|encoding|" encoding.
+
+    Keyword Args
+    ------------
+    value: :class:`str`, :class:`bytes` or |ASN.1| object
+        :class:`str`, alternatively :class:`bytes`
+        representing octet-stream of serialised unicode string
+        (note `encoding` parameter) or |ASN.1| class instance.
+        If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    encoding: :py:class:`str`
+        Unicode codec ID to encode/decode
+        :class:`str` the payload when |ASN.1| object is used
+        in octet-stream context.
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+    """
+
+    def __str__(self):
+        return str(self._value)
+
+    def __bytes__(self):
+        try:
+            return self._value.encode(self.encoding)
+        except UnicodeEncodeError as exc:
+            raise error.PyAsn1UnicodeEncodeError(
+                "Can't encode string '%s' with codec "
+                "%s" % (self._value, self.encoding), exc
+            )
+
+    def prettyIn(self, value):
+        try:
+            if isinstance(value, str):
+                return value
+            elif isinstance(value, bytes):
+                return value.decode(self.encoding)
+            elif isinstance(value, (tuple, list)):
+                return self.prettyIn(bytes(value))
+            elif isinstance(value, univ.OctetString):
+                return value.asOctets().decode(self.encoding)
+            else:
+                return str(value)
+
+        except (UnicodeDecodeError, LookupError) as exc:
+            raise error.PyAsn1UnicodeDecodeError(
+                "Can't decode string '%s' with codec "
+                "%s" % (value, self.encoding), exc
+            )
+
+    def asOctets(self, padding=True):
+        return bytes(self)
+
+    def asNumbers(self, padding=True):
+        return tuple(bytes(self))
+
+    #
+    # See OctetString.prettyPrint() for the explanation
+    #
+
+    def prettyOut(self, value):
+        return value
+
+    def prettyPrint(self, scope=0):
+        # first see if subclass has its own .prettyOut()
+        value = self.prettyOut(self._value)
+
+        if value is not self._value:
+            return value
+
+        return AbstractCharacterString.__str__(self)
+
+    def __reversed__(self):
+        return reversed(self._value)
+
+
+class NumericString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
+    )
+    encoding = 'us-ascii'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class PrintableString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
+    )
+    encoding = 'us-ascii'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class TeletexString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
+    )
+    encoding = 'iso-8859-1'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class T61String(TeletexString):
+    __doc__ = TeletexString.__doc__
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class VideotexString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
+    )
+    encoding = 'iso-8859-1'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class IA5String(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
+    )
+    encoding = 'us-ascii'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class GraphicString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
+    )
+    encoding = 'iso-8859-1'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class VisibleString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
+    )
+    encoding = 'us-ascii'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class ISO646String(VisibleString):
+    __doc__ = VisibleString.__doc__
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+class GeneralString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
+    )
+    encoding = 'iso-8859-1'
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class UniversalString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
+    )
+    encoding = "utf-32-be"
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class BMPString(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
+    )
+    encoding = "utf-16-be"
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
+
+
+class UTF8String(AbstractCharacterString):
+    __doc__ = AbstractCharacterString.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+    )
+    encoding = "utf-8"
+
+    # Optimization for faster codec lookup
+    typeId = AbstractCharacterString.getTypeId()
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/constraint.py b/.venv/lib/python3.12/site-packages/pyasn1/type/constraint.py
new file mode 100644
index 00000000..02368d0a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/constraint.py
@@ -0,0 +1,751 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+# Original concept and code by Mike C. Fletcher.
+#
+import sys
+
+from pyasn1.type import error
+
+__all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint',
+           'ValueRangeConstraint', 'ValueSizeConstraint',
+           'PermittedAlphabetConstraint', 'InnerTypeConstraint',
+           'ConstraintsExclusion', 'ConstraintsIntersection',
+           'ConstraintsUnion']
+
+
+class AbstractConstraint(object):
+
+    def __init__(self, *values):
+        self._valueMap = set()
+        self._setValues(values)
+        self.__hash = hash((self.__class__.__name__, self._values))
+
+    def __call__(self, value, idx=None):
+        if not self._values:
+            return
+
+        try:
+            self._testValue(value, idx)
+
+        except error.ValueConstraintError as exc:
+            raise error.ValueConstraintError(
+                '%s failed at: %r' % (self, exc)
+            )
+
+    def __repr__(self):
+        representation = '%s object' % (self.__class__.__name__)
+
+        if self._values:
+            representation += ', consts %s' % ', '.join(
+                [repr(x) for x in self._values])
+
+        return '<%s>' % representation
+
+    def __eq__(self, other):
+        if self is other:
+            return True
+        return self._values == other
+
+    def __ne__(self, other):
+        return self._values != other
+
+    def __lt__(self, other):
+        return self._values < other
+
+    def __le__(self, other):
+        return self._values <= other
+
+    def __gt__(self, other):
+        return self._values > other
+
+    def __ge__(self, other):
+        return self._values >= other
+
+    def __bool__(self):
+        return bool(self._values)
+
+    def __hash__(self):
+        return self.__hash
+
+    def _setValues(self, values):
+        self._values = values
+
+    def _testValue(self, value, idx):
+        raise error.ValueConstraintError(value)
+
+    # Constraints derivation logic
+    def getValueMap(self):
+        return self._valueMap
+
+    def isSuperTypeOf(self, otherConstraint):
+        # TODO: fix possible comparison of set vs scalars here
+        return (otherConstraint is self or
+                not self._values or
+                otherConstraint == self or
+                self in otherConstraint.getValueMap())
+
+    def isSubTypeOf(self, otherConstraint):
+        return (otherConstraint is self or
+                not self or
+                otherConstraint == self or
+                otherConstraint in self._valueMap)
+
+
+class SingleValueConstraint(AbstractConstraint):
+    """Create a SingleValueConstraint object.
+
+    The SingleValueConstraint satisfies any value that
+    is present in the set of permitted values.
+
+    Objects of this type are iterable (emitting constraint values) and
+    can act as operands for some arithmetic operations e.g. addition
+    and subtraction. The latter can be used for combining multiple
+    SingleValueConstraint objects into one.
+
+    The SingleValueConstraint object can be applied to
+    any ASN.1 type.
+
+    Parameters
+    ----------
+    *values: :class:`int`
+        Full set of values permitted by this constraint object.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class DivisorOfSix(Integer):
+            '''
+            ASN.1 specification:
+
+            Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6)
+            '''
+            subtypeSpec = SingleValueConstraint(1, 2, 3, 6)
+
+        # this will succeed
+        divisor_of_six = DivisorOfSix(1)
+
+        # this will raise ValueConstraintError
+        divisor_of_six = DivisorOfSix(7)
+    """
+    def _setValues(self, values):
+        self._values = values
+        self._set = set(values)
+
+    def _testValue(self, value, idx):
+        if value not in self._set:
+            raise error.ValueConstraintError(value)
+
+    # Constrains can be merged or reduced
+
+    def __contains__(self, item):
+        return item in self._set
+
+    def __iter__(self):
+        return iter(self._set)
+
+    def __add__(self, constraint):
+        return self.__class__(*(self._set.union(constraint)))
+
+    def __sub__(self, constraint):
+        return self.__class__(*(self._set.difference(constraint)))
+
+
+class ContainedSubtypeConstraint(AbstractConstraint):
+    """Create a ContainedSubtypeConstraint object.
+
+    The ContainedSubtypeConstraint satisfies any value that
+    is present in the set of permitted values and also
+    satisfies included constraints.
+
+    The ContainedSubtypeConstraint object can be applied to
+    any ASN.1 type.
+
+    Parameters
+    ----------
+    *values:
+        Full set of values and constraint objects permitted
+        by this constraint object.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class DivisorOfEighteen(Integer):
+            '''
+            ASN.1 specification:
+
+            Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18)
+            '''
+            subtypeSpec = ContainedSubtypeConstraint(
+                SingleValueConstraint(1, 2, 3, 6), 9, 18
+            )
+
+        # this will succeed
+        divisor_of_eighteen = DivisorOfEighteen(9)
+
+        # this will raise ValueConstraintError
+        divisor_of_eighteen = DivisorOfEighteen(10)
+    """
+    def _testValue(self, value, idx):
+        for constraint in self._values:
+            if isinstance(constraint, AbstractConstraint):
+                constraint(value, idx)
+            elif value not in self._set:
+                raise error.ValueConstraintError(value)
+
+
+class ValueRangeConstraint(AbstractConstraint):
+    """Create a ValueRangeConstraint object.
+
+    The ValueRangeConstraint satisfies any value that
+    falls in the range of permitted values.
+
+    The ValueRangeConstraint object can only be applied
+    to :class:`~pyasn1.type.univ.Integer` and
+    :class:`~pyasn1.type.univ.Real` types.
+
+    Parameters
+    ----------
+    start: :class:`int`
+        Minimum permitted value in the range (inclusive)
+
+    end: :class:`int`
+        Maximum permitted value in the range (inclusive)
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class TeenAgeYears(Integer):
+            '''
+            ASN.1 specification:
+
+            TeenAgeYears ::= INTEGER (13 .. 19)
+            '''
+            subtypeSpec = ValueRangeConstraint(13, 19)
+
+        # this will succeed
+        teen_year = TeenAgeYears(18)
+
+        # this will raise ValueConstraintError
+        teen_year = TeenAgeYears(20)
+    """
+    def _testValue(self, value, idx):
+        if value < self.start or value > self.stop:
+            raise error.ValueConstraintError(value)
+
+    def _setValues(self, values):
+        if len(values) != 2:
+            raise error.PyAsn1Error(
+                '%s: bad constraint values' % (self.__class__.__name__,)
+            )
+        self.start, self.stop = values
+        if self.start > self.stop:
+            raise error.PyAsn1Error(
+                '%s: screwed constraint values (start > stop): %s > %s' % (
+                    self.__class__.__name__,
+                    self.start, self.stop
+                )
+            )
+        AbstractConstraint._setValues(self, values)
+
+
+class ValueSizeConstraint(ValueRangeConstraint):
+    """Create a ValueSizeConstraint object.
+
+    The ValueSizeConstraint satisfies any value for
+    as long as its size falls within the range of
+    permitted sizes.
+
+    The ValueSizeConstraint object can be applied
+    to :class:`~pyasn1.type.univ.BitString`,
+    :class:`~pyasn1.type.univ.OctetString` (including
+    all :ref:`character ASN.1 types <type.char>`),
+    :class:`~pyasn1.type.univ.SequenceOf`
+    and :class:`~pyasn1.type.univ.SetOf` types.
+
+    Parameters
+    ----------
+    minimum: :class:`int`
+        Minimum permitted size of the value (inclusive)
+
+    maximum: :class:`int`
+        Maximum permitted size of the value (inclusive)
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class BaseballTeamRoster(SetOf):
+            '''
+            ASN.1 specification:
+
+            BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames
+            '''
+            componentType = PlayerNames()
+            subtypeSpec = ValueSizeConstraint(1, 25)
+
+        # this will succeed
+        team = BaseballTeamRoster()
+        team.extend(['Jan', 'Matej'])
+        encode(team)
+
+        # this will raise ValueConstraintError
+        team = BaseballTeamRoster()
+        team.extend(['Jan'] * 26)
+        encode(team)
+
+    Note
+    ----
+    Whenever ValueSizeConstraint is applied to mutable types
+    (e.g. :class:`~pyasn1.type.univ.SequenceOf`,
+    :class:`~pyasn1.type.univ.SetOf`), constraint
+    validation only happens at the serialisation phase rather
+    than schema instantiation phase (as it is with immutable
+    types).
+    """
+    def _testValue(self, value, idx):
+        valueSize = len(value)
+        if valueSize < self.start or valueSize > self.stop:
+            raise error.ValueConstraintError(value)
+
+
+class PermittedAlphabetConstraint(SingleValueConstraint):
+    """Create a PermittedAlphabetConstraint object.
+
+    The PermittedAlphabetConstraint satisfies any character
+    string for as long as all its characters are present in
+    the set of permitted characters.
+
+    Objects of this type are iterable (emitting constraint values) and
+    can act as operands for some arithmetic operations e.g. addition
+    and subtraction.
+
+    The PermittedAlphabetConstraint object can only be applied
+    to the :ref:`character ASN.1 types <type.char>` such as
+    :class:`~pyasn1.type.char.IA5String`.
+
+    Parameters
+    ----------
+    *alphabet: :class:`str`
+        Full set of characters permitted by this constraint object.
+
+    Example
+    -------
+    .. code-block:: python
+
+        class BooleanValue(IA5String):
+            '''
+            ASN.1 specification:
+
+            BooleanValue ::= IA5String (FROM ('T' | 'F'))
+            '''
+            subtypeSpec = PermittedAlphabetConstraint('T', 'F')
+
+        # this will succeed
+        truth = BooleanValue('T')
+        truth = BooleanValue('TF')
+
+        # this will raise ValueConstraintError
+        garbage = BooleanValue('TAF')
+
+    ASN.1 `FROM ... EXCEPT ...` clause can be modelled by combining multiple
+    PermittedAlphabetConstraint objects into one:
+
+    Example
+    -------
+    .. code-block:: python
+
+        class Lipogramme(IA5String):
+            '''
+            ASN.1 specification:
+
+            Lipogramme ::=
+                IA5String (FROM (ALL EXCEPT ("e"|"E")))
+            '''
+            subtypeSpec = (
+                PermittedAlphabetConstraint(*string.printable) -
+                PermittedAlphabetConstraint('e', 'E')
+            )
+
+        # this will succeed
+        lipogramme = Lipogramme('A work of fiction?')
+
+        # this will raise ValueConstraintError
+        lipogramme = Lipogramme('Eel')
+
+    Note
+    ----
+    Although `ConstraintsExclusion` object could seemingly be used for this
+    purpose, practically, for it to work, it needs to represent its operand
+    constraints as sets and intersect one with the other. That would require
+    the insight into the constraint values (and their types) that are otherwise
+    hidden inside the constraint object.
+
+    Therefore it's more practical to model `EXCEPT` clause at
+    `PermittedAlphabetConstraint` level instead.
+    """
+    def _setValues(self, values):
+        self._values = values
+        self._set = set(values)
+
+    def _testValue(self, value, idx):
+        if not self._set.issuperset(value):
+            raise error.ValueConstraintError(value)
+
+
+class ComponentPresentConstraint(AbstractConstraint):
+    """Create a ComponentPresentConstraint object.
+
+    The ComponentPresentConstraint is only satisfied when the value
+    is not `None`.
+
+    The ComponentPresentConstraint object is typically used with
+    `WithComponentsConstraint`.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        present = ComponentPresentConstraint()
+
+        # this will succeed
+        present('whatever')
+
+        # this will raise ValueConstraintError
+        present(None)
+    """
+    def _setValues(self, values):
+        self._values = ('<must be present>',)
+
+        if values:
+            raise error.PyAsn1Error('No arguments expected')
+
+    def _testValue(self, value, idx):
+        if value is None:
+            raise error.ValueConstraintError(
+                'Component is not present:')
+
+
+class ComponentAbsentConstraint(AbstractConstraint):
+    """Create a ComponentAbsentConstraint object.
+
+    The ComponentAbsentConstraint is only satisfied when the value
+    is `None`.
+
+    The ComponentAbsentConstraint object is typically used with
+    `WithComponentsConstraint`.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        absent = ComponentAbsentConstraint()
+
+        # this will succeed
+        absent(None)
+
+        # this will raise ValueConstraintError
+        absent('whatever')
+    """
+    def _setValues(self, values):
+        self._values = ('<must be absent>',)
+
+        if values:
+            raise error.PyAsn1Error('No arguments expected')
+
+    def _testValue(self, value, idx):
+        if value is not None:
+            raise error.ValueConstraintError(
+                'Component is not absent: %r' % value)
+
+
+class WithComponentsConstraint(AbstractConstraint):
+    """Create a WithComponentsConstraint object.
+
+    The `WithComponentsConstraint` satisfies any mapping object that has
+    constrained fields present or absent, what is indicated by
+    `ComponentPresentConstraint` and `ComponentAbsentConstraint`
+    objects respectively.
+
+    The `WithComponentsConstraint` object is typically applied
+    to  :class:`~pyasn1.type.univ.Set` or
+    :class:`~pyasn1.type.univ.Sequence` types.
+
+    Parameters
+    ----------
+    *fields: :class:`tuple`
+        Zero or more tuples of (`field`, `constraint`) indicating constrained
+        fields.
+
+    Notes
+    -----
+    On top of the primary use of `WithComponentsConstraint` (ensuring presence
+    or absence of particular components of a :class:`~pyasn1.type.univ.Set` or
+    :class:`~pyasn1.type.univ.Sequence`), it is also possible to pass any other
+    constraint objects or their combinations. In case of scalar fields, these
+    constraints will be verified in addition to the constraints belonging to
+    scalar components themselves. However, formally, these additional
+    constraints do not change the type of these ASN.1 objects.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class Item(Sequence):  #  Set is similar
+            '''
+            ASN.1 specification:
+
+            Item ::= SEQUENCE {
+                id    INTEGER OPTIONAL,
+                name  OCTET STRING OPTIONAL
+            } WITH COMPONENTS id PRESENT, name ABSENT | id ABSENT, name PRESENT
+            '''
+            componentType = NamedTypes(
+                OptionalNamedType('id', Integer()),
+                OptionalNamedType('name', OctetString())
+            )
+            withComponents = ConstraintsUnion(
+                WithComponentsConstraint(
+                    ('id', ComponentPresentConstraint()),
+                    ('name', ComponentAbsentConstraint())
+                ),
+                WithComponentsConstraint(
+                    ('id', ComponentAbsentConstraint()),
+                    ('name', ComponentPresentConstraint())
+                )
+            )
+
+        item = Item()
+
+        # This will succeed
+        item['id'] = 1
+
+        # This will succeed
+        item.reset()
+        item['name'] = 'John'
+
+        # This will fail (on encoding)
+        item.reset()
+        descr['id'] = 1
+        descr['name'] = 'John'
+    """
+    def _testValue(self, value, idx):
+        for field, constraint in self._values:
+            constraint(value.get(field))
+
+    def _setValues(self, values):
+        AbstractConstraint._setValues(self, values)
+
+
+# This is a bit kludgy, meaning two op modes within a single constraint
+class InnerTypeConstraint(AbstractConstraint):
+    """Value must satisfy the type and presence constraints"""
+
+    def _testValue(self, value, idx):
+        if self.__singleTypeConstraint:
+            self.__singleTypeConstraint(value)
+        elif self.__multipleTypeConstraint:
+            if idx not in self.__multipleTypeConstraint:
+                raise error.ValueConstraintError(value)
+            constraint, status = self.__multipleTypeConstraint[idx]
+            if status == 'ABSENT':  # XXX presence is not checked!
+                raise error.ValueConstraintError(value)
+            constraint(value)
+
+    def _setValues(self, values):
+        self.__multipleTypeConstraint = {}
+        self.__singleTypeConstraint = None
+        for v in values:
+            if isinstance(v, tuple):
+                self.__multipleTypeConstraint[v[0]] = v[1], v[2]
+            else:
+                self.__singleTypeConstraint = v
+        AbstractConstraint._setValues(self, values)
+
+
+# Logic operations on constraints
+
+class ConstraintsExclusion(AbstractConstraint):
+    """Create a ConstraintsExclusion logic operator object.
+
+    The ConstraintsExclusion logic operator succeeds when the
+    value does *not* satisfy the operand constraint.
+
+    The ConstraintsExclusion object can be applied to
+    any constraint and logic operator object.
+
+    Parameters
+    ----------
+    *constraints:
+        Constraint or logic operator objects.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class LuckyNumber(Integer):
+            subtypeSpec = ConstraintsExclusion(
+                SingleValueConstraint(13)
+            )
+
+        # this will succeed
+        luckyNumber = LuckyNumber(12)
+
+        # this will raise ValueConstraintError
+        luckyNumber = LuckyNumber(13)
+
+    Note
+    ----
+    The `FROM ... EXCEPT ...` ASN.1 clause should be modeled by combining
+    constraint objects into one. See `PermittedAlphabetConstraint` for more
+    information.
+    """
+    def _testValue(self, value, idx):
+        for constraint in self._values:
+            try:
+                constraint(value, idx)
+
+            except error.ValueConstraintError:
+                continue
+
+            raise error.ValueConstraintError(value)
+
+    def _setValues(self, values):
+        AbstractConstraint._setValues(self, values)
+
+
+class AbstractConstraintSet(AbstractConstraint):
+
+    def __getitem__(self, idx):
+        return self._values[idx]
+
+    def __iter__(self):
+        return iter(self._values)
+
+    def __add__(self, value):
+        return self.__class__(*(self._values + (value,)))
+
+    def __radd__(self, value):
+        return self.__class__(*((value,) + self._values))
+
+    def __len__(self):
+        return len(self._values)
+
+    # Constraints inclusion in sets
+
+    def _setValues(self, values):
+        self._values = values
+        for constraint in values:
+            if constraint:
+                self._valueMap.add(constraint)
+                self._valueMap.update(constraint.getValueMap())
+
+
+class ConstraintsIntersection(AbstractConstraintSet):
+    """Create a ConstraintsIntersection logic operator object.
+
+    The ConstraintsIntersection logic operator only succeeds
+    if *all* its operands succeed.
+
+    The ConstraintsIntersection object can be applied to
+    any constraint and logic operator objects.
+
+    The ConstraintsIntersection object duck-types the immutable
+    container object like Python :py:class:`tuple`.
+
+    Parameters
+    ----------
+    *constraints:
+        Constraint or logic operator objects.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class CapitalAndSmall(IA5String):
+            '''
+            ASN.1 specification:
+
+            CapitalAndSmall ::=
+                IA5String (FROM ("A".."Z"|"a".."z"))
+            '''
+            subtypeSpec = ConstraintsIntersection(
+                PermittedAlphabetConstraint('A', 'Z'),
+                PermittedAlphabetConstraint('a', 'z')
+            )
+
+        # this will succeed
+        capital_and_small = CapitalAndSmall('Hello')
+
+        # this will raise ValueConstraintError
+        capital_and_small = CapitalAndSmall('hello')
+    """
+    def _testValue(self, value, idx):
+        for constraint in self._values:
+            constraint(value, idx)
+
+
+class ConstraintsUnion(AbstractConstraintSet):
+    """Create a ConstraintsUnion logic operator object.
+
+    The ConstraintsUnion logic operator succeeds if
+    *at least* a single operand succeeds.
+
+    The ConstraintsUnion object can be applied to
+    any constraint and logic operator objects.
+
+    The ConstraintsUnion object duck-types the immutable
+    container object like Python :py:class:`tuple`.
+
+    Parameters
+    ----------
+    *constraints:
+        Constraint or logic operator objects.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class CapitalOrSmall(IA5String):
+            '''
+            ASN.1 specification:
+
+            CapitalOrSmall ::=
+                IA5String (FROM ("A".."Z") | FROM ("a".."z"))
+            '''
+            subtypeSpec = ConstraintsUnion(
+                PermittedAlphabetConstraint('A', 'Z'),
+                PermittedAlphabetConstraint('a', 'z')
+            )
+
+        # this will succeed
+        capital_or_small = CapitalAndSmall('Hello')
+
+        # this will raise ValueConstraintError
+        capital_or_small = CapitalOrSmall('hello!')
+    """
+    def _testValue(self, value, idx):
+        for constraint in self._values:
+            try:
+                constraint(value, idx)
+            except error.ValueConstraintError:
+                pass
+            else:
+                return
+
+        raise error.ValueConstraintError(
+            'all of %s failed for "%s"' % (self._values, value)
+        )
+
+# TODO:
+# refactor InnerTypeConstraint
+# add tests for type check
+# implement other constraint types
+# make constraint validation easy to skip
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/error.py b/.venv/lib/python3.12/site-packages/pyasn1/type/error.py
new file mode 100644
index 00000000..0ff082ab
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/error.py
@@ -0,0 +1,11 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+from pyasn1.error import PyAsn1Error
+
+
+class ValueConstraintError(PyAsn1Error):
+    pass
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/namedtype.py b/.venv/lib/python3.12/site-packages/pyasn1/type/namedtype.py
new file mode 100644
index 00000000..5f6c4ca3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/namedtype.py
@@ -0,0 +1,550 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import sys
+
+from pyasn1 import error
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+__all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType',
+           'NamedTypes']
+
+class NamedType(object):
+    """Create named field object for a constructed ASN.1 type.
+
+    The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
+
+    |NamedType| objects are immutable and duck-type Python :class:`tuple` objects
+    holding *name* and *asn1Object* components.
+
+    Parameters
+    ----------
+    name: :py:class:`str`
+        Field name
+
+    asn1Object:
+        ASN.1 type object
+    """
+    isOptional = False
+    isDefaulted = False
+
+    def __init__(self, name, asn1Object, openType=None):
+        self.__name = name
+        self.__type = asn1Object
+        self.__nameAndType = name, asn1Object
+        self.__openType = openType
+
+    def __repr__(self):
+        representation = '%s=%r' % (self.name, self.asn1Object)
+
+        if self.openType:
+            representation += ', open type %r' % self.openType
+
+        return '<%s object, type %s>' % (
+            self.__class__.__name__, representation)
+
+    def __eq__(self, other):
+        return self.__nameAndType == other
+
+    def __ne__(self, other):
+        return self.__nameAndType != other
+
+    def __lt__(self, other):
+        return self.__nameAndType < other
+
+    def __le__(self, other):
+        return self.__nameAndType <= other
+
+    def __gt__(self, other):
+        return self.__nameAndType > other
+
+    def __ge__(self, other):
+        return self.__nameAndType >= other
+
+    def __hash__(self):
+        return hash(self.__nameAndType)
+
+    def __getitem__(self, idx):
+        return self.__nameAndType[idx]
+
+    def __iter__(self):
+        return iter(self.__nameAndType)
+
+    @property
+    def name(self):
+        return self.__name
+
+    @property
+    def asn1Object(self):
+        return self.__type
+
+    @property
+    def openType(self):
+        return self.__openType
+
+    # Backward compatibility
+
+    def getName(self):
+        return self.name
+
+    def getType(self):
+        return self.asn1Object
+
+
+class OptionalNamedType(NamedType):
+    __doc__ = NamedType.__doc__
+
+    isOptional = True
+
+
+class DefaultedNamedType(NamedType):
+    __doc__ = NamedType.__doc__
+
+    isDefaulted = True
+
+
+class NamedTypes(object):
+    """Create a collection of named fields for a constructed ASN.1 type.
+
+    The NamedTypes object represents a collection of named fields of a constructed ASN.1 type.
+
+    *NamedTypes* objects are immutable and duck-type Python :class:`dict` objects
+    holding *name* as keys and ASN.1 type object as values.
+
+    Parameters
+    ----------
+    *namedTypes: :class:`~pyasn1.type.namedtype.NamedType`
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class Description(Sequence):
+            '''
+            ASN.1 specification:
+
+            Description ::= SEQUENCE {
+                surname    IA5String,
+                first-name IA5String OPTIONAL,
+                age        INTEGER DEFAULT 40
+            }
+            '''
+            componentType = NamedTypes(
+                NamedType('surname', IA5String()),
+                OptionalNamedType('first-name', IA5String()),
+                DefaultedNamedType('age', Integer(40))
+            )
+
+        descr = Description()
+        descr['surname'] = 'Smith'
+        descr['first-name'] = 'John'
+    """
+    def __init__(self, *namedTypes, **kwargs):
+        self.__namedTypes = namedTypes
+        self.__namedTypesLen = len(self.__namedTypes)
+        self.__minTagSet = self.__computeMinTagSet()
+        self.__nameToPosMap = self.__computeNameToPosMap()
+        self.__tagToPosMap = self.__computeTagToPosMap()
+        self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {}
+        self.__uniqueTagMap = self.__computeTagMaps(unique=True)
+        self.__nonUniqueTagMap = self.__computeTagMaps(unique=False)
+        self.__hasOptionalOrDefault = any([True for namedType in self.__namedTypes
+                                           if namedType.isDefaulted or namedType.isOptional])
+        self.__hasOpenTypes = any([True for namedType in self.__namedTypes
+                                   if namedType.openType])
+
+        self.__requiredComponents = frozenset(
+                [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
+            )
+        self.__keys = frozenset([namedType.name for namedType in self.__namedTypes])
+        self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes])
+        self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes])
+
+    def __repr__(self):
+        representation = ', '.join(['%r' % x for x in self.__namedTypes])
+        return '<%s object, types %s>' % (
+            self.__class__.__name__, representation)
+
+    def __eq__(self, other):
+        return self.__namedTypes == other
+
+    def __ne__(self, other):
+        return self.__namedTypes != other
+
+    def __lt__(self, other):
+        return self.__namedTypes < other
+
+    def __le__(self, other):
+        return self.__namedTypes <= other
+
+    def __gt__(self, other):
+        return self.__namedTypes > other
+
+    def __ge__(self, other):
+        return self.__namedTypes >= other
+
+    def __hash__(self):
+        return hash(self.__namedTypes)
+
+    def __getitem__(self, idx):
+        try:
+            return self.__namedTypes[idx]
+
+        except TypeError:
+            return self.__namedTypes[self.__nameToPosMap[idx]]
+
+    def __contains__(self, key):
+        return key in self.__nameToPosMap
+
+    def __iter__(self):
+        return (x[0] for x in self.__namedTypes)
+
+    def __bool__(self):
+        return self.__namedTypesLen > 0
+
+    def __len__(self):
+        return self.__namedTypesLen
+
+    # Python dict protocol
+
+    def values(self):
+        return self.__values
+
+    def keys(self):
+        return self.__keys
+
+    def items(self):
+        return self.__items
+
+    def clone(self):
+        return self.__class__(*self.__namedTypes)
+
+    class PostponedError(object):
+        def __init__(self, errorMsg):
+            self.__errorMsg = errorMsg
+
+        def __getitem__(self, item):
+            raise  error.PyAsn1Error(self.__errorMsg)
+
+    def __computeTagToPosMap(self):
+        tagToPosMap = {}
+        for idx, namedType in enumerate(self.__namedTypes):
+            tagMap = namedType.asn1Object.tagMap
+            if isinstance(tagMap, NamedTypes.PostponedError):
+                return tagMap
+            if not tagMap:
+                continue
+            for _tagSet in tagMap.presentTypes:
+                if _tagSet in tagToPosMap:
+                    return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType))
+                tagToPosMap[_tagSet] = idx
+
+        return tagToPosMap
+
+    def __computeNameToPosMap(self):
+        nameToPosMap = {}
+        for idx, namedType in enumerate(self.__namedTypes):
+            if namedType.name in nameToPosMap:
+                return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType))
+            nameToPosMap[namedType.name] = idx
+
+        return nameToPosMap
+
+    def __computeAmbiguousTypes(self):
+        ambiguousTypes = {}
+        partialAmbiguousTypes = ()
+        for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
+            if namedType.isOptional or namedType.isDefaulted:
+                partialAmbiguousTypes = (namedType,) + partialAmbiguousTypes
+            else:
+                partialAmbiguousTypes = (namedType,)
+            if len(partialAmbiguousTypes) == len(self.__namedTypes):
+                ambiguousTypes[idx] = self
+            else:
+                ambiguousTypes[idx] = NamedTypes(*partialAmbiguousTypes, **dict(terminal=True))
+        return ambiguousTypes
+
+    def getTypeByPosition(self, idx):
+        """Return ASN.1 type object by its position in fields set.
+
+        Parameters
+        ----------
+        idx: :py:class:`int`
+            Field index
+
+        Returns
+        -------
+        :
+            ASN.1 type
+
+        Raises
+        ------
+        ~pyasn1.error.PyAsn1Error
+            If given position is out of fields range
+        """
+        try:
+            return self.__namedTypes[idx].asn1Object
+
+        except IndexError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def getPositionByType(self, tagSet):
+        """Return field position by its ASN.1 type.
+
+        Parameters
+        ----------
+        tagSet: :class:`~pysnmp.type.tag.TagSet`
+            ASN.1 tag set distinguishing one ASN.1 type from others.
+
+        Returns
+        -------
+        : :py:class:`int`
+            ASN.1 type position in fields set
+
+        Raises
+        ------
+        ~pyasn1.error.PyAsn1Error
+            If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes*
+        """
+        try:
+            return self.__tagToPosMap[tagSet]
+
+        except KeyError:
+            raise error.PyAsn1Error('Type %s not found' % (tagSet,))
+
+    def getNameByPosition(self, idx):
+        """Return field name by its position in fields set.
+
+        Parameters
+        ----------
+        idx: :py:class:`idx`
+            Field index
+
+        Returns
+        -------
+        : :py:class:`str`
+            Field name
+
+        Raises
+        ------
+        ~pyasn1.error.PyAsn1Error
+            If given field name is not present in callee *NamedTypes*
+        """
+        try:
+            return self.__namedTypes[idx].name
+
+        except IndexError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def getPositionByName(self, name):
+        """Return field position by filed name.
+
+        Parameters
+        ----------
+        name: :py:class:`str`
+            Field name
+
+        Returns
+        -------
+        : :py:class:`int`
+            Field position in fields set
+
+        Raises
+        ------
+        ~pyasn1.error.PyAsn1Error
+            If *name* is not present or not unique within callee *NamedTypes*
+        """
+        try:
+            return self.__nameToPosMap[name]
+
+        except KeyError:
+            raise error.PyAsn1Error('Name %s not found' % (name,))
+
+    def getTagMapNearPosition(self, idx):
+        """Return ASN.1 types that are allowed at or past given field position.
+
+        Some ASN.1 serialisation allow for skipping optional and defaulted fields.
+        Some constructed ASN.1 types allow reordering of the fields. When recovering
+        such objects it may be important to know which types can possibly be
+        present at any given position in the field sets.
+
+        Parameters
+        ----------
+        idx: :py:class:`int`
+            Field index
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tagmap.TagMap`
+            Map if ASN.1 types allowed at given field position
+
+        Raises
+        ------
+        ~pyasn1.error.PyAsn1Error
+            If given position is out of fields range
+        """
+        try:
+            return self.__ambiguousTypes[idx].tagMap
+
+        except KeyError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def getPositionNearType(self, tagSet, idx):
+        """Return the closest field position where given ASN.1 type is allowed.
+
+        Some ASN.1 serialisation allow for skipping optional and defaulted fields.
+        Some constructed ASN.1 types allow reordering of the fields. When recovering
+        such objects it may be important to know at which field position, in field set,
+        given *tagSet* is allowed at or past *idx* position.
+
+        Parameters
+        ----------
+        tagSet: :class:`~pyasn1.type.tag.TagSet`
+           ASN.1 type which field position to look up
+
+        idx: :py:class:`int`
+            Field position at or past which to perform ASN.1 type look up
+
+        Returns
+        -------
+        : :py:class:`int`
+            Field position in fields set
+
+        Raises
+        ------
+        ~pyasn1.error.PyAsn1Error
+            If *tagSet* is not present or not unique within callee *NamedTypes*
+            or *idx* is out of fields range
+        """
+        try:
+            return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet)
+
+        except KeyError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def __computeMinTagSet(self):
+        minTagSet = None
+        for namedType in self.__namedTypes:
+            asn1Object = namedType.asn1Object
+
+            try:
+                tagSet = asn1Object.minTagSet
+
+            except AttributeError:
+                tagSet = asn1Object.tagSet
+
+            if minTagSet is None or tagSet < minTagSet:
+                minTagSet = tagSet
+
+        return minTagSet or tag.TagSet()
+
+    @property
+    def minTagSet(self):
+        """Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
+
+        Some ASN.1 types/serialisation protocols require ASN.1 types to be
+        arranged based on their numerical tag value. The *minTagSet* property
+        returns that.
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tagset.TagSet`
+            Minimal TagSet among ASN.1 types in callee *NamedTypes*
+        """
+        return self.__minTagSet
+
+    def __computeTagMaps(self, unique):
+        presentTypes = {}
+        skipTypes = {}
+        defaultType = None
+        for namedType in self.__namedTypes:
+            tagMap = namedType.asn1Object.tagMap
+            if isinstance(tagMap, NamedTypes.PostponedError):
+                return tagMap
+            for tagSet in tagMap:
+                if unique and tagSet in presentTypes:
+                    return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self))
+                presentTypes[tagSet] = namedType.asn1Object
+            skipTypes.update(tagMap.skipTypes)
+
+            if defaultType is None:
+                defaultType = tagMap.defaultType
+            elif tagMap.defaultType is not None:
+                return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,))
+
+        return tagmap.TagMap(presentTypes, skipTypes, defaultType)
+
+    @property
+    def tagMap(self):
+        """Return a *TagMap* object from tags and types recursively.
+
+        Return a :class:`~pyasn1.type.tagmap.TagMap` object by
+        combining tags from *TagMap* objects of children types and
+        associating them with their immediate child type.
+
+        Example
+        -------
+        .. code-block:: python
+
+           OuterType ::= CHOICE {
+               innerType INTEGER
+           }
+
+        Calling *.tagMap* on *OuterType* will yield a map like this:
+
+        .. code-block:: python
+
+           Integer.tagSet -> Choice
+        """
+        return self.__nonUniqueTagMap
+
+    @property
+    def tagMapUnique(self):
+        """Return a *TagMap* object from unique tags and types recursively.
+
+        Return a :class:`~pyasn1.type.tagmap.TagMap` object by
+        combining tags from *TagMap* objects of children types and
+        associating them with their immediate child type.
+
+        Example
+        -------
+        .. code-block:: python
+
+           OuterType ::= CHOICE {
+               innerType INTEGER
+           }
+
+        Calling *.tagMapUnique* on *OuterType* will yield a map like this:
+
+        .. code-block:: python
+
+           Integer.tagSet -> Choice
+
+        Note
+        ----
+
+        Duplicate *TagSet* objects found in the tree of children
+        types would cause error.
+        """
+        return self.__uniqueTagMap
+
+    @property
+    def hasOptionalOrDefault(self):
+        return self.__hasOptionalOrDefault
+
+    @property
+    def hasOpenTypes(self):
+        return self.__hasOpenTypes
+
+    @property
+    def namedTypes(self):
+        return tuple(self.__namedTypes)
+
+    @property
+    def requiredComponents(self):
+        return self.__requiredComponents
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/namedval.py b/.venv/lib/python3.12/site-packages/pyasn1/type/namedval.py
new file mode 100644
index 00000000..46a6496d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/namedval.py
@@ -0,0 +1,192 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+# ASN.1 named integers
+#
+from pyasn1 import error
+
+__all__ = ['NamedValues']
+
+
+class NamedValues(object):
+    """Create named values object.
+
+    The |NamedValues| object represents a collection of string names
+    associated with numeric IDs. These objects are used for giving
+    names to otherwise numerical values.
+
+    |NamedValues| objects are immutable and duck-type Python
+    :class:`dict` object mapping ID to name and vice-versa.
+
+    Parameters
+    ----------
+    *args: variable number of two-element :py:class:`tuple`
+
+        name: :py:class:`str`
+            Value label
+
+        value: :py:class:`int`
+            Numeric value
+
+    Keyword Args
+    ------------
+    name: :py:class:`str`
+        Value label
+
+    value: :py:class:`int`
+        Numeric value
+
+    Examples
+    --------
+
+    .. code-block:: pycon
+
+        >>> nv = NamedValues('a', 'b', ('c', 0), d=1)
+        >>> nv
+        >>> {'c': 0, 'd': 1, 'a': 2, 'b': 3}
+        >>> nv[0]
+        'c'
+        >>> nv['a']
+        2
+    """
+    def __init__(self, *args, **kwargs):
+        self.__names = {}
+        self.__numbers = {}
+
+        anonymousNames = []
+
+        for namedValue in args:
+            if isinstance(namedValue, (tuple, list)):
+                try:
+                    name, number = namedValue
+
+                except ValueError:
+                    raise error.PyAsn1Error('Not a proper attribute-value pair %r' % (namedValue,))
+
+            else:
+                anonymousNames.append(namedValue)
+                continue
+
+            if name in self.__names:
+                raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+            if number in self.__numbers:
+                raise error.PyAsn1Error('Duplicate number  %s=%s' % (name, number))
+
+            self.__names[name] = number
+            self.__numbers[number] = name
+
+        for name, number in kwargs.items():
+            if name in self.__names:
+                raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+            if number in self.__numbers:
+                raise error.PyAsn1Error('Duplicate number  %s=%s' % (name, number))
+
+            self.__names[name] = number
+            self.__numbers[number] = name
+
+        if anonymousNames:
+
+            number = self.__numbers and max(self.__numbers) + 1 or 0
+
+            for name in anonymousNames:
+
+                if name in self.__names:
+                    raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+                self.__names[name] = number
+                self.__numbers[number] = name
+
+                number += 1
+
+    def __repr__(self):
+        representation = ', '.join(['%s=%d' % x for x in self.items()])
+
+        if len(representation) > 64:
+            representation = representation[:32] + '...' + representation[-32:]
+
+        return '<%s object, enums %s>' % (
+            self.__class__.__name__, representation)
+
+    def __eq__(self, other):
+        return dict(self) == other
+
+    def __ne__(self, other):
+        return dict(self) != other
+
+    def __lt__(self, other):
+        return dict(self) < other
+
+    def __le__(self, other):
+        return dict(self) <= other
+
+    def __gt__(self, other):
+        return dict(self) > other
+
+    def __ge__(self, other):
+        return dict(self) >= other
+
+    def __hash__(self):
+        return hash(self.items())
+
+    # Python dict protocol (read-only)
+
+    def __getitem__(self, key):
+        try:
+            return self.__numbers[key]
+
+        except KeyError:
+            return self.__names[key]
+
+    def __len__(self):
+        return len(self.__names)
+
+    def __contains__(self, key):
+        return key in self.__names or key in self.__numbers
+
+    def __iter__(self):
+        return iter(self.__names)
+
+    def values(self):
+        return iter(self.__numbers)
+
+    def keys(self):
+        return iter(self.__names)
+
+    def items(self):
+        for name in self.__names:
+            yield name, self.__names[name]
+
+    # support merging
+
+    def __add__(self, namedValues):
+        return self.__class__(*tuple(self.items()) + tuple(namedValues.items()))
+
+    # XXX clone/subtype?
+
+    def clone(self, *args, **kwargs):
+        new = self.__class__(*args, **kwargs)
+        return self + new
+
+    # legacy protocol
+
+    def getName(self, value):
+        if value in self.__numbers:
+            return self.__numbers[value]
+
+    def getValue(self, name):
+        if name in self.__names:
+            return self.__names[name]
+
+    def getValues(self, *names):
+        try:
+            return [self.__names[name] for name in names]
+
+        except KeyError:
+            raise error.PyAsn1Error(
+                'Unknown bit identifier(s): %s' % (set(names).difference(self.__names),)
+            )
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/opentype.py b/.venv/lib/python3.12/site-packages/pyasn1/type/opentype.py
new file mode 100644
index 00000000..5a15f896
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/opentype.py
@@ -0,0 +1,104 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+
+__all__ = ['OpenType']
+
+
+class OpenType(object):
+    """Create ASN.1 type map indexed by a value
+
+    The *OpenType* object models an untyped field of a constructed ASN.1
+    type. In ASN.1 syntax it is usually represented by the
+    `ANY DEFINED BY` for scalars or `SET OF ANY DEFINED BY`,
+    `SEQUENCE OF ANY DEFINED BY` for container types clauses. Typically
+    used together with :class:`~pyasn1.type.univ.Any` object.
+
+    OpenType objects duck-type a read-only Python :class:`dict` objects,
+    however the passed `typeMap` is not copied, but stored by reference.
+    That means the user can manipulate `typeMap` at run time having this
+    reflected on *OpenType* object behavior.
+
+    The |OpenType| class models an untyped field of a constructed ASN.1
+    type. In ASN.1 syntax it is usually represented by the
+    `ANY DEFINED BY` for scalars or `SET OF ANY DEFINED BY`,
+    `SEQUENCE OF ANY DEFINED BY` for container types clauses. Typically
+    used with :class:`~pyasn1.type.univ.Any` type.
+
+    Parameters
+    ----------
+    name: :py:class:`str`
+        Field name
+
+    typeMap: :py:class:`dict`
+        A map of value->ASN.1 type. It's stored by reference and can be
+        mutated later to register new mappings.
+
+    Examples
+    --------
+
+    For untyped scalars:
+
+    .. code-block:: python
+
+        openType = OpenType(
+            'id', {1: Integer(),
+                   2: OctetString()}
+        )
+        Sequence(
+            componentType=NamedTypes(
+                NamedType('id', Integer()),
+                NamedType('blob', Any(), openType=openType)
+            )
+        )
+
+    For untyped `SET OF` or `SEQUENCE OF` vectors:
+
+    .. code-block:: python
+
+        openType = OpenType(
+            'id', {1: Integer(),
+                   2: OctetString()}
+        )
+        Sequence(
+            componentType=NamedTypes(
+                NamedType('id', Integer()),
+                NamedType('blob', SetOf(componentType=Any()),
+                          openType=openType)
+            )
+        )
+    """
+
+    def __init__(self, name, typeMap=None):
+        self.__name = name
+        if typeMap is None:
+            self.__typeMap = {}
+        else:
+            self.__typeMap = typeMap
+
+    @property
+    def name(self):
+        return self.__name
+
+    # Python dict protocol
+
+    def values(self):
+        return self.__typeMap.values()
+
+    def keys(self):
+        return self.__typeMap.keys()
+
+    def items(self):
+        return self.__typeMap.items()
+
+    def __contains__(self, key):
+        return key in self.__typeMap
+
+    def __getitem__(self, key):
+        return self.__typeMap[key]
+
+    def __iter__(self):
+        return iter(self.__typeMap)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/tag.py b/.venv/lib/python3.12/site-packages/pyasn1/type/tag.py
new file mode 100644
index 00000000..ccb8b00c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/tag.py
@@ -0,0 +1,335 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+from pyasn1 import error
+
+__all__ = ['tagClassUniversal', 'tagClassApplication', 'tagClassContext',
+           'tagClassPrivate', 'tagFormatSimple', 'tagFormatConstructed',
+           'tagCategoryImplicit', 'tagCategoryExplicit',
+           'tagCategoryUntagged', 'Tag', 'TagSet']
+
+#: Identifier for ASN.1 class UNIVERSAL
+tagClassUniversal = 0x00
+
+#: Identifier for ASN.1 class APPLICATION
+tagClassApplication = 0x40
+
+#: Identifier for ASN.1 class context-specific
+tagClassContext = 0x80
+
+#: Identifier for ASN.1 class private
+tagClassPrivate = 0xC0
+
+#: Identifier for "simple" ASN.1 structure (e.g. scalar)
+tagFormatSimple = 0x00
+
+#: Identifier for "constructed" ASN.1 structure (e.g. may have inner components)
+tagFormatConstructed = 0x20
+
+tagCategoryImplicit = 0x01
+tagCategoryExplicit = 0x02
+tagCategoryUntagged = 0x04
+
+
+class Tag(object):
+    """Create ASN.1 tag
+
+    Represents ASN.1 tag that can be attached to a ASN.1 type to make
+    types distinguishable from each other.
+
+    *Tag* objects are immutable and duck-type Python :class:`tuple` objects
+    holding three integer components of a tag.
+
+    Parameters
+    ----------
+    tagClass: :py:class:`int`
+        Tag *class* value
+
+    tagFormat: :py:class:`int`
+        Tag *format* value
+
+    tagId: :py:class:`int`
+        Tag ID value
+    """
+    def __init__(self, tagClass, tagFormat, tagId):
+        if tagId < 0:
+            raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId)
+        self.__tagClass = tagClass
+        self.__tagFormat = tagFormat
+        self.__tagId = tagId
+        self.__tagClassId = tagClass, tagId
+        self.__hash = hash(self.__tagClassId)
+
+    def __repr__(self):
+        representation = '[%s:%s:%s]' % (
+            self.__tagClass, self.__tagFormat, self.__tagId)
+        return '<%s object, tag %s>' % (
+            self.__class__.__name__, representation)
+
+    def __eq__(self, other):
+        return self.__tagClassId == other
+
+    def __ne__(self, other):
+        return self.__tagClassId != other
+
+    def __lt__(self, other):
+        return self.__tagClassId < other
+
+    def __le__(self, other):
+        return self.__tagClassId <= other
+
+    def __gt__(self, other):
+        return self.__tagClassId > other
+
+    def __ge__(self, other):
+        return self.__tagClassId >= other
+
+    def __hash__(self):
+        return self.__hash
+
+    def __getitem__(self, idx):
+        if idx == 0:
+            return self.__tagClass
+        elif idx == 1:
+            return self.__tagFormat
+        elif idx == 2:
+            return self.__tagId
+        else:
+            raise IndexError
+
+    def __iter__(self):
+        yield self.__tagClass
+        yield self.__tagFormat
+        yield self.__tagId
+
+    def __and__(self, otherTag):
+        return self.__class__(self.__tagClass & otherTag.tagClass,
+                              self.__tagFormat & otherTag.tagFormat,
+                              self.__tagId & otherTag.tagId)
+
+    def __or__(self, otherTag):
+        return self.__class__(self.__tagClass | otherTag.tagClass,
+                              self.__tagFormat | otherTag.tagFormat,
+                              self.__tagId | otherTag.tagId)
+
+    @property
+    def tagClass(self):
+        """ASN.1 tag class
+
+        Returns
+        -------
+        : :py:class:`int`
+            Tag class
+        """
+        return self.__tagClass
+
+    @property
+    def tagFormat(self):
+        """ASN.1 tag format
+
+        Returns
+        -------
+        : :py:class:`int`
+            Tag format
+        """
+        return self.__tagFormat
+
+    @property
+    def tagId(self):
+        """ASN.1 tag ID
+
+        Returns
+        -------
+        : :py:class:`int`
+            Tag ID
+        """
+        return self.__tagId
+
+
+class TagSet(object):
+    """Create a collection of ASN.1 tags
+
+    Represents a combination of :class:`~pyasn1.type.tag.Tag` objects
+    that can be attached to a ASN.1 type to make types distinguishable
+    from each other.
+
+    *TagSet* objects are immutable and duck-type Python :class:`tuple` objects
+    holding arbitrary number of :class:`~pyasn1.type.tag.Tag` objects.
+
+    Parameters
+    ----------
+    baseTag: :class:`~pyasn1.type.tag.Tag`
+        Base *Tag* object. This tag survives IMPLICIT tagging.
+
+    *superTags: :class:`~pyasn1.type.tag.Tag`
+        Additional *Tag* objects taking part in subtyping.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class OrderNumber(NumericString):
+            '''
+            ASN.1 specification
+
+            Order-number ::=
+                [APPLICATION 5] IMPLICIT NumericString
+            '''
+            tagSet = NumericString.tagSet.tagImplicitly(
+                Tag(tagClassApplication, tagFormatSimple, 5)
+            )
+
+        orderNumber = OrderNumber('1234')
+    """
+    def __init__(self, baseTag=(), *superTags):
+        self.__baseTag = baseTag
+        self.__superTags = superTags
+        self.__superTagsClassId = tuple(
+            [(superTag.tagClass, superTag.tagId) for superTag in superTags]
+        )
+        self.__lenOfSuperTags = len(superTags)
+        self.__hash = hash(self.__superTagsClassId)
+
+    def __repr__(self):
+        representation = '-'.join(['%s:%s:%s' % (x.tagClass, x.tagFormat, x.tagId)
+                                   for x in self.__superTags])
+        if representation:
+            representation = 'tags ' + representation
+        else:
+            representation = 'untagged'
+
+        return '<%s object, %s>' % (self.__class__.__name__, representation)
+
+    def __add__(self, superTag):
+        return self.__class__(self.__baseTag, *self.__superTags + (superTag,))
+
+    def __radd__(self, superTag):
+        return self.__class__(self.__baseTag, *(superTag,) + self.__superTags)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.__class__(self.__baseTag, *self.__superTags[i])
+        else:
+            return self.__superTags[i]
+
+    def __eq__(self, other):
+        return self.__superTagsClassId == other
+
+    def __ne__(self, other):
+        return self.__superTagsClassId != other
+
+    def __lt__(self, other):
+        return self.__superTagsClassId < other
+
+    def __le__(self, other):
+        return self.__superTagsClassId <= other
+
+    def __gt__(self, other):
+        return self.__superTagsClassId > other
+
+    def __ge__(self, other):
+        return self.__superTagsClassId >= other
+
+    def __hash__(self):
+        return self.__hash
+
+    def __len__(self):
+        return self.__lenOfSuperTags
+
+    @property
+    def baseTag(self):
+        """Return base ASN.1 tag
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tag.Tag`
+            Base tag of this *TagSet*
+        """
+        return self.__baseTag
+
+    @property
+    def superTags(self):
+        """Return ASN.1 tags
+
+        Returns
+        -------
+        : :py:class:`tuple`
+            Tuple of :class:`~pyasn1.type.tag.Tag` objects that this *TagSet* contains
+        """
+        return self.__superTags
+
+    def tagExplicitly(self, superTag):
+        """Return explicitly tagged *TagSet*
+
+        Create a new *TagSet* representing callee *TagSet* explicitly tagged
+        with passed tag(s). With explicit tagging mode, new tags are appended
+        to existing tag(s).
+
+        Parameters
+        ----------
+        superTag: :class:`~pyasn1.type.tag.Tag`
+            *Tag* object to tag this *TagSet*
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tag.TagSet`
+            New *TagSet* object
+        """
+        if superTag.tagClass == tagClassUniversal:
+            raise error.PyAsn1Error("Can't tag with UNIVERSAL class tag")
+        if superTag.tagFormat != tagFormatConstructed:
+            superTag = Tag(superTag.tagClass, tagFormatConstructed, superTag.tagId)
+        return self + superTag
+
+    def tagImplicitly(self, superTag):
+        """Return implicitly tagged *TagSet*
+
+        Create a new *TagSet* representing callee *TagSet* implicitly tagged
+        with passed tag(s). With implicit tagging mode, new tag(s) replace the
+        last existing tag.
+
+        Parameters
+        ----------
+        superTag: :class:`~pyasn1.type.tag.Tag`
+            *Tag* object to tag this *TagSet*
+
+        Returns
+        -------
+        : :class:`~pyasn1.type.tag.TagSet`
+            New *TagSet* object
+        """
+        if self.__superTags:
+            superTag = Tag(superTag.tagClass, self.__superTags[-1].tagFormat, superTag.tagId)
+        return self[:-1] + superTag
+
+    def isSuperTagSetOf(self, tagSet):
+        """Test type relationship against given *TagSet*
+
+        The callee is considered to be a supertype of given *TagSet*
+        tag-wise if all tags in *TagSet* are present in the callee and
+        they are in the same order.
+
+        Parameters
+        ----------
+        tagSet: :class:`~pyasn1.type.tag.TagSet`
+            *TagSet* object to evaluate against the callee
+
+        Returns
+        -------
+        : :py:class:`bool`
+            :obj:`True` if callee is a supertype of *tagSet*
+        """
+        if len(tagSet) < self.__lenOfSuperTags:
+            return False
+        return self.__superTags == tagSet[:self.__lenOfSuperTags]
+
+    # Backward compatibility
+
+    def getBaseTag(self):
+        return self.__baseTag
+
+def initTagSet(tag):
+    return TagSet(tag, tag)
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/tagmap.py b/.venv/lib/python3.12/site-packages/pyasn1/type/tagmap.py
new file mode 100644
index 00000000..7f8a955a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/tagmap.py
@@ -0,0 +1,96 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+from pyasn1 import error
+
+__all__ = ['TagMap']
+
+
+class TagMap(object):
+    """Map *TagSet* objects to ASN.1 types
+
+    Create an object mapping *TagSet* object to ASN.1 type.
+
+    *TagMap* objects are immutable and duck-type read-only Python
+    :class:`dict` objects holding *TagSet* objects as keys and ASN.1
+    type objects as values.
+
+    Parameters
+    ----------
+    presentTypes: :py:class:`dict`
+        Map of :class:`~pyasn1.type.tag.TagSet` to ASN.1 objects considered
+        as being unconditionally present in the *TagMap*.
+
+    skipTypes: :py:class:`dict`
+        A collection of :class:`~pyasn1.type.tag.TagSet` objects considered
+        as absent in the *TagMap* even when *defaultType* is present.
+
+    defaultType: ASN.1 type object
+        An ASN.1 type object callee *TagMap* returns for any *TagSet* key not present
+        in *presentTypes* (unless given key is present in *skipTypes*).
+    """
+    def __init__(self, presentTypes=None, skipTypes=None, defaultType=None):
+        self.__presentTypes = presentTypes or {}
+        self.__skipTypes = skipTypes or {}
+        self.__defaultType = defaultType
+
+    def __contains__(self, tagSet):
+        return (tagSet in self.__presentTypes or
+                self.__defaultType is not None and tagSet not in self.__skipTypes)
+
+    def __getitem__(self, tagSet):
+        try:
+            return self.__presentTypes[tagSet]
+        except KeyError:
+            if self.__defaultType is None:
+                raise
+            elif tagSet in self.__skipTypes:
+                raise error.PyAsn1Error('Key in negative map')
+            else:
+                return self.__defaultType
+
+    def __iter__(self):
+        return iter(self.__presentTypes)
+
+    def __repr__(self):
+        representation = '%s object' % self.__class__.__name__
+
+        if self.__presentTypes:
+            representation += ', present %s' % repr(self.__presentTypes)
+
+        if self.__skipTypes:
+            representation += ', skip %s' % repr(self.__skipTypes)
+
+        if self.__defaultType is not None:
+            representation += ', default %s' % repr(self.__defaultType)
+
+        return '<%s>' % representation
+
+    @property
+    def presentTypes(self):
+        """Return *TagSet* to ASN.1 type map present in callee *TagMap*"""
+        return self.__presentTypes
+
+    @property
+    def skipTypes(self):
+        """Return *TagSet* collection unconditionally absent in callee *TagMap*"""
+        return self.__skipTypes
+
+    @property
+    def defaultType(self):
+        """Return default ASN.1 type being returned for any missing *TagSet*"""
+        return self.__defaultType
+
+    # Backward compatibility
+
+    def getPosMap(self):
+        return self.presentTypes
+
+    def getNegMap(self):
+        return self.skipTypes
+
+    def getDef(self):
+        return self.defaultType
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/univ.py b/.venv/lib/python3.12/site-packages/pyasn1/type/univ.py
new file mode 100644
index 00000000..9aff5e69
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/univ.py
@@ -0,0 +1,3327 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import math
+import sys
+
+from pyasn1 import error
+from pyasn1.codec.ber import eoo
+from pyasn1.compat import integer
+from pyasn1.type import base
+from pyasn1.type import constraint
+from pyasn1.type import namedtype
+from pyasn1.type import namedval
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+NoValue = base.NoValue
+noValue = NoValue()
+
+__all__ = ['Integer', 'Boolean', 'BitString', 'OctetString', 'Null',
+           'ObjectIdentifier', 'Real', 'Enumerated',
+           'SequenceOfAndSetOfBase', 'SequenceOf', 'SetOf',
+           'SequenceAndSetBase', 'Sequence', 'Set', 'Choice', 'Any',
+           'NoValue', 'noValue']
+
+# "Simple" ASN.1 types (yet incomplete)
+
+
+class Integer(base.SimpleAsn1Type):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type Python :class:`int` objects.
+
+    Keyword Args
+    ------------
+    value: :class:`int`, :class:`str` or |ASN.1| object
+        Python :class:`int` or :class:`str` literal or |ASN.1| class
+        instance. If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+        Object representing non-default symbolic aliases for numbers
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class ErrorCode(Integer):
+            '''
+            ASN.1 specification:
+
+            ErrorCode ::=
+                INTEGER { disk-full(1), no-disk(-1),
+                          disk-not-formatted(2) }
+
+            error ErrorCode ::= disk-full
+            '''
+            namedValues = NamedValues(
+                ('disk-full', 1), ('no-disk', -1),
+                ('disk-not-formatted', 2)
+            )
+
+        error = ErrorCode('disk-full')
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+    #: representing symbolic aliases for numbers
+    namedValues = namedval.NamedValues()
+
+    # Optimization for faster codec lookup
+    typeId = base.SimpleAsn1Type.getTypeId()
+
+    def __init__(self, value=noValue, **kwargs):
+        if 'namedValues' not in kwargs:
+            kwargs['namedValues'] = self.namedValues
+
+        base.SimpleAsn1Type.__init__(self, value, **kwargs)
+
+    def __and__(self, value):
+        return self.clone(self._value & value)
+
+    def __rand__(self, value):
+        return self.clone(value & self._value)
+
+    def __or__(self, value):
+        return self.clone(self._value | value)
+
+    def __ror__(self, value):
+        return self.clone(value | self._value)
+
+    def __xor__(self, value):
+        return self.clone(self._value ^ value)
+
+    def __rxor__(self, value):
+        return self.clone(value ^ self._value)
+
+    def __lshift__(self, value):
+        return self.clone(self._value << value)
+
+    def __rshift__(self, value):
+        return self.clone(self._value >> value)
+
+    def __add__(self, value):
+        return self.clone(self._value + value)
+
+    def __radd__(self, value):
+        return self.clone(value + self._value)
+
+    def __sub__(self, value):
+        return self.clone(self._value - value)
+
+    def __rsub__(self, value):
+        return self.clone(value - self._value)
+
+    def __mul__(self, value):
+        return self.clone(self._value * value)
+
+    def __rmul__(self, value):
+        return self.clone(value * self._value)
+
+    def __mod__(self, value):
+        return self.clone(self._value % value)
+
+    def __rmod__(self, value):
+        return self.clone(value % self._value)
+
+    def __pow__(self, value, modulo=None):
+        return self.clone(pow(self._value, value, modulo))
+
+    def __rpow__(self, value):
+        return self.clone(pow(value, self._value))
+
+    def __floordiv__(self, value):
+        return self.clone(self._value // value)
+
+    def __rfloordiv__(self, value):
+        return self.clone(value // self._value)
+
+    def __truediv__(self, value):
+        return Real(self._value / value)
+
+    def __rtruediv__(self, value):
+        return Real(value / self._value)
+
+    def __divmod__(self, value):
+        return self.clone(divmod(self._value, value))
+
+    def __rdivmod__(self, value):
+        return self.clone(divmod(value, self._value))
+
+    __hash__ = base.SimpleAsn1Type.__hash__
+
+    def __int__(self):
+        return int(self._value)
+
+    def __float__(self):
+        return float(self._value)
+
+    def __abs__(self):
+        return self.clone(abs(self._value))
+
+    def __index__(self):
+        return int(self._value)
+
+    def __pos__(self):
+        return self.clone(+self._value)
+
+    def __neg__(self):
+        return self.clone(-self._value)
+
+    def __invert__(self):
+        return self.clone(~self._value)
+
+    def __round__(self, n=0):
+        r = round(self._value, n)
+        if n:
+            return self.clone(r)
+        else:
+            return r
+
+    def __floor__(self):
+        return math.floor(self._value)
+
+    def __ceil__(self):
+        return math.ceil(self._value)
+
+    def __trunc__(self):
+        return self.clone(math.trunc(self._value))
+
+    def __lt__(self, value):
+        return self._value < value
+
+    def __le__(self, value):
+        return self._value <= value
+
+    def __eq__(self, value):
+        return self._value == value
+
+    def __ne__(self, value):
+        return self._value != value
+
+    def __gt__(self, value):
+        return self._value > value
+
+    def __ge__(self, value):
+        return self._value >= value
+
+    def prettyIn(self, value):
+        try:
+            return int(value)
+
+        except ValueError:
+            try:
+                return self.namedValues[value]
+
+            except KeyError as exc:
+                raise error.PyAsn1Error(
+                    'Can\'t coerce %r into integer: %s' % (value, exc)
+                )
+
+    def prettyOut(self, value):
+        try:
+            return str(self.namedValues[value])
+
+        except KeyError:
+            return str(value)
+
+    # backward compatibility
+
+    def getNamedValues(self):
+        return self.namedValues
+
+
+class Boolean(Integer):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type Python :class:`int` objects.
+
+    Keyword Args
+    ------------
+    value: :class:`int`, :class:`str` or |ASN.1| object
+        Python :class:`int` or :class:`str` literal or |ASN.1| class
+        instance. If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s).Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+        Object representing non-default symbolic aliases for numbers
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class RoundResult(Boolean):
+            '''
+            ASN.1 specification:
+
+            RoundResult ::= BOOLEAN
+
+            ok RoundResult ::= TRUE
+            ko RoundResult ::= FALSE
+            '''
+        ok = RoundResult(True)
+        ko = RoundResult(False)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01),
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = Integer.subtypeSpec + constraint.SingleValueConstraint(0, 1)
+
+    #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+    #: representing symbolic aliases for numbers
+    namedValues = namedval.NamedValues(('False', 0), ('True', 1))
+
+    # Optimization for faster codec lookup
+    typeId = Integer.getTypeId()
+
+
+class SizedInteger(int):
+    bitLength = leadingZeroBits = None
+
+    def setBitLength(self, bitLength):
+        self.bitLength = bitLength
+        self.leadingZeroBits = max(bitLength - self.bit_length(), 0)
+        return self
+
+    def __len__(self):
+        if self.bitLength is None:
+            self.setBitLength(self.bit_length())
+
+        return self.bitLength
+
+
+class BitString(base.SimpleAsn1Type):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type both Python :class:`tuple` (as a tuple
+    of bits) and :class:`int` objects.
+
+    Keyword Args
+    ------------
+    value: :class:`int`, :class:`str` or |ASN.1| object
+        Python :class:`int` or :class:`str` literal representing binary
+        or hexadecimal number or sequence of integer bits or |ASN.1| object.
+        If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+        Object representing non-default symbolic aliases for numbers
+
+    binValue: :py:class:`str`
+        Binary string initializer to use instead of the *value*.
+        Example: '10110011'.
+
+    hexValue: :py:class:`str`
+        Hexadecimal string initializer to use instead of the *value*.
+        Example: 'DEADBEEF'.
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Rights(BitString):
+            '''
+            ASN.1 specification:
+
+            Rights ::= BIT STRING { user-read(0), user-write(1),
+                                    group-read(2), group-write(3),
+                                    other-read(4), other-write(5) }
+
+            group1 Rights ::= { group-read, group-write }
+            group2 Rights ::= '0011'B
+            group3 Rights ::= '3'H
+            '''
+            namedValues = NamedValues(
+                ('user-read', 0), ('user-write', 1),
+                ('group-read', 2), ('group-write', 3),
+                ('other-read', 4), ('other-write', 5)
+            )
+
+        group1 = Rights(('group-read', 'group-write'))
+        group2 = Rights('0011')
+        group3 = Rights(0x3)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+    #: representing symbolic aliases for numbers
+    namedValues = namedval.NamedValues()
+
+    # Optimization for faster codec lookup
+    typeId = base.SimpleAsn1Type.getTypeId()
+
+    defaultBinValue = defaultHexValue = noValue
+
+    def __init__(self, value=noValue, **kwargs):
+        if value is noValue:
+            if kwargs:
+                try:
+                    value = self.fromBinaryString(kwargs.pop('binValue'), internalFormat=True)
+
+                except KeyError:
+                    pass
+
+                try:
+                    value = self.fromHexString(kwargs.pop('hexValue'), internalFormat=True)
+
+                except KeyError:
+                    pass
+
+        if value is noValue:
+            if self.defaultBinValue is not noValue:
+                value = self.fromBinaryString(self.defaultBinValue, internalFormat=True)
+
+            elif self.defaultHexValue is not noValue:
+                value = self.fromHexString(self.defaultHexValue, internalFormat=True)
+
+        if 'namedValues' not in kwargs:
+            kwargs['namedValues'] = self.namedValues
+
+        base.SimpleAsn1Type.__init__(self, value, **kwargs)
+
+    def __str__(self):
+        return self.asBinary()
+
+    def __eq__(self, other):
+        other = self.prettyIn(other)
+        return self is other or self._value == other and len(self._value) == len(other)
+
+    def __ne__(self, other):
+        other = self.prettyIn(other)
+        return self._value != other or len(self._value) != len(other)
+
+    def __lt__(self, other):
+        other = self.prettyIn(other)
+        return len(self._value) < len(other) or len(self._value) == len(other) and self._value < other
+
+    def __le__(self, other):
+        other = self.prettyIn(other)
+        return len(self._value) <= len(other) or len(self._value) == len(other) and self._value <= other
+
+    def __gt__(self, other):
+        other = self.prettyIn(other)
+        return len(self._value) > len(other) or len(self._value) == len(other) and self._value > other
+
+    def __ge__(self, other):
+        other = self.prettyIn(other)
+        return len(self._value) >= len(other) or len(self._value) == len(other) and self._value >= other
+
+    # Immutable sequence object protocol
+
+    def __len__(self):
+        return len(self._value)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.clone([self[x] for x in range(*i.indices(len(self)))])
+        else:
+            length = len(self._value) - 1
+            if i > length or i < 0:
+                raise IndexError('bit index out of range')
+            return (self._value >> (length - i)) & 1
+
+    def __iter__(self):
+        length = len(self._value)
+        while length:
+            length -= 1
+            yield (self._value >> length) & 1
+
+    def __reversed__(self):
+        return reversed(tuple(self))
+
+    # arithmetic operators
+
+    def __add__(self, value):
+        value = self.prettyIn(value)
+        return self.clone(SizedInteger(self._value << len(value) | value).setBitLength(len(self._value) + len(value)))
+
+    def __radd__(self, value):
+        value = self.prettyIn(value)
+        return self.clone(SizedInteger(value << len(self._value) | self._value).setBitLength(len(self._value) + len(value)))
+
+    def __mul__(self, value):
+        bitString = self._value
+        while value > 1:
+            bitString <<= len(self._value)
+            bitString |= self._value
+            value -= 1
+        return self.clone(bitString)
+
+    def __rmul__(self, value):
+        return self * value
+
+    def __lshift__(self, count):
+        return self.clone(SizedInteger(self._value << count).setBitLength(len(self._value) + count))
+
+    def __rshift__(self, count):
+        return self.clone(SizedInteger(self._value >> count).setBitLength(max(0, len(self._value) - count)))
+
+    def __int__(self):
+        return int(self._value)
+
+    def __float__(self):
+        return float(self._value)
+
+    def asNumbers(self):
+        """Get |ASN.1| value as a sequence of 8-bit integers.
+
+        If |ASN.1| object length is not a multiple of 8, result
+        will be left-padded with zeros.
+        """
+        return tuple(self.asOctets())
+
+    def asOctets(self):
+        """Get |ASN.1| value as a sequence of octets.
+
+        If |ASN.1| object length is not a multiple of 8, result
+        will be left-padded with zeros.
+        """
+        return integer.to_bytes(self._value, length=len(self))
+
+    def asInteger(self):
+        """Get |ASN.1| value as a single integer value.
+        """
+        return self._value
+
+    def asBinary(self):
+        """Get |ASN.1| value as a text string of bits.
+        """
+        binString = bin(self._value)[2:]
+        return '0' * (len(self._value) - len(binString)) + binString
+
+    @classmethod
+    def fromHexString(cls, value, internalFormat=False, prepend=None):
+        """Create a |ASN.1| object initialized from the hex string.
+
+        Parameters
+        ----------
+        value: :class:`str`
+            Text string like 'DEADBEEF'
+        """
+        try:
+            value = SizedInteger(value, 16).setBitLength(len(value) * 4)
+
+        except ValueError as exc:
+            raise error.PyAsn1Error('%s.fromHexString() error: %s' % (cls.__name__, exc))
+
+        if prepend is not None:
+            value = SizedInteger(
+                (SizedInteger(prepend) << len(value)) | value
+            ).setBitLength(len(prepend) + len(value))
+
+        if not internalFormat:
+            value = cls(value)
+
+        return value
+
+    @classmethod
+    def fromBinaryString(cls, value, internalFormat=False, prepend=None):
+        """Create a |ASN.1| object initialized from a string of '0' and '1'.
+
+        Parameters
+        ----------
+        value: :class:`str`
+            Text string like '1010111'
+        """
+        try:
+            value = SizedInteger(value or '0', 2).setBitLength(len(value))
+
+        except ValueError as exc:
+            raise error.PyAsn1Error('%s.fromBinaryString() error: %s' % (cls.__name__, exc))
+
+        if prepend is not None:
+            value = SizedInteger(
+                (SizedInteger(prepend) << len(value)) | value
+            ).setBitLength(len(prepend) + len(value))
+
+        if not internalFormat:
+            value = cls(value)
+
+        return value
+
+    @classmethod
+    def fromOctetString(cls, value, internalFormat=False, prepend=None, padding=0):
+        """Create a |ASN.1| object initialized from a string.
+
+        Parameters
+        ----------
+        value: :class:`bytes`
+            Text string like b'\\\\x01\\\\xff'
+        """
+        value = SizedInteger(int.from_bytes(bytes(value), 'big') >> padding).setBitLength(len(value) * 8 - padding)
+
+        if prepend is not None:
+            value = SizedInteger(
+                (SizedInteger(prepend) << len(value)) | value
+            ).setBitLength(len(prepend) + len(value))
+
+        if not internalFormat:
+            value = cls(value)
+
+        return value
+
+    def prettyIn(self, value):
+        if isinstance(value, SizedInteger):
+            return value
+        elif isinstance(value, str):
+            if not value:
+                return SizedInteger(0).setBitLength(0)
+
+            elif value[0] == '\'':  # "'1011'B" -- ASN.1 schema representation (deprecated)
+                if value[-2:] == '\'B':
+                    return self.fromBinaryString(value[1:-2], internalFormat=True)
+                elif value[-2:] == '\'H':
+                    return self.fromHexString(value[1:-2], internalFormat=True)
+                else:
+                    raise error.PyAsn1Error(
+                        'Bad BIT STRING value notation %s' % (value,)
+                    )
+
+            elif self.namedValues and not value.isdigit():  # named bits like 'Urgent, Active'
+                names = [x.strip() for x in value.split(',')]
+
+                try:
+
+                    bitPositions = [self.namedValues[name] for name in names]
+
+                except KeyError:
+                    raise error.PyAsn1Error('unknown bit name(s) in %r' % (names,))
+
+                rightmostPosition = max(bitPositions)
+
+                number = 0
+                for bitPosition in bitPositions:
+                    number |= 1 << (rightmostPosition - bitPosition)
+
+                return SizedInteger(number).setBitLength(rightmostPosition + 1)
+
+            elif value.startswith('0x'):
+                return self.fromHexString(value[2:], internalFormat=True)
+
+            elif value.startswith('0b'):
+                return self.fromBinaryString(value[2:], internalFormat=True)
+
+            else:  # assume plain binary string like '1011'
+                return self.fromBinaryString(value, internalFormat=True)
+
+        elif isinstance(value, (tuple, list)):
+            return self.fromBinaryString(''.join([b and '1' or '0' for b in value]), internalFormat=True)
+
+        elif isinstance(value, BitString):
+            return SizedInteger(value).setBitLength(len(value))
+
+        elif isinstance(value, int):
+            return SizedInteger(value)
+
+        else:
+            raise error.PyAsn1Error(
+                'Bad BitString initializer type \'%s\'' % (value,)
+            )
+
+
+class OctetString(base.SimpleAsn1Type):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type :class:`bytes`.
+    When used in Unicode context, |ASN.1| type
+    assumes "|encoding|" serialisation.
+
+    Keyword Args
+    ------------
+    value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
+        :class:`bytes`, alternatively :class:`str`
+        representing character string to be serialised into octets
+        (note `encoding` parameter) or |ASN.1| object.
+        If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    encoding: :py:class:`str`
+        Unicode codec ID to encode/decode
+        :class:`str` the payload when |ASN.1| object is used
+        in text string context.
+
+    binValue: :py:class:`str`
+        Binary string initializer to use instead of the *value*.
+        Example: '10110011'.
+
+    hexValue: :py:class:`str`
+        Hexadecimal string initializer to use instead of the *value*.
+        Example: 'DEADBEEF'.
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Icon(OctetString):
+            '''
+            ASN.1 specification:
+
+            Icon ::= OCTET STRING
+
+            icon1 Icon ::= '001100010011001000110011'B
+            icon2 Icon ::= '313233'H
+            '''
+        icon1 = Icon.fromBinaryString('001100010011001000110011')
+        icon2 = Icon.fromHexString('313233')
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = base.SimpleAsn1Type.getTypeId()
+
+    defaultBinValue = defaultHexValue = noValue
+    encoding = 'iso-8859-1'
+
+    def __init__(self, value=noValue, **kwargs):
+        if kwargs:
+            if value is noValue:
+                try:
+                    value = self.fromBinaryString(kwargs.pop('binValue'))
+
+                except KeyError:
+                    pass
+
+                try:
+                    value = self.fromHexString(kwargs.pop('hexValue'))
+
+                except KeyError:
+                    pass
+
+        if value is noValue:
+            if self.defaultBinValue is not noValue:
+                value = self.fromBinaryString(self.defaultBinValue)
+
+            elif self.defaultHexValue is not noValue:
+                value = self.fromHexString(self.defaultHexValue)
+
+        if 'encoding' not in kwargs:
+            kwargs['encoding'] = self.encoding
+
+        base.SimpleAsn1Type.__init__(self, value, **kwargs)
+
+    def prettyIn(self, value):
+        if isinstance(value, bytes):
+            return value
+
+        elif isinstance(value, str):
+            try:
+                return value.encode(self.encoding)
+
+            except UnicodeEncodeError as exc:
+                raise error.PyAsn1UnicodeEncodeError(
+                    "Can't encode string '%s' with '%s' "
+                    "codec" % (value, self.encoding), exc
+                )
+        elif isinstance(value, OctetString):  # a shortcut, bytes() would work the same way
+            return value.asOctets()
+
+        elif isinstance(value, base.SimpleAsn1Type):  # this mostly targets Integer objects
+            return self.prettyIn(str(value))
+
+        elif isinstance(value, (tuple, list)):
+            return self.prettyIn(bytes(value))
+
+        else:
+            return bytes(value)
+
+    def __str__(self):
+        try:
+            return self._value.decode(self.encoding)
+
+        except UnicodeDecodeError as exc:
+            raise error.PyAsn1UnicodeDecodeError(
+                "Can't decode string '%s' with '%s' codec at "
+                "'%s'" % (self._value, self.encoding,
+                            self.__class__.__name__), exc
+            )
+
+    def __bytes__(self):
+        return bytes(self._value)
+
+    def asOctets(self):
+        return bytes(self._value)
+
+    def asNumbers(self):
+        return tuple(self._value)
+
+    #
+    # Normally, `.prettyPrint()` is called from `__str__()`. Historically,
+    # OctetString.prettyPrint() used to return hexified payload
+    # representation in cases when non-printable content is present. At the
+    # same time `str()` used to produce either octet-stream (Py2) or
+    # text (Py3) representations.
+    #
+    # Therefore `OctetString.__str__()` -> `.prettyPrint()` call chain is
+    # reversed to preserve the original behaviour.
+    #
+    # Eventually we should deprecate `.prettyPrint()` / `.prettyOut()` harness
+    # and end up with just `__str__()` producing hexified representation while
+    # both text and octet-stream representation should only be requested via
+    # the `.asOctets()` method.
+    #
+    # Note: ASN.1 OCTET STRING is never mean to contain text!
+    #
+
+    def prettyOut(self, value):
+        return value
+
+    def prettyPrint(self, scope=0):
+        # first see if subclass has its own .prettyOut()
+        value = self.prettyOut(self._value)
+
+        if value is not self._value:
+            return value
+
+        numbers = self.asNumbers()
+
+        for x in numbers:
+            # hexify if needed
+            if x < 32 or x > 126:
+                return '0x' + ''.join(('%.2x' % x for x in numbers))
+        else:
+            # this prevents infinite recursion
+            return OctetString.__str__(self)
+
+    @staticmethod
+    def fromBinaryString(value):
+        """Create a |ASN.1| object initialized from a string of '0' and '1'.
+
+        Parameters
+        ----------
+        value: :class:`str`
+            Text string like '1010111'
+        """
+        bitNo = 8
+        byte = 0
+        r = []
+        for v in value:
+            if bitNo:
+                bitNo -= 1
+            else:
+                bitNo = 7
+                r.append(byte)
+                byte = 0
+            if v in ('0', '1'):
+                v = int(v)
+            else:
+                raise error.PyAsn1Error(
+                    'Non-binary OCTET STRING initializer %s' % (v,)
+                )
+            byte |= v << bitNo
+
+        r.append(byte)
+
+        return bytes(r)
+
+    @staticmethod
+    def fromHexString(value):
+        """Create a |ASN.1| object initialized from the hex string.
+
+        Parameters
+        ----------
+        value: :class:`str`
+            Text string like 'DEADBEEF'
+        """
+        r = []
+        p = []
+        for v in value:
+            if p:
+                r.append(int(p + v, 16))
+                p = None
+            else:
+                p = v
+        if p:
+            r.append(int(p + '0', 16))
+
+        return bytes(r)
+
+    # Immutable sequence object protocol
+
+    def __len__(self):
+        return len(self._value)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.clone(self._value[i])
+        else:
+            return self._value[i]
+
+    def __iter__(self):
+        return iter(self._value)
+
+    def __contains__(self, value):
+        return value in self._value
+
+    def __add__(self, value):
+        return self.clone(self._value + self.prettyIn(value))
+
+    def __radd__(self, value):
+        return self.clone(self.prettyIn(value) + self._value)
+
+    def __mul__(self, value):
+        return self.clone(self._value * value)
+
+    def __rmul__(self, value):
+        return self * value
+
+    def __int__(self):
+        return int(self._value)
+
+    def __float__(self):
+        return float(self._value)
+
+    def __reversed__(self):
+        return reversed(self._value)
+
+
+class Null(OctetString):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type Python :class:`str` objects
+    (always empty).
+
+    Keyword Args
+    ------------
+    value: :class:`str` or |ASN.1| object
+        Python empty :class:`str` literal or any object that evaluates to :obj:`False`
+        If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Ack(Null):
+            '''
+            ASN.1 specification:
+
+            Ack ::= NULL
+            '''
+        ack = Ack('')
+    """
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05)
+    )
+    subtypeSpec = OctetString.subtypeSpec + constraint.SingleValueConstraint(b'')
+
+    # Optimization for faster codec lookup
+    typeId = OctetString.getTypeId()
+
+    def prettyIn(self, value):
+        if value:
+            return value
+
+        return b''
+
+
+class ObjectIdentifier(base.SimpleAsn1Type):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type Python :class:`tuple` objects
+    (tuple of non-negative integers).
+
+    Keyword Args
+    ------------
+    value: :class:`tuple`, :class:`str` or |ASN.1| object
+        Python sequence of :class:`int` or :class:`str` literal or |ASN.1| object.
+        If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class ID(ObjectIdentifier):
+            '''
+            ASN.1 specification:
+
+            ID ::= OBJECT IDENTIFIER
+
+            id-edims ID ::= { joint-iso-itu-t mhs-motif(6) edims(7) }
+            id-bp ID ::= { id-edims 11 }
+            '''
+        id_edims = ID('2.6.7')
+        id_bp = id_edims + (11,)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = base.SimpleAsn1Type.getTypeId()
+
+    def __add__(self, other):
+        return self.clone(self._value + other)
+
+    def __radd__(self, other):
+        return self.clone(other + self._value)
+
+    def asTuple(self):
+        return self._value
+
+    # Sequence object protocol
+
+    def __len__(self):
+        return len(self._value)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.clone(self._value[i])
+        else:
+            return self._value[i]
+
+    def __iter__(self):
+        return iter(self._value)
+
+    def __contains__(self, value):
+        return value in self._value
+
+    def index(self, suboid):
+        return self._value.index(suboid)
+
+    def isPrefixOf(self, other):
+        """Indicate if this |ASN.1| object is a prefix of other |ASN.1| object.
+
+        Parameters
+        ----------
+        other: |ASN.1| object
+            |ASN.1| object
+
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object
+            or :obj:`False` otherwise.
+        """
+        l = len(self)
+        if l <= len(other):
+            if self._value[:l] == other[:l]:
+                return True
+        return False
+
+    def prettyIn(self, value):
+        if isinstance(value, ObjectIdentifier):
+            return tuple(value)
+        elif isinstance(value, str):
+            if '-' in value:
+                raise error.PyAsn1Error(
+                    # sys.exc_info in case prettyIn was called while handling an exception
+                    'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1])
+                )
+            try:
+                return tuple([int(subOid) for subOid in value.split('.') if subOid])
+            except ValueError as exc:
+                raise error.PyAsn1Error(
+                    'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, exc)
+                )
+
+        try:
+            tupleOfInts = tuple([int(subOid) for subOid in value if subOid >= 0])
+
+        except (ValueError, TypeError) as exc:
+            raise error.PyAsn1Error(
+                'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, exc)
+            )
+
+        if len(tupleOfInts) == len(value):
+            return tupleOfInts
+
+        raise error.PyAsn1Error('Malformed Object ID %s at %s' % (value, self.__class__.__name__))
+
+    def prettyOut(self, value):
+        return '.'.join([str(x) for x in value])
+
+
+class RelativeOID(base.SimpleAsn1Type):
+    """Create |ASN.1| schema or value object.
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type Python :class:`tuple` objects
+    (tuple of non-negative integers).
+    Keyword Args
+    ------------
+    value: :class:`tuple`, :class:`str` or |ASN.1| object
+        Python sequence of :class:`int` or :class:`str` literal or |ASN.1| object.
+        If `value` is not given, schema object will be created.
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+    Examples
+    --------
+    .. code-block:: python
+        class RelOID(RelativeOID):
+            '''
+            ASN.1 specification:
+            id-pad-null RELATIVE-OID ::= { 0 }
+            id-pad-once RELATIVE-OID ::= { 5 6 }
+            id-pad-twice RELATIVE-OID ::= { 5 6 7 }
+            '''
+        id_pad_null = RelOID('0')
+        id_pad_once = RelOID('5.6')
+        id_pad_twice = id_pad_once + (7,)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0d)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = base.SimpleAsn1Type.getTypeId()
+
+    def __add__(self, other):
+        return self.clone(self._value + other)
+
+    def __radd__(self, other):
+        return self.clone(other + self._value)
+
+    def asTuple(self):
+        return self._value
+
+    # Sequence object protocol
+
+    def __len__(self):
+        return len(self._value)
+
+    def __getitem__(self, i):
+        if i.__class__ is slice:
+            return self.clone(self._value[i])
+        else:
+            return self._value[i]
+
+    def __iter__(self):
+        return iter(self._value)
+
+    def __contains__(self, value):
+        return value in self._value
+
+    def index(self, suboid):
+        return self._value.index(suboid)
+
+    def isPrefixOf(self, other):
+        """Indicate if this |ASN.1| object is a prefix of other |ASN.1| object.
+        Parameters
+        ----------
+        other: |ASN.1| object
+            |ASN.1| object
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object
+            or :obj:`False` otherwise.
+        """
+        l = len(self)
+        if l <= len(other):
+            if self._value[:l] == other[:l]:
+                return True
+        return False
+
+    def prettyIn(self, value):
+        if isinstance(value, RelativeOID):
+            return tuple(value)
+        elif isinstance(value, str):
+            if '-' in value:
+                raise error.PyAsn1Error(
+                    # sys.exc_info in case prettyIn was called while handling an exception
+                    'Malformed RELATIVE-OID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1])
+                )
+            try:
+                return tuple([int(subOid) for subOid in value.split('.') if subOid])
+            except ValueError as exc:
+                raise error.PyAsn1Error(
+                    'Malformed RELATIVE-OID %s at %s: %s' % (value, self.__class__.__name__, exc)
+                )
+
+        try:
+            tupleOfInts = tuple([int(subOid) for subOid in value if subOid >= 0])
+
+        except (ValueError, TypeError) as exc:
+            raise error.PyAsn1Error(
+                'Malformed RELATIVE-OID %s at %s: %s' % (value, self.__class__.__name__, exc)
+            )
+
+        if len(tupleOfInts) == len(value):
+            return tupleOfInts
+
+        raise error.PyAsn1Error('Malformed RELATIVE-OID %s at %s' % (value, self.__class__.__name__))
+
+    def prettyOut(self, value):
+        return '.'.join([str(x) for x in value])
+
+
+class Real(base.SimpleAsn1Type):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type Python :class:`float` objects.
+    Additionally, |ASN.1| objects behave like a :class:`tuple` in which case its
+    elements are mantissa, base and exponent.
+
+    Keyword Args
+    ------------
+    value: :class:`tuple`, :class:`float` or |ASN.1| object
+        Python sequence of :class:`int` (representing mantissa, base and
+        exponent) or :class:`float` instance or |ASN.1| object.
+        If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Pi(Real):
+            '''
+            ASN.1 specification:
+
+            Pi ::= REAL
+
+            pi Pi ::= { mantissa 314159, base 10, exponent -5 }
+
+            '''
+        pi = Pi((314159, 10, -5))
+    """
+    binEncBase = None  # binEncBase = 16 is recommended for large numbers
+
+    try:
+        _plusInf = float('inf')
+        _minusInf = float('-inf')
+        _inf = _plusInf, _minusInf
+
+    except ValueError:
+        # Infinity support is platform and Python dependent
+        _plusInf = _minusInf = None
+        _inf = ()
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = base.SimpleAsn1Type.getTypeId()
+
+    @staticmethod
+    def __normalizeBase10(value):
+        m, b, e = value
+        while m and m % 10 == 0:
+            m /= 10
+            e += 1
+        return m, b, e
+
+    def prettyIn(self, value):
+        if isinstance(value, tuple) and len(value) == 3:
+            if (not isinstance(value[0], (int, float)) or
+                    not isinstance(value[1], int) or
+                    not isinstance(value[2], int)):
+                raise error.PyAsn1Error('Lame Real value syntax: %s' % (value,))
+            if (isinstance(value[0], float) and
+                    self._inf and value[0] in self._inf):
+                return value[0]
+            if value[1] not in (2, 10):
+                raise error.PyAsn1Error(
+                    'Prohibited base for Real value: %s' % (value[1],)
+                )
+            if value[1] == 10:
+                value = self.__normalizeBase10(value)
+            return value
+        elif isinstance(value, int):
+            return self.__normalizeBase10((value, 10, 0))
+        elif isinstance(value, float) or isinstance(value, str):
+            if isinstance(value, str):
+                try:
+                    value = float(value)
+                except ValueError:
+                    raise error.PyAsn1Error(
+                        'Bad real value syntax: %s' % (value,)
+                    )
+            if self._inf and value in self._inf:
+                return value
+            else:
+                e = 0
+                while int(value) != value:
+                    value *= 10
+                    e -= 1
+                return self.__normalizeBase10((int(value), 10, e))
+        elif isinstance(value, Real):
+            return tuple(value)
+        raise error.PyAsn1Error(
+            'Bad real value syntax: %s' % (value,)
+        )
+
+    def prettyPrint(self, scope=0):
+        try:
+            return self.prettyOut(float(self))
+
+        except OverflowError:
+            return '<overflow>'
+
+    @property
+    def isPlusInf(self):
+        """Indicate PLUS-INFINITY object value
+
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`True` if calling object represents plus infinity
+            or :obj:`False` otherwise.
+
+        """
+        return self._value == self._plusInf
+
+    @property
+    def isMinusInf(self):
+        """Indicate MINUS-INFINITY object value
+
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`True` if calling object represents minus infinity
+            or :obj:`False` otherwise.
+        """
+        return self._value == self._minusInf
+
+    @property
+    def isInf(self):
+        return self._value in self._inf
+
+    def __add__(self, value):
+        return self.clone(float(self) + value)
+
+    def __radd__(self, value):
+        return self + value
+
+    def __mul__(self, value):
+        return self.clone(float(self) * value)
+
+    def __rmul__(self, value):
+        return self * value
+
+    def __sub__(self, value):
+        return self.clone(float(self) - value)
+
+    def __rsub__(self, value):
+        return self.clone(value - float(self))
+
+    def __mod__(self, value):
+        return self.clone(float(self) % value)
+
+    def __rmod__(self, value):
+        return self.clone(value % float(self))
+
+    def __pow__(self, value, modulo=None):
+        return self.clone(pow(float(self), value, modulo))
+
+    def __rpow__(self, value):
+        return self.clone(pow(value, float(self)))
+
+    def __truediv__(self, value):
+        return self.clone(float(self) / value)
+
+    def __rtruediv__(self, value):
+        return self.clone(value / float(self))
+
+    def __divmod__(self, value):
+        return self.clone(float(self) // value)
+
+    def __rdivmod__(self, value):
+        return self.clone(value // float(self))
+
+    def __int__(self):
+        return int(float(self))
+
+    def __float__(self):
+        if self._value in self._inf:
+            return self._value
+        else:
+            return float(
+                self._value[0] * pow(self._value[1], self._value[2])
+            )
+
+    def __abs__(self):
+        return self.clone(abs(float(self)))
+
+    def __pos__(self):
+        return self.clone(+float(self))
+
+    def __neg__(self):
+        return self.clone(-float(self))
+
+    def __round__(self, n=0):
+        r = round(float(self), n)
+        if n:
+            return self.clone(r)
+        else:
+            return r
+
+    def __floor__(self):
+        return self.clone(math.floor(float(self)))
+
+    def __ceil__(self):
+        return self.clone(math.ceil(float(self)))
+
+    def __trunc__(self):
+        return self.clone(math.trunc(float(self)))
+
+    def __lt__(self, value):
+        return float(self) < value
+
+    def __le__(self, value):
+        return float(self) <= value
+
+    def __eq__(self, value):
+        return float(self) == value
+
+    def __ne__(self, value):
+        return float(self) != value
+
+    def __gt__(self, value):
+        return float(self) > value
+
+    def __ge__(self, value):
+        return float(self) >= value
+
+    def __bool__(self):
+        return bool(float(self))
+
+    __hash__ = base.SimpleAsn1Type.__hash__
+
+    def __getitem__(self, idx):
+        if self._value in self._inf:
+            raise error.PyAsn1Error('Invalid infinite value operation')
+        else:
+            return self._value[idx]
+
+    # compatibility stubs
+
+    def isPlusInfinity(self):
+        return self.isPlusInf
+
+    def isMinusInfinity(self):
+        return self.isMinusInf
+
+    def isInfinity(self):
+        return self.isInf
+
+
+class Enumerated(Integer):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its
+    objects are immutable and duck-type Python :class:`int` objects.
+
+    Keyword Args
+    ------------
+    value: :class:`int`, :class:`str` or |ASN.1| object
+        Python :class:`int` or :class:`str` literal or |ASN.1| object.
+        If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+        Object representing non-default symbolic aliases for numbers
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class RadioButton(Enumerated):
+            '''
+            ASN.1 specification:
+
+            RadioButton ::= ENUMERATED { button1(0), button2(1),
+                                         button3(2) }
+
+            selected-by-default RadioButton ::= button1
+            '''
+            namedValues = NamedValues(
+                ('button1', 0), ('button2', 1),
+                ('button3', 2)
+            )
+
+        selected_by_default = RadioButton('button1')
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Optimization for faster codec lookup
+    typeId = Integer.getTypeId()
+
+    #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+    #: representing symbolic aliases for numbers
+    namedValues = namedval.NamedValues()
+
+
+# "Structured" ASN.1 types
+
+class SequenceOfAndSetOfBase(base.ConstructedAsn1Type):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`,
+    its objects are mutable and duck-type Python :class:`list` objects.
+
+    Keyword Args
+    ------------
+    componentType : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+        A pyasn1 object representing ASN.1 type allowed within |ASN.1| type
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type can only occur on explicit
+        `.isInconsistent` call.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class LotteryDraw(SequenceOf):  #  SetOf is similar
+            '''
+            ASN.1 specification:
+
+            LotteryDraw ::= SEQUENCE OF INTEGER
+            '''
+            componentType = Integer()
+
+        lotteryDraw = LotteryDraw()
+        lotteryDraw.extend([123, 456, 789])
+    """
+    def __init__(self, *args, **kwargs):
+        # support positional params for backward compatibility
+        if args:
+            for key, value in zip(('componentType', 'tagSet',
+                                   'subtypeSpec'), args):
+                if key in kwargs:
+                    raise error.PyAsn1Error('Conflicting positional and keyword params!')
+                kwargs['componentType'] = value
+
+        self._componentValues = noValue
+
+        base.ConstructedAsn1Type.__init__(self, **kwargs)
+
+    # Python list protocol
+
+    def __getitem__(self, idx):
+        try:
+            return self.getComponentByPosition(idx)
+
+        except error.PyAsn1Error as exc:
+            raise IndexError(exc)
+
+    def __setitem__(self, idx, value):
+        try:
+            self.setComponentByPosition(idx, value)
+
+        except error.PyAsn1Error as exc:
+            raise IndexError(exc)
+
+    def append(self, value):
+        if self._componentValues is noValue:
+            pos = 0
+
+        else:
+            pos = len(self._componentValues)
+
+        self[pos] = value
+
+    def count(self, value):
+        return list(self._componentValues.values()).count(value)
+
+    def extend(self, values):
+        for value in values:
+            self.append(value)
+
+        if self._componentValues is noValue:
+            self._componentValues = {}
+
+    def index(self, value, start=0, stop=None):
+        if stop is None:
+            stop = len(self)
+
+        indices, values = zip(*self._componentValues.items())
+
+        # TODO: remove when Py2.5 support is gone
+        values = list(values)
+
+        try:
+            return indices[values.index(value, start, stop)]
+
+        except error.PyAsn1Error as exc:
+            raise ValueError(exc)
+
+    def reverse(self):
+        self._componentValues.reverse()
+
+    def sort(self, key=None, reverse=False):
+        self._componentValues = dict(
+            enumerate(sorted(self._componentValues.values(),
+                             key=key, reverse=reverse)))
+
+    def __len__(self):
+        if self._componentValues is noValue or not self._componentValues:
+            return 0
+
+        return max(self._componentValues) + 1
+
+    def __iter__(self):
+        for idx in range(0, len(self)):
+            yield self.getComponentByPosition(idx)
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        for idx, componentValue in self._componentValues.items():
+            if componentValue is not noValue:
+                if isinstance(componentValue, base.ConstructedAsn1Type):
+                    myClone.setComponentByPosition(
+                        idx, componentValue.clone(cloneValueFlag=cloneValueFlag)
+                    )
+                else:
+                    myClone.setComponentByPosition(idx, componentValue.clone())
+
+    def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+        """Return |ASN.1| type component value by position.
+
+        Equivalent to Python sequence subscription operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        idx : :class:`int`
+            Component index (zero-based). Must either refer to an existing
+            component or to N+1 component (if *componentType* is set). In the latter
+            case a new component type gets instantiated and appended to the |ASN.1|
+            sequence.
+
+        Keyword Args
+        ------------
+        default: :class:`object`
+            If set and requested component is a schema object, return the `default`
+            object instead of the requested component.
+
+        instantiate: :class:`bool`
+            If :obj:`True` (default), inner component will be automatically instantiated.
+            If :obj:`False` either existing component or the :class:`NoValue` object will be
+            returned.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            Instantiate |ASN.1| component type or return existing component value
+
+        Examples
+        --------
+
+        .. code-block:: python
+
+            # can also be SetOf
+            class MySequenceOf(SequenceOf):
+                componentType = OctetString()
+
+            s = MySequenceOf()
+
+            # returns component #0 with `.isValue` property False
+            s.getComponentByPosition(0)
+
+            # returns None
+            s.getComponentByPosition(0, default=None)
+
+            s.clear()
+
+            # returns noValue
+            s.getComponentByPosition(0, instantiate=False)
+
+            # sets component #0 to OctetString() ASN.1 schema
+            # object and returns it
+            s.getComponentByPosition(0, instantiate=True)
+
+            # sets component #0 to ASN.1 value object
+            s.setComponentByPosition(0, 'ABCD')
+
+            # returns OctetString('ABCD') value object
+            s.getComponentByPosition(0, instantiate=False)
+
+            s.clear()
+
+            # returns noValue
+            s.getComponentByPosition(0, instantiate=False)
+        """
+        if isinstance(idx, slice):
+            indices = tuple(range(len(self)))
+            return [self.getComponentByPosition(subidx, default, instantiate)
+                    for subidx in indices[idx]]
+
+        if idx < 0:
+            idx = len(self) + idx
+            if idx < 0:
+                raise error.PyAsn1Error(
+                    'SequenceOf/SetOf index is out of range')
+
+        try:
+            componentValue = self._componentValues[idx]
+
+        except (KeyError, error.PyAsn1Error):
+            if not instantiate:
+                return default
+
+            self.setComponentByPosition(idx)
+
+            componentValue = self._componentValues[idx]
+
+        if default is noValue or componentValue.isValue:
+            return componentValue
+        else:
+            return default
+
+    def setComponentByPosition(self, idx, value=noValue,
+                               verifyConstraints=True,
+                               matchTags=True,
+                               matchConstraints=True):
+        """Assign |ASN.1| type component by position.
+
+        Equivalent to Python sequence item assignment operation (e.g. `[]`)
+        or list.append() (when idx == len(self)).
+
+        Parameters
+        ----------
+        idx: :class:`int`
+            Component index (zero-based). Must either refer to existing
+            component or to N+1 component. In the latter case a new component
+            type gets instantiated (if *componentType* is set, or given ASN.1
+            object is taken otherwise) and appended to the |ASN.1| sequence.
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component.
+            If `value` is not given, schema object will be set as a component.
+
+        verifyConstraints: :class:`bool`
+             If :obj:`False`, skip constraints validation
+
+        matchTags: :class:`bool`
+             If :obj:`False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+             If :obj:`False`, skip component constraints matching
+
+        Returns
+        -------
+        self
+
+        Raises
+        ------
+        ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+            On constraint violation or bad initializer
+        IndexError
+            When idx > len(self)
+        """
+        if isinstance(idx, slice):
+            indices = tuple(range(len(self)))
+            startIdx = indices and indices[idx][0] or 0
+            for subIdx, subValue in enumerate(value):
+                self.setComponentByPosition(
+                    startIdx + subIdx, subValue, verifyConstraints,
+                    matchTags, matchConstraints)
+            return self
+
+        if idx < 0:
+            idx = len(self) + idx
+            if idx < 0:
+                raise error.PyAsn1Error(
+                    'SequenceOf/SetOf index is out of range')
+
+        componentType = self.componentType
+
+        if self._componentValues is noValue:
+            componentValues = {}
+
+        else:
+            componentValues = self._componentValues
+
+        currentValue = componentValues.get(idx, noValue)
+
+        if value is noValue:
+            if componentType is not None:
+                value = componentType.clone()
+
+            elif currentValue is noValue:
+                raise error.PyAsn1Error('Component type not defined')
+
+        elif not isinstance(value, base.Asn1Item):
+            if (componentType is not None and
+                    isinstance(componentType, base.SimpleAsn1Type)):
+                value = componentType.clone(value=value)
+
+            elif (currentValue is not noValue and
+                    isinstance(currentValue, base.SimpleAsn1Type)):
+                value = currentValue.clone(value=value)
+
+            else:
+                raise error.PyAsn1Error(
+                    'Non-ASN.1 value %r and undefined component'
+                    ' type at %r' % (value, self))
+
+        elif componentType is not None and (matchTags or matchConstraints):
+            subtypeChecker = (
+                    self.strictConstraints and
+                    componentType.isSameTypeWith or
+                    componentType.isSuperTypeOf)
+
+            if not subtypeChecker(value, verifyConstraints and matchTags,
+                                  verifyConstraints and matchConstraints):
+                # TODO: we should wrap componentType with UnnamedType to carry
+                # additional properties associated with componentType
+                if componentType.typeId != Any.typeId:
+                    raise error.PyAsn1Error(
+                        'Component value is tag-incompatible: %r vs '
+                        '%r' % (value, componentType))
+
+        componentValues[idx] = value
+
+        self._componentValues = componentValues
+
+        return self
+
+    @property
+    def componentTagMap(self):
+        if self.componentType is not None:
+            return self.componentType.tagMap
+
+    @property
+    def components(self):
+        return [self._componentValues[idx]
+                for idx in sorted(self._componentValues)]
+
+    def clear(self):
+        """Remove all components and become an empty |ASN.1| value object.
+
+        Has the same effect on |ASN.1| object as it does on :class:`list`
+        built-in.
+        """
+        self._componentValues = {}
+        return self
+
+    def reset(self):
+        """Remove all components and become a |ASN.1| schema object.
+
+        See :meth:`isValue` property for more information on the
+        distinction between value and schema objects.
+        """
+        self._componentValues = noValue
+        return self
+
+    def prettyPrint(self, scope=0):
+        scope += 1
+        representation = self.__class__.__name__ + ':\n'
+
+        if not self.isValue:
+            return representation
+
+        for idx, componentValue in enumerate(self):
+            representation += ' ' * scope
+            if (componentValue is noValue and
+                    self.componentType is not None):
+                representation += '<empty>'
+            else:
+                representation += componentValue.prettyPrint(scope)
+
+        return representation
+
+    def prettyPrintType(self, scope=0):
+        scope += 1
+        representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__)
+        if self.componentType is not None:
+            representation += ' ' * scope
+            representation += self.componentType.prettyPrintType(scope)
+        return representation + '\n' + ' ' * (scope - 1) + '}'
+
+
+    @property
+    def isValue(self):
+        """Indicate that |ASN.1| object represents ASN.1 value.
+
+        If *isValue* is :obj:`False` then this object represents just ASN.1 schema.
+
+        If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features,
+        this object can also be used like a Python built-in object
+        (e.g. :class:`int`, :class:`str`, :class:`dict` etc.).
+
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`False` if object represents just ASN.1 schema.
+            :obj:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+        Note
+        ----
+        There is an important distinction between PyASN1 schema and value objects.
+        The PyASN1 schema objects can only participate in ASN.1 schema-related
+        operations (e.g. defining or testing the structure of the data). Most
+        obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+        encoding/decoding serialised ASN.1 contents.
+
+        The PyASN1 value objects can **additionally** participate in many operations
+        involving regular Python objects (e.g. arithmetic, comprehension etc).
+        """
+        if self._componentValues is noValue:
+            return False
+
+        if len(self._componentValues) != len(self):
+            return False
+
+        for componentValue in self._componentValues.values():
+            if componentValue is noValue or not componentValue.isValue:
+                return False
+
+        return True
+
+    @property
+    def isInconsistent(self):
+        """Run necessary checks to ensure |ASN.1| object consistency.
+
+        Default action is to verify |ASN.1| object against constraints imposed
+        by `subtypeSpec`.
+
+        Raises
+        ------
+        :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found
+        """
+        if self.componentType is noValue or not self.subtypeSpec:
+            return False
+
+        if self._componentValues is noValue:
+            return True
+
+        mapping = {}
+
+        for idx, value in self._componentValues.items():
+            # Absent fields are not in the mapping
+            if value is noValue:
+                continue
+
+            mapping[idx] = value
+
+        try:
+            # Represent SequenceOf/SetOf as a bare dict to constraints chain
+            self.subtypeSpec(mapping)
+
+        except error.PyAsn1Error as exc:
+            return exc
+
+        return False
+
+class SequenceOf(SequenceOfAndSetOfBase):
+    __doc__ = SequenceOfAndSetOfBase.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+    )
+
+    #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+    #: object representing ASN.1 type allowed within |ASN.1| type
+    componentType = None
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = SequenceOfAndSetOfBase.getTypeId()
+
+
+class SetOf(SequenceOfAndSetOfBase):
+    __doc__ = SequenceOfAndSetOfBase.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+    )
+
+    #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+    #: object representing ASN.1 type allowed within |ASN.1| type
+    componentType = None
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = SequenceOfAndSetOfBase.getTypeId()
+
+
+class SequenceAndSetBase(base.ConstructedAsn1Type):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`,
+    its objects are mutable and duck-type Python :class:`dict` objects.
+
+    Keyword Args
+    ------------
+    componentType: :py:class:`~pyasn1.type.namedtype.NamedType`
+        Object holding named ASN.1 types allowed within this collection
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s).  Constraints
+        verification for |ASN.1| type can only occur on explicit
+        `.isInconsistent` call.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class Description(Sequence):  #  Set is similar
+            '''
+            ASN.1 specification:
+
+            Description ::= SEQUENCE {
+                surname    IA5String,
+                first-name IA5String OPTIONAL,
+                age        INTEGER DEFAULT 40
+            }
+            '''
+            componentType = NamedTypes(
+                NamedType('surname', IA5String()),
+                OptionalNamedType('first-name', IA5String()),
+                DefaultedNamedType('age', Integer(40))
+            )
+
+        descr = Description()
+        descr['surname'] = 'Smith'
+        descr['first-name'] = 'John'
+    """
+    #: Default :py:class:`~pyasn1.type.namedtype.NamedTypes`
+    #: object representing named ASN.1 types allowed within |ASN.1| type
+    componentType = namedtype.NamedTypes()
+
+
+    class DynamicNames(object):
+        """Fields names/positions mapping for component-less objects"""
+        def __init__(self):
+            self._keyToIdxMap = {}
+            self._idxToKeyMap = {}
+
+        def __len__(self):
+            return len(self._keyToIdxMap)
+
+        def __contains__(self, item):
+            return item in self._keyToIdxMap or item in self._idxToKeyMap
+
+        def __iter__(self):
+            return (self._idxToKeyMap[idx] for idx in range(len(self._idxToKeyMap)))
+
+        def __getitem__(self, item):
+            try:
+                return self._keyToIdxMap[item]
+
+            except KeyError:
+                return self._idxToKeyMap[item]
+
+        def getNameByPosition(self, idx):
+            try:
+                return self._idxToKeyMap[idx]
+
+            except KeyError:
+                raise error.PyAsn1Error('Type position out of range')
+
+        def getPositionByName(self, name):
+            try:
+                return self._keyToIdxMap[name]
+
+            except KeyError:
+                raise error.PyAsn1Error('Name %s not found' % (name,))
+
+        def addField(self, idx):
+            self._keyToIdxMap['field-%d' % idx] = idx
+            self._idxToKeyMap[idx] = 'field-%d' % idx
+
+
+    def __init__(self, **kwargs):
+        base.ConstructedAsn1Type.__init__(self, **kwargs)
+        self._componentTypeLen = len(self.componentType)
+        if self._componentTypeLen:
+            self._componentValues = []
+        else:
+            self._componentValues = noValue
+        self._dynamicNames = self._componentTypeLen or self.DynamicNames()
+
+    def __getitem__(self, idx):
+        if isinstance(idx, str):
+            try:
+                return self.getComponentByName(idx)
+
+            except error.PyAsn1Error as exc:
+                # duck-typing dict
+                raise KeyError(exc)
+
+        else:
+            try:
+                return self.getComponentByPosition(idx)
+
+            except error.PyAsn1Error as exc:
+                # duck-typing list
+                raise IndexError(exc)
+
+    def __setitem__(self, idx, value):
+        if isinstance(idx, str):
+            try:
+                self.setComponentByName(idx, value)
+
+            except error.PyAsn1Error as exc:
+                # duck-typing dict
+                raise KeyError(exc)
+
+        else:
+            try:
+                self.setComponentByPosition(idx, value)
+
+            except error.PyAsn1Error as exc:
+                # duck-typing list
+                raise IndexError(exc)
+
+    def __contains__(self, key):
+        if self._componentTypeLen:
+            return key in self.componentType
+        else:
+            return key in self._dynamicNames
+
+    def __len__(self):
+        return len(self._componentValues)
+
+    def __iter__(self):
+        return iter(self.componentType or self._dynamicNames)
+
+    # Python dict protocol
+
+    def values(self):
+        for idx in range(self._componentTypeLen or len(self._dynamicNames)):
+            yield self[idx]
+
+    def keys(self):
+        return iter(self)
+
+    def items(self):
+        for idx in range(self._componentTypeLen or len(self._dynamicNames)):
+            if self._componentTypeLen:
+                yield self.componentType[idx].name, self[idx]
+            else:
+                yield self._dynamicNames[idx], self[idx]
+
+    def update(self, *iterValue, **mappingValue):
+        for k, v in iterValue:
+            self[k] = v
+        for k in mappingValue:
+            self[k] = mappingValue[k]
+
+    def clear(self):
+        """Remove all components and become an empty |ASN.1| value object.
+
+        Has the same effect on |ASN.1| object as it does on :class:`dict`
+        built-in.
+        """
+        self._componentValues = []
+        self._dynamicNames = self.DynamicNames()
+        return self
+
+    def reset(self):
+        """Remove all components and become a |ASN.1| schema object.
+
+        See :meth:`isValue` property for more information on the
+        distinction between value and schema objects.
+        """
+        self._componentValues = noValue
+        self._dynamicNames = self.DynamicNames()
+        return self
+
+    @property
+    def components(self):
+        return self._componentValues
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        if self._componentValues is noValue:
+            return
+
+        for idx, componentValue in enumerate(self._componentValues):
+            if componentValue is not noValue:
+                if isinstance(componentValue, base.ConstructedAsn1Type):
+                    myClone.setComponentByPosition(
+                        idx, componentValue.clone(cloneValueFlag=cloneValueFlag)
+                    )
+                else:
+                    myClone.setComponentByPosition(idx, componentValue.clone())
+
+    def getComponentByName(self, name, default=noValue, instantiate=True):
+        """Returns |ASN.1| type component by name.
+
+        Equivalent to Python :class:`dict` subscription operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        name: :class:`str`
+            |ASN.1| type component name
+
+        Keyword Args
+        ------------
+        default: :class:`object`
+            If set and requested component is a schema object, return the `default`
+            object instead of the requested component.
+
+        instantiate: :class:`bool`
+            If :obj:`True` (default), inner component will be automatically
+            instantiated.
+            If :obj:`False` either existing component or the :class:`NoValue`
+            object will be returned.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            Instantiate |ASN.1| component type or return existing
+            component value
+        """
+        if self._componentTypeLen:
+            idx = self.componentType.getPositionByName(name)
+        else:
+            try:
+                idx = self._dynamicNames.getPositionByName(name)
+
+            except KeyError:
+                raise error.PyAsn1Error('Name %s not found' % (name,))
+
+        return self.getComponentByPosition(idx, default=default, instantiate=instantiate)
+
+    def setComponentByName(self, name, value=noValue,
+                           verifyConstraints=True,
+                           matchTags=True,
+                           matchConstraints=True):
+        """Assign |ASN.1| type component by name.
+
+        Equivalent to Python :class:`dict` item assignment operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        name: :class:`str`
+            |ASN.1| type component name
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component.
+            If `value` is not given, schema object will be set as a component.
+
+        verifyConstraints: :class:`bool`
+             If :obj:`False`, skip constraints validation
+
+        matchTags: :class:`bool`
+             If :obj:`False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+             If :obj:`False`, skip component constraints matching
+
+        Returns
+        -------
+        self
+        """
+        if self._componentTypeLen:
+            idx = self.componentType.getPositionByName(name)
+        else:
+            try:
+                idx = self._dynamicNames.getPositionByName(name)
+
+            except KeyError:
+                raise error.PyAsn1Error('Name %s not found' % (name,))
+
+        return self.setComponentByPosition(
+            idx, value, verifyConstraints, matchTags, matchConstraints
+        )
+
+    def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+        """Returns |ASN.1| type component by index.
+
+        Equivalent to Python sequence subscription operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        idx: :class:`int`
+            Component index (zero-based). Must either refer to an existing
+            component or (if *componentType* is set) new ASN.1 schema object gets
+            instantiated.
+
+        Keyword Args
+        ------------
+        default: :class:`object`
+            If set and requested component is a schema object, return the `default`
+            object instead of the requested component.
+
+        instantiate: :class:`bool`
+            If :obj:`True` (default), inner component will be automatically
+            instantiated.
+            If :obj:`False` either existing component or the :class:`NoValue`
+            object will be returned.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            a PyASN1 object
+
+        Examples
+        --------
+
+        .. code-block:: python
+
+            # can also be Set
+            class MySequence(Sequence):
+                componentType = NamedTypes(
+                    NamedType('id', OctetString())
+                )
+
+            s = MySequence()
+
+            # returns component #0 with `.isValue` property False
+            s.getComponentByPosition(0)
+
+            # returns None
+            s.getComponentByPosition(0, default=None)
+
+            s.clear()
+
+            # returns noValue
+            s.getComponentByPosition(0, instantiate=False)
+
+            # sets component #0 to OctetString() ASN.1 schema
+            # object and returns it
+            s.getComponentByPosition(0, instantiate=True)
+
+            # sets component #0 to ASN.1 value object
+            s.setComponentByPosition(0, 'ABCD')
+
+            # returns OctetString('ABCD') value object
+            s.getComponentByPosition(0, instantiate=False)
+
+            s.clear()
+
+            # returns noValue
+            s.getComponentByPosition(0, instantiate=False)
+        """
+        try:
+            if self._componentValues is noValue:
+                componentValue = noValue
+
+            else:
+                componentValue = self._componentValues[idx]
+
+        except IndexError:
+            componentValue = noValue
+
+        if not instantiate:
+            if componentValue is noValue or not componentValue.isValue:
+                return default
+            else:
+                return componentValue
+
+        if componentValue is noValue:
+            self.setComponentByPosition(idx)
+
+        componentValue = self._componentValues[idx]
+
+        if default is noValue or componentValue.isValue:
+            return componentValue
+        else:
+            return default
+
+    def setComponentByPosition(self, idx, value=noValue,
+                               verifyConstraints=True,
+                               matchTags=True,
+                               matchConstraints=True):
+        """Assign |ASN.1| type component by position.
+
+        Equivalent to Python sequence item assignment operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        idx : :class:`int`
+            Component index (zero-based). Must either refer to existing
+            component (if *componentType* is set) or to N+1 component
+            otherwise. In the latter case a new component of given ASN.1
+            type gets instantiated and appended to |ASN.1| sequence.
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component.
+            If `value` is not given, schema object will be set as a component.
+
+        verifyConstraints : :class:`bool`
+             If :obj:`False`, skip constraints validation
+
+        matchTags: :class:`bool`
+             If :obj:`False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+             If :obj:`False`, skip component constraints matching
+
+        Returns
+        -------
+        self
+        """
+        componentType = self.componentType
+        componentTypeLen = self._componentTypeLen
+
+        if self._componentValues is noValue:
+            componentValues = []
+
+        else:
+            componentValues = self._componentValues
+
+        try:
+            currentValue = componentValues[idx]
+
+        except IndexError:
+            currentValue = noValue
+            if componentTypeLen:
+                if componentTypeLen < idx:
+                    raise error.PyAsn1Error('component index out of range')
+
+                componentValues = [noValue] * componentTypeLen
+
+        if value is noValue:
+            if componentTypeLen:
+                value = componentType.getTypeByPosition(idx)
+                if isinstance(value, base.ConstructedAsn1Type):
+                    value = value.clone(cloneValueFlag=componentType[idx].isDefaulted)
+
+            elif currentValue is noValue:
+                raise error.PyAsn1Error('Component type not defined')
+
+        elif not isinstance(value, base.Asn1Item):
+            if componentTypeLen:
+                subComponentType = componentType.getTypeByPosition(idx)
+                if isinstance(subComponentType, base.SimpleAsn1Type):
+                    value = subComponentType.clone(value=value)
+
+                else:
+                    raise error.PyAsn1Error('%s can cast only scalar values' % componentType.__class__.__name__)
+
+            elif currentValue is not noValue and isinstance(currentValue, base.SimpleAsn1Type):
+                value = currentValue.clone(value=value)
+
+            else:
+                raise error.PyAsn1Error('%s undefined component type' % componentType.__class__.__name__)
+
+        elif ((verifyConstraints or matchTags or matchConstraints) and
+              componentTypeLen):
+            subComponentType = componentType.getTypeByPosition(idx)
+            if subComponentType is not noValue:
+                subtypeChecker = (self.strictConstraints and
+                                  subComponentType.isSameTypeWith or
+                                  subComponentType.isSuperTypeOf)
+
+                if not subtypeChecker(value, verifyConstraints and matchTags,
+                                      verifyConstraints and matchConstraints):
+                    if not componentType[idx].openType:
+                        raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType))
+
+        if componentTypeLen or idx in self._dynamicNames:
+            componentValues[idx] = value
+
+        elif len(componentValues) == idx:
+            componentValues.append(value)
+            self._dynamicNames.addField(idx)
+
+        else:
+            raise error.PyAsn1Error('Component index out of range')
+
+        self._componentValues = componentValues
+
+        return self
+
+    @property
+    def isValue(self):
+        """Indicate that |ASN.1| object represents ASN.1 value.
+
+        If *isValue* is :obj:`False` then this object represents just ASN.1 schema.
+
+        If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features,
+        this object can also be used like a Python built-in object (e.g.
+        :class:`int`, :class:`str`, :class:`dict` etc.).
+
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`False` if object represents just ASN.1 schema.
+            :obj:`True` if object represents ASN.1 schema and can be used as a
+            normal value.
+
+        Note
+        ----
+        There is an important distinction between PyASN1 schema and value objects.
+        The PyASN1 schema objects can only participate in ASN.1 schema-related
+        operations (e.g. defining or testing the structure of the data). Most
+        obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+        encoding/decoding serialised ASN.1 contents.
+
+        The PyASN1 value objects can **additionally** participate in many operations
+        involving regular Python objects (e.g. arithmetic, comprehension etc).
+
+        It is sufficient for |ASN.1| objects to have all non-optional and non-defaulted
+        components being value objects to be considered as a value objects as a whole.
+        In other words, even having one or more optional components not turned into
+        value objects, |ASN.1| object is still considered as a value object. Defaulted
+        components are normally value objects by default.
+        """
+        if self._componentValues is noValue:
+            return False
+
+        componentType = self.componentType
+
+        if componentType:
+            for idx, subComponentType in enumerate(componentType.namedTypes):
+                if subComponentType.isDefaulted or subComponentType.isOptional:
+                    continue
+
+                if not self._componentValues:
+                    return False
+
+                componentValue = self._componentValues[idx]
+                if componentValue is noValue or not componentValue.isValue:
+                    return False
+
+        else:
+            for componentValue in self._componentValues:
+                if componentValue is noValue or not componentValue.isValue:
+                    return False
+
+        return True
+
+    @property
+    def isInconsistent(self):
+        """Run necessary checks to ensure |ASN.1| object consistency.
+
+        Default action is to verify |ASN.1| object against constraints imposed
+        by `subtypeSpec`.
+
+        Raises
+        ------
+        :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found
+        """
+        if self.componentType is noValue or not self.subtypeSpec:
+            return False
+
+        if self._componentValues is noValue:
+            return True
+
+        mapping = {}
+
+        for idx, value in enumerate(self._componentValues):
+            # Absent fields are not in the mapping
+            if value is noValue:
+                continue
+
+            name = self.componentType.getNameByPosition(idx)
+
+            mapping[name] = value
+
+        try:
+            # Represent Sequence/Set as a bare dict to constraints chain
+            self.subtypeSpec(mapping)
+
+        except error.PyAsn1Error as exc:
+            return exc
+
+        return False
+
+    def prettyPrint(self, scope=0):
+        """Return an object representation string.
+
+        Returns
+        -------
+        : :class:`str`
+            Human-friendly object representation.
+        """
+        scope += 1
+        representation = self.__class__.__name__ + ':\n'
+        for idx, componentValue in enumerate(self._componentValues):
+            if componentValue is not noValue and componentValue.isValue:
+                representation += ' ' * scope
+                if self.componentType:
+                    representation += self.componentType.getNameByPosition(idx)
+                else:
+                    representation += self._dynamicNames.getNameByPosition(idx)
+                representation = '%s=%s\n' % (
+                    representation, componentValue.prettyPrint(scope)
+                )
+        return representation
+
+    def prettyPrintType(self, scope=0):
+        scope += 1
+        representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__)
+        for idx, componentType in enumerate(self.componentType.values() or self._componentValues):
+            representation += ' ' * scope
+            if self.componentType:
+                representation += '"%s"' % self.componentType.getNameByPosition(idx)
+            else:
+                representation += '"%s"' % self._dynamicNames.getNameByPosition(idx)
+            representation = '%s = %s\n' % (
+                representation, componentType.prettyPrintType(scope)
+            )
+        return representation + '\n' + ' ' * (scope - 1) + '}'
+
+    # backward compatibility
+
+    def setDefaultComponents(self):
+        return self
+
+    def getComponentType(self):
+        if self._componentTypeLen:
+            return self.componentType
+
+    def getNameByPosition(self, idx):
+        if self._componentTypeLen:
+            return self.componentType[idx].name
+
+class Sequence(SequenceAndSetBase):
+    __doc__ = SequenceAndSetBase.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+    )
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+    #: object imposing size constraint on |ASN.1| objects
+    componentType = namedtype.NamedTypes()
+
+    # Disambiguation ASN.1 types identification
+    typeId = SequenceAndSetBase.getTypeId()
+
+    # backward compatibility
+
+    def getComponentTagMapNearPosition(self, idx):
+        if self.componentType:
+            return self.componentType.getTagMapNearPosition(idx)
+
+    def getComponentPositionNearType(self, tagSet, idx):
+        if self.componentType:
+            return self.componentType.getPositionNearType(tagSet, idx)
+        else:
+            return idx
+
+
+class Set(SequenceAndSetBase):
+    __doc__ = SequenceAndSetBase.__doc__
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+    )
+
+    #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+    #: object representing ASN.1 type allowed within |ASN.1| type
+    componentType = namedtype.NamedTypes()
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = SequenceAndSetBase.getTypeId()
+
+    def getComponent(self, innerFlag=False):
+        return self
+
+    def getComponentByType(self, tagSet, default=noValue,
+                           instantiate=True, innerFlag=False):
+        """Returns |ASN.1| type component by ASN.1 tag.
+
+        Parameters
+        ----------
+        tagSet : :py:class:`~pyasn1.type.tag.TagSet`
+            Object representing ASN.1 tags to identify one of
+            |ASN.1| object component
+
+        Keyword Args
+        ------------
+        default: :class:`object`
+            If set and requested component is a schema object, return the `default`
+            object instead of the requested component.
+
+        instantiate: :class:`bool`
+            If :obj:`True` (default), inner component will be automatically
+            instantiated.
+            If :obj:`False` either existing component or the :class:`noValue`
+            object will be returned.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            a pyasn1 object
+        """
+        componentValue = self.getComponentByPosition(
+            self.componentType.getPositionByType(tagSet),
+            default=default, instantiate=instantiate
+        )
+        if innerFlag and isinstance(componentValue, Set):
+            # get inner component by inner tagSet
+            return componentValue.getComponent(innerFlag=True)
+        else:
+            # get outer component by inner tagSet
+            return componentValue
+
+    def setComponentByType(self, tagSet, value=noValue,
+                           verifyConstraints=True,
+                           matchTags=True,
+                           matchConstraints=True,
+                           innerFlag=False):
+        """Assign |ASN.1| type component by ASN.1 tag.
+
+        Parameters
+        ----------
+        tagSet : :py:class:`~pyasn1.type.tag.TagSet`
+            Object representing ASN.1 tags to identify one of
+            |ASN.1| object component
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component.
+            If `value` is not given, schema object will be set as a component.
+
+        verifyConstraints : :class:`bool`
+            If :obj:`False`, skip constraints validation
+
+        matchTags: :class:`bool`
+            If :obj:`False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+            If :obj:`False`, skip component constraints matching
+
+        innerFlag: :class:`bool`
+            If :obj:`True`, search for matching *tagSet* recursively.
+
+        Returns
+        -------
+        self
+        """
+        idx = self.componentType.getPositionByType(tagSet)
+
+        if innerFlag:  # set inner component by inner tagSet
+            componentType = self.componentType.getTypeByPosition(idx)
+
+            if componentType.tagSet:
+                return self.setComponentByPosition(
+                    idx, value, verifyConstraints, matchTags, matchConstraints
+                )
+            else:
+                componentType = self.getComponentByPosition(idx)
+                return componentType.setComponentByType(
+                    tagSet, value, verifyConstraints, matchTags, matchConstraints, innerFlag=innerFlag
+                )
+        else:  # set outer component by inner tagSet
+            return self.setComponentByPosition(
+                idx, value, verifyConstraints, matchTags, matchConstraints
+            )
+
+    @property
+    def componentTagMap(self):
+        if self.componentType:
+            return self.componentType.tagMapUnique
+
+
+class Choice(Set):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`,
+    its objects are mutable and duck-type Python :class:`list` objects.
+
+    Keyword Args
+    ------------
+    componentType: :py:class:`~pyasn1.type.namedtype.NamedType`
+        Object holding named ASN.1 types allowed within this collection
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s).  Constraints
+        verification for |ASN.1| type can only occur on explicit
+        `.isInconsistent` call.
+
+    Examples
+    --------
+
+    .. code-block:: python
+
+        class Afters(Choice):
+            '''
+            ASN.1 specification:
+
+            Afters ::= CHOICE {
+                cheese  [0] IA5String,
+                dessert [1] IA5String
+            }
+            '''
+            componentType = NamedTypes(
+                NamedType('cheese', IA5String().subtype(
+                    implicitTag=Tag(tagClassContext, tagFormatSimple, 0)
+                ),
+                NamedType('dessert', IA5String().subtype(
+                    implicitTag=Tag(tagClassContext, tagFormatSimple, 1)
+                )
+            )
+
+        afters = Afters()
+        afters['cheese'] = 'Mascarpone'
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.TagSet()  # untagged
+
+    #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+    #: object representing ASN.1 type allowed within |ASN.1| type
+    componentType = namedtype.NamedTypes()
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection(
+        constraint.ValueSizeConstraint(1, 1)
+    )
+
+    # Disambiguation ASN.1 types identification
+    typeId = Set.getTypeId()
+
+    _currentIdx = None
+
+    def __eq__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] == other
+        return NotImplemented
+
+    def __ne__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] != other
+        return NotImplemented
+
+    def __lt__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] < other
+        return NotImplemented
+
+    def __le__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] <= other
+        return NotImplemented
+
+    def __gt__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] > other
+        return NotImplemented
+
+    def __ge__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] >= other
+        return NotImplemented
+
+    def __bool__(self):
+        return bool(self._componentValues)
+
+    def __len__(self):
+        return self._currentIdx is not None and 1 or 0
+
+    def __contains__(self, key):
+        if self._currentIdx is None:
+            return False
+        return key == self.componentType[self._currentIdx].getName()
+
+    def __iter__(self):
+        if self._currentIdx is None:
+            raise StopIteration
+        yield self.componentType[self._currentIdx].getName()
+
+    # Python dict protocol
+
+    def values(self):
+        if self._currentIdx is not None:
+            yield self._componentValues[self._currentIdx]
+
+    def keys(self):
+        if self._currentIdx is not None:
+            yield self.componentType[self._currentIdx].getName()
+
+    def items(self):
+        if self._currentIdx is not None:
+            yield self.componentType[self._currentIdx].getName(), self[self._currentIdx]
+
+    def checkConsistency(self):
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        try:
+            component = self.getComponent()
+        except error.PyAsn1Error:
+            pass
+        else:
+            if isinstance(component, Choice):
+                tagSet = component.effectiveTagSet
+            else:
+                tagSet = component.tagSet
+            if isinstance(component, base.ConstructedAsn1Type):
+                myClone.setComponentByType(
+                    tagSet, component.clone(cloneValueFlag=cloneValueFlag)
+                )
+            else:
+                myClone.setComponentByType(tagSet, component.clone())
+
+    def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+        __doc__ = Set.__doc__
+
+        if self._currentIdx is None or self._currentIdx != idx:
+            return Set.getComponentByPosition(self, idx, default=default,
+                                              instantiate=instantiate)
+
+        return self._componentValues[idx]
+
+    def setComponentByPosition(self, idx, value=noValue,
+                               verifyConstraints=True,
+                               matchTags=True,
+                               matchConstraints=True):
+        """Assign |ASN.1| type component by position.
+
+        Equivalent to Python sequence item assignment operation (e.g. `[]`).
+
+        Parameters
+        ----------
+        idx: :class:`int`
+            Component index (zero-based). Must either refer to existing
+            component or to N+1 component. In the latter case a new component
+            type gets instantiated (if *componentType* is set, or given ASN.1
+            object is taken otherwise) and appended to the |ASN.1| sequence.
+
+        Keyword Args
+        ------------
+        value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+            A Python value to initialize |ASN.1| component with (if *componentType* is set)
+            or ASN.1 value object to assign to |ASN.1| component. Once a new value is
+            set to *idx* component, previous value is dropped.
+            If `value` is not given, schema object will be set as a component.
+
+        verifyConstraints : :class:`bool`
+            If :obj:`False`, skip constraints validation
+
+        matchTags: :class:`bool`
+            If :obj:`False`, skip component tags matching
+
+        matchConstraints: :class:`bool`
+            If :obj:`False`, skip component constraints matching
+
+        Returns
+        -------
+        self
+        """
+        oldIdx = self._currentIdx
+        Set.setComponentByPosition(self, idx, value, verifyConstraints, matchTags, matchConstraints)
+        self._currentIdx = idx
+        if oldIdx is not None and oldIdx != idx:
+            self._componentValues[oldIdx] = noValue
+        return self
+
+    @property
+    def effectiveTagSet(self):
+        """Return a :class:`~pyasn1.type.tag.TagSet` object of the currently initialized component or self (if |ASN.1| is tagged)."""
+        if self.tagSet:
+            return self.tagSet
+        else:
+            component = self.getComponent()
+            return component.effectiveTagSet
+
+    @property
+    def tagMap(self):
+        """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping
+            ASN.1 tags to ASN.1 objects contained within callee.
+        """
+        if self.tagSet:
+            return Set.tagMap.fget(self)
+        else:
+            return self.componentType.tagMapUnique
+
+    def getComponent(self, innerFlag=False):
+        """Return currently assigned component of the |ASN.1| object.
+
+        Returns
+        -------
+        : :py:class:`~pyasn1.type.base.PyAsn1Item`
+            a PyASN1 object
+        """
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+        else:
+            c = self._componentValues[self._currentIdx]
+            if innerFlag and isinstance(c, Choice):
+                return c.getComponent(innerFlag)
+            else:
+                return c
+
+    def getName(self, innerFlag=False):
+        """Return the name of currently assigned component of the |ASN.1| object.
+
+        Returns
+        -------
+        : :py:class:`str`
+            |ASN.1| component name
+        """
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+        else:
+            if innerFlag:
+                c = self._componentValues[self._currentIdx]
+                if isinstance(c, Choice):
+                    return c.getName(innerFlag)
+            return self.componentType.getNameByPosition(self._currentIdx)
+
+    @property
+    def isValue(self):
+        """Indicate that |ASN.1| object represents ASN.1 value.
+
+        If *isValue* is :obj:`False` then this object represents just ASN.1 schema.
+
+        If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features,
+        this object can also be used like a Python built-in object (e.g.
+        :class:`int`, :class:`str`, :class:`dict` etc.).
+
+        Returns
+        -------
+        : :class:`bool`
+            :obj:`False` if object represents just ASN.1 schema.
+            :obj:`True` if object represents ASN.1 schema and can be used as a normal
+            value.
+
+        Note
+        ----
+        There is an important distinction between PyASN1 schema and value objects.
+        The PyASN1 schema objects can only participate in ASN.1 schema-related
+        operations (e.g. defining or testing the structure of the data). Most
+        obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+        encoding/decoding serialised ASN.1 contents.
+
+        The PyASN1 value objects can **additionally** participate in many operations
+        involving regular Python objects (e.g. arithmetic, comprehension etc).
+        """
+        if self._currentIdx is None:
+            return False
+
+        componentValue = self._componentValues[self._currentIdx]
+
+        return componentValue is not noValue and componentValue.isValue
+
+    def clear(self):
+        self._currentIdx = None
+        return Set.clear(self)
+
+    # compatibility stubs
+
+    def getMinTagSet(self):
+        return self.minTagSet
+
+
+class Any(OctetString):
+    """Create |ASN.1| schema or value object.
+
+    |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`,
+    its objects are immutable and duck-type :class:`bytes`.
+    When used in Unicode context, |ASN.1| type assumes
+    "|encoding|" serialisation.
+
+    Keyword Args
+    ------------
+    value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
+        :class:`bytes`, alternatively :class:`str`
+        representing character string to be serialised into octets (note
+        `encoding` parameter) or |ASN.1| object.
+        If `value` is not given, schema object will be created.
+
+    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+        Object representing non-default ASN.1 tag(s)
+
+    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+        Object representing non-default ASN.1 subtype constraint(s). Constraints
+        verification for |ASN.1| type occurs automatically on object
+        instantiation.
+
+    encoding: :py:class:`str`
+        Unicode codec ID to encode/decode
+        :class:`str` the payload when |ASN.1| object is used
+        in text string context.
+
+    binValue: :py:class:`str`
+        Binary string initializer to use instead of the *value*.
+        Example: '10110011'.
+
+    hexValue: :py:class:`str`
+        Hexadecimal string initializer to use instead of the *value*.
+        Example: 'DEADBEEF'.
+
+    Raises
+    ------
+    ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error
+        On constraint violation or bad initializer.
+
+    Examples
+    --------
+    .. code-block:: python
+
+        class Error(Sequence):
+            '''
+            ASN.1 specification:
+
+            Error ::= SEQUENCE {
+                code      INTEGER,
+                parameter ANY DEFINED BY code  -- Either INTEGER or REAL
+            }
+            '''
+            componentType=NamedTypes(
+                NamedType('code', Integer()),
+                NamedType('parameter', Any(),
+                          openType=OpenType('code', {1: Integer(),
+                                                     2: Real()}))
+            )
+
+        error = Error()
+        error['code'] = 1
+        error['parameter'] = Integer(1234)
+    """
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+    #: associated with |ASN.1| type.
+    tagSet = tag.TagSet()  # untagged
+
+    #: Set (on class, not on instance) or return a
+    #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+    #: imposing constraints on |ASN.1| type initialization values.
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Disambiguation ASN.1 types identification
+    typeId = OctetString.getTypeId()
+
+    @property
+    def tagMap(self):
+        """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping
+            ASN.1 tags to ASN.1 objects contained within callee.
+        """
+        try:
+            return self._tagMap
+
+        except AttributeError:
+            self._tagMap = tagmap.TagMap(
+                {self.tagSet: self},
+                {eoo.endOfOctets.tagSet: eoo.endOfOctets},
+                self
+            )
+
+            return self._tagMap
+
+# XXX
+# coercion rules?
diff --git a/.venv/lib/python3.12/site-packages/pyasn1/type/useful.py b/.venv/lib/python3.12/site-packages/pyasn1/type/useful.py
new file mode 100644
index 00000000..a8ae8740
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pyasn1/type/useful.py
@@ -0,0 +1,189 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
+# License: https://pyasn1.readthedocs.io/en/latest/license.html
+#
+import datetime
+
+from pyasn1 import error
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+
+__all__ = ['ObjectDescriptor', 'GeneralizedTime', 'UTCTime']
+
+NoValue = univ.NoValue
+noValue = univ.noValue
+
+
+class ObjectDescriptor(char.GraphicString):
+    __doc__ = char.GraphicString.__doc__
+
+    #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
+    tagSet = char.GraphicString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7)
+    )
+
+    # Optimization for faster codec lookup
+    typeId = char.GraphicString.getTypeId()
+
+
+class TimeMixIn(object):
+
+    _yearsDigits = 4
+    _hasSubsecond = False
+    _optionalMinutes = False
+    _shortTZ = False
+
+    class FixedOffset(datetime.tzinfo):
+        """Fixed offset in minutes east from UTC."""
+
+        # defaulted arguments required
+        # https: // docs.python.org / 2.3 / lib / datetime - tzinfo.html
+        def __init__(self, offset=0, name='UTC'):
+            self.__offset = datetime.timedelta(minutes=offset)
+            self.__name = name
+
+        def utcoffset(self, dt):
+            return self.__offset
+
+        def tzname(self, dt):
+            return self.__name
+
+        def dst(self, dt):
+            return datetime.timedelta(0)
+
+    UTC = FixedOffset()
+
+    @property
+    def asDateTime(self):
+        """Create :py:class:`datetime.datetime` object from a |ASN.1| object.
+
+        Returns
+        -------
+        :
+            new instance of :py:class:`datetime.datetime` object
+        """
+        text = str(self)
+        if text.endswith('Z'):
+            tzinfo = TimeMixIn.UTC
+            text = text[:-1]
+
+        elif '-' in text or '+' in text:
+            if '+' in text:
+                text, plusminus, tz = text.partition('+')
+            else:
+                text, plusminus, tz = text.partition('-')
+
+            if self._shortTZ and len(tz) == 2:
+                tz += '00'
+
+            if len(tz) != 4:
+                raise error.PyAsn1Error('malformed time zone offset %s' % tz)
+
+            try:
+                minutes = int(tz[:2]) * 60 + int(tz[2:])
+                if plusminus == '-':
+                    minutes *= -1
+
+            except ValueError:
+                raise error.PyAsn1Error('unknown time specification %s' % self)
+
+            tzinfo = TimeMixIn.FixedOffset(minutes, '?')
+
+        else:
+            tzinfo = None
+
+        if '.' in text or ',' in text:
+            if '.' in text:
+                text, _, ms = text.partition('.')
+            else:
+                text, _, ms = text.partition(',')
+
+            try:
+                ms = int(ms) * 1000
+
+            except ValueError:
+                raise error.PyAsn1Error('bad sub-second time specification %s' % self)
+
+        else:
+            ms = 0
+
+        if self._optionalMinutes and len(text) - self._yearsDigits == 6:
+            text += '0000'
+        elif len(text) - self._yearsDigits == 8:
+            text += '00'
+
+        try:
+            dt = datetime.datetime.strptime(text, self._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
+
+        except ValueError:
+            raise error.PyAsn1Error('malformed datetime format %s' % self)
+
+        return dt.replace(microsecond=ms, tzinfo=tzinfo)
+
+    @classmethod
+    def fromDateTime(cls, dt):
+        """Create |ASN.1| object from a :py:class:`datetime.datetime` object.
+
+        Parameters
+        ----------
+        dt: :py:class:`datetime.datetime` object
+            The `datetime.datetime` object to initialize the |ASN.1| object
+            from
+
+        Returns
+        -------
+        :
+            new instance of |ASN.1| value
+        """
+        text = dt.strftime(cls._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
+        if cls._hasSubsecond:
+            text += '.%d' % (dt.microsecond // 1000)
+
+        if dt.utcoffset():
+            seconds = dt.utcoffset().seconds
+            if seconds < 0:
+                text += '-'
+            else:
+                text += '+'
+            text += '%.2d%.2d' % (seconds // 3600, seconds % 3600)
+        else:
+            text += 'Z'
+
+        return cls(text)
+
+
+class GeneralizedTime(char.VisibleString, TimeMixIn):
+    __doc__ = char.VisibleString.__doc__
+
+    #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
+    tagSet = char.VisibleString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
+    )
+
+    # Optimization for faster codec lookup
+    typeId = char.VideotexString.getTypeId()
+
+    _yearsDigits = 4
+    _hasSubsecond = True
+    _optionalMinutes = True
+    _shortTZ = True
+
+
+class UTCTime(char.VisibleString, TimeMixIn):
+    __doc__ = char.VisibleString.__doc__
+
+    #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
+    tagSet = char.VisibleString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
+    )
+
+    # Optimization for faster codec lookup
+    typeId = char.VideotexString.getTypeId()
+
+    _yearsDigits = 2
+    _hasSubsecond = False
+    _optionalMinutes = False
+    _shortTZ = False