/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.channel.cc.commons.keymanagement;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.time.ZonedDateTime;
import java.util.Date;

import javax.annotation.WillNotClose;
import javax.annotation.concurrent.ThreadSafe;

import com.scytl.cryptolib.elgamal.bean.ElGamalPrivateKey;
import com.scytl.cryptolib.elgamal.bean.ElGamalPublicKey;
import com.scytl.cryptolib.elgamal.cryptoapi.EncryptionParameters;

/**
 * <p>
 * Key manager.
 * <p>
 * Key manager is stateful. It is required to activate the node keys like CCN
 * CA, secure log keys etc before using the manager. During the first launch of
 * the application the client creates and activates the node keys using
 * {@link #createAndActivateNodeKeys(KeyStore, PasswordProtection)} method or
 * alike. In case of restart client activates previously created node keys with
 * {@link #activateNodeKeys(PasswordProtection)} method. Client can find out if
 * the keys are activated with {@link #hasNodeKeysActivated()} method.
 */
@ThreadSafe
public interface KeyManager {
    /**
     * Activates node keys stored in the internal database and protected with
     * the specified password.
     * 
     * @param password
     *            the password
     * @throws KeyNotFoundException
     *             the keys does not exist
     * @throws InvalidPasswordException
     *             the password is invalid
     * @throws KeyManagementException
     *             failed to activate the keys.
     */
    void activateNodeKeys(PasswordProtection password)
            throws KeyNotFoundException, InvalidPasswordException, KeyManagementException;

    /**
     * Creates and activates the node keys from the CCN CA keys and X509
     * certificates which are stored under the specified alias in a PKCS12 key
     * store represented by a given stream. Client provides the password to open
     * the key store, that password will also be used to protect the node keys.
     * 
     * @param stream
     *            the stream
     * @param alias
     *            the alias
     * @param password
     *            the password
     * @throws InvalidKeyStoreException
     *             the keys store is invalid
     * @throws InvalidPasswordException
     *             the password is invalid
     * @throws InvalidNodeCAException
     *             the node CA is invalid
     * @throws KeyAlreadyExistsException
     *             the keys already exist
     * @throws KeyManagementException
     *             failed to create the keys
     * @throws IOException
     *             I/O error occurred.
     */
    void createAndActivateNodeKeys(@WillNotClose InputStream stream, String alias, PasswordProtection password)
            throws InvalidKeyStoreException, InvalidPasswordException, InvalidNodeCAException,
            KeyAlreadyExistsException, KeyManagementException, IOException;

    /**
     * Creates and activates the node keys from the CCN CA keys and X509
     * certificates which are stored under the specified alias in a given PKCS12
     * key store. Client provides the password to open the key store, that
     * password will also be used to protect the node keys.
     * 
     * @param store
     *            the store
     * @param alias
     *            the alias
     * @param password
     *            the password
     * @throws InvalidKeyStoreException
     *             the keys store is invalid
     * @throws InvalidPasswordException
     *             the password is invalid
     * @throws InvalidNodeCAException
     *             the node CA is invalid
     * @throws KeyAlreadyExistsException
     *             the keys already exist
     * @throws KeyManagementException
     *             failed to create the keys.
     */
    void createAndActivateNodeKeys(KeyStore store, String alias, PasswordProtection password)
            throws InvalidKeyStoreException, InvalidPasswordException, InvalidNodeCAException,
            KeyAlreadyExistsException, KeyManagementException;

    /**
     * Creates and activates the node keys from the CCN CA keys and X509
     * certificates which are stored under the specified alias in a PKCS12 key
     * store represented by a given file. Client provides the password to open
     * the key store, that password will also be used to protect the node keys.
     * 
     * @param file
     *            the file
     * @param alias
     *            the alias
     * @param password
     *            the password
     * @throws InvalidKeyStoreException
     *             the keys store is invalid
     * @throws InvalidPasswordException
     *             the password is invalid
     * @throws InvalidNodeCAException
     *             the node CA is invalid
     * @throws KeyAlreadyExistsException
     *             the keys already exist
     * @throws KeyManagementException
     *             failed to create the keys
     * @throws NoSuchFileException
     *             the file does not exist
     * @throws IOException
     *             I/O error occurred
     */
    void createAndActivateNodeKeys(Path file, String alias, PasswordProtection password)
            throws InvalidKeyStoreException, InvalidPasswordException, InvalidNodeCAException,
            KeyAlreadyExistsException, KeyManagementException, NoSuchFileException, IOException;

