package meerkat.crypto;

import com.google.protobuf.ByteString;
import com.google.protobuf.Message;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.SignatureException;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import static meerkat.protobuf.Crypto.*;

/**
 * Created by talm on 25/10/15.
 *
 * Sign and verify arrays of messages
 */
public interface DigitalSignature {
    final public static String CERTIFICATE_ENCODING_X509 = "X.509";


    /**
     * Load a verification certificate.
     * This will be added to the existing set of verification certificates.
     * @param cert
     *
     * @throws CertificateException on parsing errors
     */
    public void loadVerificationCertificate(SignatureVerificationKey cert)
        throws CertificateException;


    /**
     * Compute a fingerprint (SHA256 hash) of a certificate.
     * @param cert
     * @return
     */
    public ByteString computeCertificateFingerprint(SignatureVerificationKey cert) throws CertificateException;

    /**
     * Load a set of certificates from an input stream.
     * This will consume the entire stream.
     * Certificates can be either DER-encoded (binary) or PEM (base64) encoded.
     * This may be called multiple times to load several different certificates.
     * It must be called before calling {@link #verify()}.
     * @param certStream source from which certificates are loaded
     * @throws CertificateException on parsing errors
     */
    public void loadVerificationCertificates(InputStream certStream)
            throws CertificateException;

    /**
     * Clear the loaded verification certificates.
     */
    public void clearVerificationCertificates();

    /**
     * Add raw data to the content stream to be verified / signed.
     *
     * @param data
     * @throws SignatureException
     */
    public void updateContent(byte[] data) throws SignatureException;

    /**
     * Add msg to the content stream to be verified / signed. Each message is (automatically)
     * prepended with its length as a 32-bit big-endian unsigned integer.
     *
     * @param msg
     * @throws SignatureException
     */
    public void updateContent(Message msg) throws SignatureException;


    /**
     * Sign the content that was added using {@link #updateContent(Message)}.
     * Reset the DigitalSignature and make it available to sign a new message using the same key.
     * @return
     * @throws SignatureException
     */
    Signature sign() throws SignatureException;

    /**
     * Initialize the verifier with the certificate whose Id is in sig.
     * @param sig
     * @throws CertificateException
     * @throws InvalidKeyException
     */
    void initVerify(Signature sig)
            throws CertificateException, InvalidKeyException;

    /**
     * Verify the updated content using the initialized signature.
     * @return
     */
    public boolean verify();

    /**
     * Load a keystore from an input stream in PKCS12 format.
     *
     * @param keyStream
     * @param password
     * @return
     * @throws IOException
     * @throws CertificateException
     * @throws KeyStoreException
     * @throws NoSuchAlgorithmException
     */
    public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password)
            throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException;



    /**
     * Loads a private signing key. The keystore must include both the public and private
     * key parts.
     * This method must be called before calling {@link #sign()} or {@link #updateContent(Message)}
     * Calling this method again will replace the key.
     *
     * @param keyStoreBuilder A keystore builder that can be used to load a keystore.
     */
    public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder)
            throws IOException, CertificateException, UnrecoverableKeyException;


    /**
     * @return the signer ID if it exists; null otherwise.
     */
    public ByteString getSignerID();

    /**
     *
     * @return the signer verification key if it exists, null otherwise.
     */
    public SignatureVerificationKey getSignerPublicKey();

    /**
     * Clear the signing key (will require authentication to use again).
     */
    public void clearSigningKey();
}
