package org.bouncycastle.crypto.tls;

import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;

public abstract class AbstractTlsKeyExchange
    implements TlsKeyExchange
{
    protected int keyExchange;
    protected Vector supportedSignatureAlgorithms;

    protected TlsContext context;

    protected AbstractTlsKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms)
    {
        this.keyExchange = keyExchange;
        this.supportedSignatureAlgorithms = supportedSignatureAlgorithms;
    }

    protected DigitallySigned parseSignature(InputStream input) throws IOException
    {
        DigitallySigned signature = DigitallySigned.parse(context, input);
        SignatureAndHashAlgorithm signatureAlgorithm = signature.getAlgorithm();
        if (signatureAlgorithm != null)
        {
            TlsUtils.verifySupportedSignatureAlgorithm(supportedSignatureAlgorithms, signatureAlgorithm);
        }
        return signature;
    }

    public void init(TlsContext context)
    {
        this.context = context;

        ProtocolVersion clientVersion = context.getClientVersion();

        if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
        {
            /*
             * RFC 5246 7.4.1.4.1. If the client does not send the signature_algorithms extension,
             * the server MUST do the following:
             * 
             * - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, DH_RSA, RSA_PSK,
             * ECDH_RSA, ECDHE_RSA), behave as if client had sent the value {sha1,rsa}.
             * 
             * - If the negotiated key exchange algorithm is one of (DHE_DSS, DH_DSS), behave as if
             * the client had sent the value {sha1,dsa}.
             * 
             * - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, ECDHE_ECDSA),
             * behave as if the client had sent value {sha1,ecdsa}.
             */
            if (this.supportedSignatureAlgorithms == null)
            {
                switch (keyExchange)
                {
                case KeyExchangeAlgorithm.DH_DSS:
                case KeyExchangeAlgorithm.DHE_DSS:
                case KeyExchangeAlgorithm.SRP_DSS:
                {
                    this.supportedSignatureAlgorithms = TlsUtils.getDefaultDSSSignatureAlgorithms();
                    break;
                }

                case KeyExchangeAlgorithm.ECDH_ECDSA:
                case KeyExchangeAlgorithm.ECDHE_ECDSA:
                {
                    this.supportedSignatureAlgorithms = TlsUtils.getDefaultECDSASignatureAlgorithms();
                    break;
                }

                case KeyExchangeAlgorithm.DH_RSA:
                case KeyExchangeAlgorithm.DHE_RSA:
                case KeyExchangeAlgorithm.ECDH_RSA:
                case KeyExchangeAlgorithm.ECDHE_RSA:
                case KeyExchangeAlgorithm.RSA:
                case KeyExchangeAlgorithm.RSA_PSK:
                case KeyExchangeAlgorithm.SRP_RSA:
                {
                    this.supportedSignatureAlgorithms = TlsUtils.getDefaultRSASignatureAlgorithms();
                    break;
                }

                case KeyExchangeAlgorithm.DHE_PSK:
                case KeyExchangeAlgorithm.ECDHE_PSK:
                case KeyExchangeAlgorithm.PSK:
                case KeyExchangeAlgorithm.SRP:
                    break;

                default:
                    throw new IllegalStateException("unsupported key exchange algorithm");
                }
            }
        }
        else if (this.supportedSignatureAlgorithms != null)
        {
            throw new IllegalStateException("supported_signature_algorithms not allowed for " + clientVersion);
        }
    }

    public void processServerCertificate(Certificate serverCertificate)
        throws IOException
    {
        if (supportedSignatureAlgorithms == null)
        {
            /*
             * TODO RFC 2246 7.4.2. Unless otherwise specified, the signing algorithm for the
             * certificate must be the same as the algorithm for the certificate key.
             */
        }
        else
        {
            /*
             * TODO RFC 5246 7.4.2. If the client provided a "signature_algorithms" extension, then
             * all certificates provided by the server MUST be signed by a hash/signature algorithm
             * pair that appears in that extension.
             */
        }
    }

    public void processServerCredentials(TlsCredentials serverCredentials)
        throws IOException
    {
        processServerCertificate(serverCredentials.getCertificate());
    }

    public boolean requiresServerKeyExchange()
    {
        return false;
    }

    public byte[] generateServerKeyExchange()
        throws IOException
    {
        if (requiresServerKeyExchange())
        {
            throw new TlsFatalAlert(AlertDescription.internal_error);
        }
        return null;
    }

    public void skipServerKeyExchange()
        throws IOException
    {
        if (requiresServerKeyExchange())
        {
            throw new TlsFatalAlert(AlertDescription.unexpected_message);
        }
    }

    public void processServerKeyExchange(InputStream input)
        throws IOException
    {
        if (!requiresServerKeyExchange())
        {
            throw new TlsFatalAlert(AlertDescription.unexpected_message);
        }
    }

    public void skipClientCredentials()
        throws IOException
    {
    }

    public void processClientCertificate(Certificate clientCertificate)
        throws IOException
    {
    }

    public void processClientKeyExchange(InputStream input)
        throws IOException
    {
        // Key exchange implementation MUST support client key exchange
        throw new TlsFatalAlert(AlertDescription.internal_error);
    }
}