    /**
     * Creates and activates the node keys from given CCN CA keys and X509
     * certificates. Client also provides a password to protect the node key,
     * that password will be used later in
     * {@link #activateNodeKeys(PasswordProtection)}.
     * 
     * @param nodeCAPrivateKey
     *            the private key
     * @param nodeCACertificateChain
     *            the certificate chain
     * @param password
     *            the password
     * @throws InvalidNodeCAException
     *             the node CA is invalid
     * @throws KeyAlreadyExistsException
     *             the keys already exist
     * @throws KeyManagementException
     *             failed to create and activate the node keys.
     */
    void createAndActivateNodeKeys(PrivateKey nodeCAPrivateKey, X509Certificate[] nodeCACertificateChain,
            PasswordProtection password)
            throws InvalidNodeCAException, KeyAlreadyExistsException, KeyManagementException;

    /**
     * Creates choice code keys for given specification
     * 
     * @param spec
     *            the specification
     * @throws KeyAlreadyExistsException
     *             the keys already exist
     * @throws KeyNotFoundException
     *             election signing key not found
     * @throws KeyManagementException
     *             failed to create the keys
     * @throws FingerprintGeneratorException
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    void createChoiceCodeKeys(ChoiceCodeKeysSpec spec)
            throws KeyAlreadyExistsException, KeyNotFoundException, KeyManagementException,
            FingerprintGeneratorException;

    /**
     * Creates the election signing keys for given election and validity period.
     * 
     * @param electionEventId
     *            the election identifier
     * @param validFrom
     *            the start of the validity period
     * @param validTo
     *            the end of the validity period (inclusive)
     * @throws KeyAlreadyExistsException
     *             the keys already exist
     * @throws KeyManagementException
     *             failed to create the keys
     * @throws IllegalArgumentException
     *             the validity period is empty
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    void createElectionSigningKeys(String electionEventId, Date validFrom, Date validTo)
            throws KeyAlreadyExistsException, KeyManagementException, FingerprintGeneratorException;

    /**
     * Creates the election signing keys for given election and validity period.
     * 
     * @param electionEventId
     *            the election identifier
     * @param validFrom
     *            the start of the validity period
     * @param validTo
     *            the end of the validity period (inclusive)
     * @throws KeyAlreadyExistsException
     *             the keys already exist
     * @throws KeyManagementException
     *             failed to create the keys
     * @throws IllegalArgumentException
     *             the validity period is empty
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    void createElectionSigningKeys(String electionEventId, ZonedDateTime validFrom, ZonedDateTime validTo)
            throws KeyAlreadyExistsException, KeyManagementException, FingerprintGeneratorException;

    /**
     * Creates mixing keys for given specificaion.
     * 
     * @param spec
     *            the specification
     * @throws KeyAlreadyExistsException
     *             the keys already exist
     * @throws KeyNotFoundException
     *             the election signing key not found
     * @throws KeyManagementException
     *             failed to the keys
     * @throws FingerprintGeneratorException
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    void createMixingKeys(MixingKeysSpec spec)
            throws KeyAlreadyExistsException, KeyNotFoundException, KeyManagementException,
            FingerprintGeneratorException;

    /**
     * Returns the choice code decryption private key for given election and
     * verification card set.
     * 
     * @param electionEventId
     *            the election identifier
     * @param verificationCardSetId
     *            the verification card set identifier
     * @return the key
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    ElGamalPrivateKey getChoiceCodeDecryptionPrivateKey(String electionEventId, String verificationCardSetId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the choice code decryption public key for given election and
     * verification card set.
     * 
     * @param electionEventId
     *            the election identifier
     * @param verificationCardSetId
     *            the verification card set identifier
     * @return the key
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    ElGamalPublicKey getChoiceCodeDecryptionPublicKey(String electionEventId, String verificationCardSetId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the choice code decryption public key signature for given
     * election and verification card set.
     * 
     * @param electionEventId
     *            the election identifier
     * @param verificationCardSetId
     *            the verification card set identifier
     * @return the signature
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the signature
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    byte[] getChoiceCodeDecryptionPublicKeySignature(String electionEventId, String verificationCardSetId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the choice code encryption parameters for given election and
     * verification card set.
     * 
     * @param electionEventId
     *            the election identifier
     * @param verificationCardSetId
     *            the verification card set identifier
     * @return the signature
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the signature
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    EncryptionParameters getChoiceCodeEncryptionParameters(String electionEventId, String verificationCardSetId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the choice code generation private key for given election and
     * verification card set.
     * 
     * @param electionEventId
     *            the election identifier
     * @param verificationCardSetId
     *            the verification card set identifier
     * @return the key
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    ElGamalPrivateKey getChoiceCodeGenerationPrivateKey(String electionEventId, String verificationCardSetId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the choice code generation public key for given election and
     * verification card set.
     * 
     * @param electionEventId
     *            the election identifier
     * @param verificationCardSetId
     *            the verification card set identifier
     * @return the key
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    ElGamalPublicKey getChoiceCodeGenerationPublicKey(String electionEventId, String verificationCardSetId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the choice code generation public key signature for given
     * election and verification card set.
     * 
     * @param electionEventId
     *            the election identifier
     * @param verificationCardSetId
     *            the verification card set identifier
     * @return the signature
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the signature
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    byte[] getChoiceCodeGenerationPublicKeySignature(String electionEventId, String verificationCardSetId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the election signing certificate for a given election.
     * 
     * @param electionEventId
     *            the election identifier
     * @return the certificate
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the certificate
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    X509Certificate getElectionSigningCertificate(String electionEventId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the election signing certificate chain for a given election.
     * 
     * @param electionEventId
     *            the election identifier
     * @return the certificate chain
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the certificate
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    X509Certificate[] getElectionSigningCertificateChain(String electionEventId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the election signing private key for a given election.
     * 
     * @param electionEventId
     *            the election identifier
     * @return the key
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PrivateKey getElectionSigningPrivateKey(String electionEventId) throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the election signing public key for a given election.
     * 
     * @param electionEventId
     *            the election identifier
     * @return the key
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PublicKey getElectionSigningPublicKey(String electionEventId) throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the mixing private key for given election and electoral
     * authority.
     * 
     * @param electionEventId
     *            the election identifier
     * @param electoralAuthorityId
     *            the electoral authority identifier
     * @return the key
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    ElGamalPrivateKey getMixingPrivateKey(String electionEventId, String electoralAuthorityId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the mixing public key for given election and electoral authority.
     * 
     * @param electionEventId
     *            the election identifier
     * @param electoralAuthorityId
     *            the electoral authority identifier
     * @return the key
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    ElGamalPublicKey getMixingPublicKey(String electionEventId, String electoralAuthorityId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns the mixing public key signature for given election and electoral
     * authority.
     * 
     * @param electionEventId
     *            the election identifier
     * @param electoralAuthorityId
     *            the electoral authority identifier
     * @return the signature
     * @throws KeyNotFoundException
     *             the key not found
     * @throws KeyManagementException
     *             failed to get the signature
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    byte[] getMixingPublicKeySignature(String electionEventId, String electoralAuthorityId)
            throws KeyNotFoundException, KeyManagementException;

    /**
     * Gets the platform CA certificate.
     *
     * @return the platform CA certificate
     * @throws KeyNotFoundException
     * @throws KeyManagementException
     */
    X509Certificate getPlatformCACertificate() throws KeyNotFoundException, KeyManagementException;

