text: from encryption.ECC import *
  
  from Crypto.Protocol.KDF import HKDF
  from Crypto.Hash import SHA512
  
  
  class DiffieHellman:
      """
      This class is a wrapper for a simple scalar multiplication over the MontgomeryCurve25519. It is used by the
      ExtendedTripleDiffieHellman class to generate shared secrets.
  
      Parameters:
          curve :: MontgomeryCurve25519 - the curve over which the scalar multiplication should be performed.
      """
      def __init__(self, curve: MontgomeryCurve25519):
          self.curve = curve
  
      def run(self, private_key: Scalar, public_key: UCoordinate) -> UCoordinate:
          """
          This method runs a multiplication to generate a DH secret.
  
          Parameters:
              private_key :: Scalar - the private key used to generate the secret.
              public_key :: UCoordinate - the public key used to generate the secret.
  
          Returns:
              A new u-coordinate that can be used as secret.
          """
          return self.curve.scalar_multiplication(private_key, public_key)
  
  
  class ExtendedTripleDiffieHellman:
      """
      This class provides the methods necessary to derive a shared secret based on multiple keys as per the Extended
      Diffie Hellman procedure.
  
      Parameters:
          ik_pair :: MontgomeryKeyPair - the key pair of the user. The public key of this key pair is also the unique
              identifier to the server in this application
          curve :: MontgomeryCurve25519 - the curve over which the calculations are performed. This is used by the
              DiffieHellman class.
      """
      def __init__(self, ik_pair: MontgomeryKeyPair, curve: MontgomeryCurve25519):
          self.ik_private, self.ik_public = ik_pair
          self.curve = curve
          self.dh = DiffieHellman(self.curve)
  
      def generate_mutual_secret(self, ik: UCoordinate, spk: UCoordinate,
                                 opk: Optional[UCoordinate] = None) -> Tuple[SharedSecret, EncodedPublicKey]:
          """
          This method handles the part of a shared secret initiator. It adheres to the Extended Triple Diffie Hellmann
          protocol.
  
          Parameters:
               ik :: UCoordinate - the identity key of the other party.
               spk :: UCoordinate - te signed pre-key of the other party.
               opk :: Optional[UCoordinate] - optional pre-key of the other party.
  
          Returns:
              The shared secret and the public key associated with the ephemeral key pair.
          """
          # Determine ephemeral key.
          e_private, e_public = self.curve.generate_key_pair()
          e_s = MontgomeryCurve25519.decode_scalar(e_private)
          # Calculate relevant DH values.
          dh1 = self.dh.run(self.ik_private, spk).to_bytes(32, "little")
          dh2 = self.dh.run(e_s, ik).to_bytes(32, "little")
          dh3 = self.dh.run(e_s, spk).to_bytes(32, "little")
          dh4 = b""
          if opk:
              dh4 = self.dh.run(e_s, spk).to_bytes(32, "little")
          # Format data properly.
          ad = self.ik_public.to_bytes(32, "little") + ik.to_bytes(32, "little")
          # Derive secret key.
          sk = HKDF(salt=b"00" * 32, master=b"ff" * 32 + dh1 + dh2 + dh3 + dh4, key_len=32, hashmod=SHA512)
          return (ad, sk), e_public
  
      def interpret_initial_message(self, ik: UCoordinate, ek: UCoordinate, spk: UCoordinate,
                                    opk: Optional[UCoordinate] = None) -> SharedSecret:
          """
  
          Parameters:
               ik :: UCoordinate - the identity key of the other party.
               ek :: UCoordinate - the ephemeral key generated by the other party.
               spk :: UCoordinate - te signed pre-key of the other party.
               opk :: Optional[UCoordinate] - optional pre-key of the other party.
  
          Returns:
              The shared secret.
          """
          # Calculate relevant DH values.
          dh1 = self.dh.run(spk, ik).to_bytes(32, "little")
          dh2 = self.dh.run(self.ik_private, ek).to_bytes(32, "little")
          dh3 = self.dh.run(spk, ek).to_bytes(32, "little")
          dh4 = b""
          if opk:
              dh4 = self.dh.run(spk, ek).to_bytes(32, "little")
          # Format data properly.
          ad = ik.to_bytes(32, "little") + self.ik_public.to_bytes(32, "little")
          # Derive secret key.
          sk = HKDF(salt=b"00" * 32, master=b"ff" * 32 + dh1 + dh2 + dh3 + dh4, key_len=32, hashmod=SHA512)
          return ad, sk
  
  