docs: def getSharedSecretActive(
          self,
          other_public_bundle,
          allow_zero_otpks = False
      ):
          """
          Do the key exchange, as the active party. This involves selecting keys from the
          passive parties' public bundle.
  
          :param other_public_bundle: An instance of PublicBundle, filled with the public
              data of the passive party.
          :param allow_zero_otpks: A flag indicating whether bundles with no one-time pre
              keys are allowed or throw an error. False is the recommended default.
          :returns: A dictionary containing the shared secret, the shared associated data
              and the data the passive party needs to finalize the key exchange.
  
          The returned structure looks like this::
          
              {
                  "to_other": {
                      # The public key of the active parties' identity key pair
                      "ik": bytes,
  
                      # The public key of the active parties' ephemeral key pair
                      "ek": bytes,
  
                      # The public key of the used passive parties' one-time pre key or None
                      "otpk": bytes or None,
  
                      # The public key of the passive parties' signed pre key pair
                      "spk": bytes
                  },
                  "ad": bytes, # The shared associated data
                  "sk": bytes  # The shared secret
              }
  
          :raises KeyExchangeException: If an error occurs during the key exchange. The
              exception message will contain (human-readable) details.
          """
  
          self.__checkSPKTimestamp()
  
          other_ik = self.__KeyPair(pub = other_public_bundle.ik)
  
          other_spk = {
              "key": self.__KeyPair(pub = other_public_bundle.spk),
              "signature": other_public_bundle.spk_signature
          }
  
          other_otpks = [
              self.__KeyPair(pub = otpk) for otpk in other_public_bundle.otpks
          ]
  
          if len(other_otpks) == 0 and not allow_zero_otpks:
              raise KeyExchangeException(
                  "The other public bundle does not contain any OTPKs, which is not " +
                  "allowed."
              )
  
          other_spk_serialized = self.__PublicKeyEncoder.encodePublicKey(
              other_spk["key"].pub,
              self.__curve
          )
  
          if not self.__XEdDSA(mont_pub = other_ik.pub).verify(
              other_spk_serialized,
              other_spk["signature"]
          ):
              raise KeyExchangeException(
                  "The signature of this public bundle's spk could not be verifified."
              )
  
          ek = self.__KeyPair.generate()
  
          dh1 = self.__ik.getSharedSecret(other_spk["key"])
          dh2 = ek.getSharedSecret(other_ik)
          dh3 = ek.getSharedSecret(other_spk["key"])
          dh4 = b""
  
          otpk = None
          if len(other_otpks) > 0:
              otpk_index = ord(os.urandom(1)) % len(other_otpks)
              otpk = other_otpks[otpk_index]
  
              dh4 = ek.getSharedSecret(otpk)
  
          sk = self.__kdf(dh1 + dh2 + dh3 + dh4)
  
          ik_pub_serialized = self.__PublicKeyEncoder.encodePublicKey(
              self.__ik.pub,
              self.__curve
          )
  
          other_ik_pub_serialized = self.__PublicKeyEncoder.encodePublicKey(
              other_ik.pub,
              self.__curve
          )
  
          ad = ik_pub_serialized + other_ik_pub_serialized
  
          return {
              "to_other": {
                  "ik": self.__ik.pub,
                  "ek": ek.pub,
                  "otpk": otpk.pub if otpk else None,
                  "spk": other_spk["key"].pub
              },
              "ad": ad,
              "sk": sk
          }