    /**
     * Returns true, if the choice code keys are already available in the
     * database.
     * 
     * @param electionEventId
     *            the election identifier
     * @param verificationCardSetId
     *            the verification card set identifier
     * @return true if the key exists
     * @throws KeyManagementException
     *             failed to get the key
     */
    boolean hasChoiceCodeKeys(String electionEventId, String verificationCardSetId) throws KeyManagementException;

    /**
     * Returns true, if the mixing keys are already available in the database.
     * 
     * @param electionEventId
     *            the election identifier
     * @param electoralAuthorityId
     *            the electoral authority identifier
     * @return true if the key exists
     * @throws KeyManagementException
     *             failed to get the key
     */
    boolean hasMixingKeys(String electionEventId, String electoralAuthorityId) throws KeyManagementException;

    /**
     * Returns whether the node keys are activated.
     * 
     * @return the node keys are activated.
     */
    boolean hasNodeKeysActivated();

    /**
     * Returns whether for a given election there is a signing key with the
     * certificate which is valid during the specified period.
     * 
     * @param electionEventId
     *            the election identifier
     * @param validFrom
     *            the start of the validity period
     * @param validTo
     *            the end of the validity period (inclusive)
     * @return the key exists
     * @throws KeyManagementException
     *             failed to find out whether the key exists
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    boolean hasValidElectionSigningKeys(String electionEventId, Date validFrom, Date validTo)
            throws KeyManagementException;

    /**
     * Returns whether for a given election there is a signing key with the
     * certificate which is valid during the specified period.
     * 
     * @param electionEventId
     *            the election identifier
     * @param validFrom
     *            the start of the validity period
     * @param validTo
     *            the end of the validity period (inclusive)
     * @return the key exists
     * @throws KeyManagementException
     *             failed to find out whether the key exists
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    boolean hasValidElectionSigningKeys(String electionEventId, ZonedDateTime validFrom, ZonedDateTime validTo)
            throws KeyManagementException;

    /**
     * Returns the CCN CA certificate.
     * 
     * @return the CCN CA certificate
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    X509Certificate nodeCACertificate();

    /**
     * Returns the CCN CA private key.
     * 
     * @return the CCN CA private key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PrivateKey nodeCAPrivateKey();

    /**
     * Returns the CCN CA public key.
     * 
     * @return the CCN CA public key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PublicKey nodeCAPublicKey();

    /**
     * Returns the node encryption certificate.
     * 
     * @return the node encryption certificate
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    X509Certificate nodeEncryptionCertificate();

    /**
     * Returns the node encryption private key.
     * 
     * @return the node encryption private key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PrivateKey nodeEncryptionPrivateKey();

    /**
     * Returns the node encryption public key.
     * 
     * @return the node encryption public key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PublicKey nodeEncryptionPublicKey();

    /**
     * Returns the node log encryption certificate.
     * 
     * @return the node log encryption certificate
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    X509Certificate nodeLogEncryptionCertificate();

    /**
     * Returns the node log encryption private key.
     * 
     * @return the node log encryption private key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PrivateKey nodeLogEncryptionPrivateKey();

    /**
     * Returns the node log encryption public key.
     * 
     * @return the node log encryption public key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PublicKey nodeLogEncryptionPublicKey();

    /**
     * Returns the node log signing certificate.
     * 
     * @return the node log signing certificate
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    X509Certificate nodeLogSigningCertificate();

    /**
     * Returns the node log signing private key.
     * 
     * @return the node log signing private key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PrivateKey nodeLogSigningPrivateKey();

    /**
     * Returns the node log signing public key.
     * 
     * @return the node log signing public key
     * @throws IllegalStateException
     *             the node keys are not activated.
     */
    PublicKey nodeLogSigningPublicKey();

    /**
     * Shuts the key manager down.
     */
    void shutdown();

    /**
     * Starts the key manager up:
     */
    void startup();

}
