All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.crypto.tls.TlsProtocol Maven / Gradle / Ivy

Go to download

The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for JDK 1.4.

There is a newer version: 1.79
Show newest version
package org.bouncycastle.crypto.tls;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Integers;

/**
 * An implementation of all high level protocols in TLS 1.0/1.1.
 */
public abstract class TlsProtocol
{

    protected static final Integer EXT_RenegotiationInfo = Integers.valueOf(ExtensionType.renegotiation_info);
    protected static final Integer EXT_SessionTicket = Integers.valueOf(ExtensionType.session_ticket);

    private static final String TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";

    /*
     * Our Connection states
     */
    protected static final short CS_START = 0;
    protected static final short CS_CLIENT_HELLO = 1;
    protected static final short CS_SERVER_HELLO = 2;
    protected static final short CS_SERVER_SUPPLEMENTAL_DATA = 3;
    protected static final short CS_SERVER_CERTIFICATE = 4;
    protected static final short CS_SERVER_KEY_EXCHANGE = 5;
    protected static final short CS_CERTIFICATE_REQUEST = 6;
    protected static final short CS_SERVER_HELLO_DONE = 7;
    protected static final short CS_CLIENT_SUPPLEMENTAL_DATA = 8;
    protected static final short CS_CLIENT_CERTIFICATE = 9;
    protected static final short CS_CLIENT_KEY_EXCHANGE = 10;
    protected static final short CS_CERTIFICATE_VERIFY = 11;
    protected static final short CS_CLIENT_CHANGE_CIPHER_SPEC = 12;
    protected static final short CS_CLIENT_FINISHED = 13;
    protected static final short CS_SERVER_SESSION_TICKET = 14;
    protected static final short CS_SERVER_CHANGE_CIPHER_SPEC = 15;
    protected static final short CS_SERVER_FINISHED = 16;

    /*
     * Queues for data from some protocols.
     */
    private ByteQueue applicationDataQueue = new ByteQueue();
    private ByteQueue changeCipherSpecQueue = new ByteQueue();
    private ByteQueue alertQueue = new ByteQueue();
    private ByteQueue handshakeQueue = new ByteQueue();

    /*
     * The Record Stream we use
     */
    protected RecordStream recordStream;
    protected SecureRandom secureRandom;

    private TlsInputStream tlsInputStream = null;
    private TlsOutputStream tlsOutputStream = null;

    private volatile boolean closed = false;
    private volatile boolean failedWithError = false;
    private volatile boolean appDataReady = false;
    private volatile boolean writeExtraEmptyRecords = true;
    private byte[] expected_verify_data = null;

    protected SecurityParameters securityParameters = null;

    protected short connection_state = CS_START;
    protected boolean secure_renegotiation = false;
    protected boolean expectSessionTicket = false;

    public TlsProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
    {
        this.recordStream = new RecordStream(this, input, output);
        this.secureRandom = secureRandom;
    }

    protected abstract AbstractTlsContext getContext();

    protected abstract TlsPeer getPeer();

    protected abstract void handleChangeCipherSpecMessage()
        throws IOException;

    protected abstract void handleHandshakeMessage(short type, byte[] buf)
        throws IOException;

    protected void handleWarningMessage(short description)
        throws IOException
    {

    }

    protected void completeHandshake()
        throws IOException
    {

        this.expected_verify_data = null;

        /*
         * We will now read data, until we have completed the handshake.
         */
        while (this.connection_state != CS_SERVER_FINISHED)
        {
            safeReadRecord();
        }

        this.recordStream.finaliseHandshake();

        ProtocolVersion version = getContext().getServerVersion();
        this.writeExtraEmptyRecords = version.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10);

        /*
         * If this was an initial handshake, we are now ready to send and receive application data.
         */
        if (!appDataReady)
        {
            this.appDataReady = true;

            this.tlsInputStream = new TlsInputStream(this);
            this.tlsOutputStream = new TlsOutputStream(this);
        }
    }

    protected void processRecord(short protocol, byte[] buf, int offset, int len)
        throws IOException
    {
        /*
         * Have a look at the protocol type, and add it to the correct queue.
         */
        switch (protocol)
        {
        case ContentType.change_cipher_spec:
            changeCipherSpecQueue.addData(buf, offset, len);
            processChangeCipherSpec();
            break;
        case ContentType.alert:
            alertQueue.addData(buf, offset, len);
            processAlert();
            break;
        case ContentType.handshake:
            handshakeQueue.addData(buf, offset, len);
            processHandshake();
            break;
        case ContentType.application_data:
            if (!appDataReady)
            {
                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
            }
            applicationDataQueue.addData(buf, offset, len);
            processApplicationData();
            break;
        default:
            /*
             * Uh, we don't know this protocol.
             * 
             * RFC2246 defines on page 13, that we should ignore this.
             */
        }
    }

    private void processHandshake()
        throws IOException
    {
        boolean read;
        do
        {
            read = false;
            /*
             * We need the first 4 bytes, they contain type and length of the message.
             */
            if (handshakeQueue.size() >= 4)
            {
                byte[] beginning = new byte[4];
                handshakeQueue.read(beginning, 0, 4, 0);
                ByteArrayInputStream bis = new ByteArrayInputStream(beginning);
                short type = TlsUtils.readUint8(bis);
                int len = TlsUtils.readUint24(bis);

                /*
                 * Check if we have enough bytes in the buffer to read the full message.
                 */
                if (handshakeQueue.size() >= (len + 4))
                {
                    /*
                     * Read the message.
                     */
                    byte[] buf = new byte[len];
                    handshakeQueue.read(buf, 0, len, 4);
                    handshakeQueue.removeData(len + 4);

                    /*
                     * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
                     * starting at client hello up to, but not including, this finished message.
                     * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes.
                     */
                    switch (type)
                    {
                    case HandshakeType.hello_request:
                        break;
                    case HandshakeType.finished:
                    {

                        if (this.expected_verify_data == null)
                        {
                            this.expected_verify_data = createVerifyData(!getContext().isServer());
                        }

                        // NB: Fall through to next case label
                    }
                    default:
                        recordStream.updateHandshakeData(beginning, 0, 4);
                        recordStream.updateHandshakeData(buf, 0, len);
                        break;
                    }

                    /*
                     * Now, parse the message.
                     */
                    handleHandshakeMessage(type, buf);
                    read = true;
                }
            }
        }
        while (read);
    }

    private void processApplicationData()
    {
        /*
         * There is nothing we need to do here.
         * 
         * This function could be used for callbacks when application data arrives in the future.
         */
    }

    private void processAlert()
        throws IOException
    {
        while (alertQueue.size() >= 2)
        {
            /*
             * An alert is always 2 bytes. Read the alert.
             */
            byte[] tmp = new byte[2];
            alertQueue.read(tmp, 0, 2, 0);
            alertQueue.removeData(2);
            short level = tmp[0];
            short description = tmp[1];

            getPeer().notifyAlertReceived(level, description);

            if (level == AlertLevel.fatal)
            {

                this.failedWithError = true;
                this.closed = true;
                /*
                 * Now try to close the stream, ignore errors.
                 */
                try
                {
                    recordStream.close();
                }
                catch (Exception e)
                {

                }
                throw new IOException(TLS_ERROR_MESSAGE);
            }
            else
            {

                /*
                 * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own
                 * and close down the connection immediately, discarding any pending writes.
                 */
                // TODO Can close_notify be a fatal alert?
                if (description == AlertDescription.close_notify)
                {
                    handleClose(false);
                }

                /*
                 * If it is just a warning, we continue.
                 */
                handleWarningMessage(description);
            }
        }
    }

    /**
     * This method is called, when a change cipher spec message is received.
     *
     * @throws IOException If the message has an invalid content or the handshake is not in the correct
     * state.
     */
    private void processChangeCipherSpec()
        throws IOException
    {
        while (changeCipherSpecQueue.size() > 0)
        {
            /*
             * A change cipher spec message is only one byte with the value 1.
             */
            byte[] b = new byte[1];
            changeCipherSpecQueue.read(b, 0, 1, 0);
            changeCipherSpecQueue.removeData(1);
            if (b[0] != 1)
            {
                /*
                 * This should never happen.
                 */
                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
            }

            recordStream.receivedReadCipherSpec();

            handleChangeCipherSpecMessage();
        }
    }

    /**
     * Read data from the network. The method will return immediately, if there is still some data
     * left in the buffer, or block until some application data has been read from the network.
     *
     * @param buf    The buffer where the data will be copied to.
     * @param offset The position where the data will be placed in the buffer.
     * @param len    The maximum number of bytes to read.
     * @return The number of bytes read.
     * @throws IOException If something goes wrong during reading data.
     */
    protected int readApplicationData(byte[] buf, int offset, int len)
        throws IOException
    {

        if (len < 1)
        {
            return 0;
        }

        while (applicationDataQueue.size() == 0)
        {
            /*
             * We need to read some data.
             */
            if (this.closed)
            {
                if (this.failedWithError)
                {
                    /*
                     * Something went terribly wrong, we should throw an IOException
                     */
                    throw new IOException(TLS_ERROR_MESSAGE);
                }

                /*
                 * Connection has been closed, there is no more data to read.
                 */
                return -1;
            }

            safeReadRecord();
        }
        len = Math.min(len, applicationDataQueue.size());
        applicationDataQueue.read(buf, offset, len, 0);
        applicationDataQueue.removeData(len);
        return len;
    }

    protected void safeReadRecord()
        throws IOException
    {
        try
        {
            recordStream.readRecord();
        }
        catch (TlsFatalAlert e)
        {
            if (!this.closed)
            {
                this.failWithError(AlertLevel.fatal, e.getAlertDescription());
            }
            throw e;
        }
        catch (IOException e)
        {
            if (!this.closed)
            {
                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
            }
            throw e;
        }
        catch (RuntimeException e)
        {
            if (!this.closed)
            {
                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
            }
            throw e;
        }
    }

    protected void safeWriteRecord(short type, byte[] buf, int offset, int len)
        throws IOException
    {
        try
        {
            recordStream.writeRecord(type, buf, offset, len);
        }
        catch (TlsFatalAlert e)
        {
            if (!this.closed)
            {
                this.failWithError(AlertLevel.fatal, e.getAlertDescription());
            }
            throw e;
        }
        catch (IOException e)
        {
            if (!closed)
            {
                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
            }
            throw e;
        }
        catch (RuntimeException e)
        {
            if (!closed)
            {
                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
            }
            throw e;
        }
    }

    /**
     * Send some application data to the remote system.
     * 

* The method will handle fragmentation internally. * * @param buf The buffer with the data. * @param offset The position in the buffer where the data is placed. * @param len The length of the data. * @throws IOException If something goes wrong during sending. */ protected void writeData(byte[] buf, int offset, int len) throws IOException { if (this.closed) { if (this.failedWithError) { throw new IOException(TLS_ERROR_MESSAGE); } throw new IOException("Sorry, connection has been closed, you cannot write more data"); } while (len > 0) { /* * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are * potentially useful as a traffic analysis countermeasure. */ if (this.writeExtraEmptyRecords) { /* * Protect against known IV attack! * * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE. */ safeWriteRecord(ContentType.application_data, TlsUtils.EMPTY_BYTES, 0, 0); } /* * We are only allowed to write fragments up to 2^14 bytes. */ int toWrite = Math.min(len, 1 << 14); safeWriteRecord(ContentType.application_data, buf, offset, toWrite); offset += toWrite; len -= toWrite; } } /** * @return An OutputStream which can be used to send data. */ public OutputStream getOutputStream() { return this.tlsOutputStream; } /** * @return An InputStream which can be used to read data. */ public InputStream getInputStream() { return this.tlsInputStream; } /** * Terminate this connection with an alert. *

* Can be used for normal closure too. * * @param alertLevel The level of the alert, an be AlertLevel.fatal or AL_warning. * @param alertDescription The exact alert message. * @throws IOException If alert was fatal. */ protected void failWithError(short alertLevel, short alertDescription) throws IOException { /* * Check if the connection is still open. */ if (!closed) { /* * Prepare the message */ this.closed = true; if (alertLevel == AlertLevel.fatal) { /* * This is a fatal message. */ this.failedWithError = true; } raiseAlert(alertLevel, alertDescription, null, null); recordStream.close(); if (alertLevel == AlertLevel.fatal) { throw new IOException(TLS_ERROR_MESSAGE); } } else { throw new IOException(TLS_ERROR_MESSAGE); } } protected void processFinishedMessage(ByteArrayInputStream buf) throws IOException { byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf); assertEmpty(buf); /* * Compare both checksums. */ if (!Arrays.constantTimeAreEqual(expected_verify_data, verify_data)) { /* * Wrong checksum in the finished message. */ this.failWithError(AlertLevel.fatal, AlertDescription.decrypt_error); } } protected void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause) throws IOException { getPeer().notifyAlertRaised(alertLevel, alertDescription, message, cause); byte[] error = new byte[2]; error[0] = (byte)alertLevel; error[1] = (byte)alertDescription; safeWriteRecord(ContentType.alert, error, 0, 2); } protected void raiseWarning(short alertDescription, String message) throws IOException { raiseAlert(AlertLevel.warning, alertDescription, message, null); } protected void sendCertificateMessage(Certificate certificate) throws IOException { if (certificate == null) { certificate = Certificate.EMPTY_CHAIN; } if (certificate.getLength() == 0) { TlsContext context = getContext(); if (!context.isServer()) { ProtocolVersion serverVersion = getContext().getServerVersion(); if (serverVersion.isSSL()) { String message = serverVersion.toString() + " client didn't provide credentials"; raiseWarning(AlertDescription.no_certificate, message); return; } } } ByteArrayOutputStream bos = new ByteArrayOutputStream(); TlsUtils.writeUint8(HandshakeType.certificate, bos); // Reserve space for length TlsUtils.writeUint24(0, bos); certificate.encode(bos); byte[] message = bos.toByteArray(); // Patch actual length back in TlsUtils.writeUint24(message.length - 4, message, 1); safeWriteRecord(ContentType.handshake, message, 0, message.length); } protected void sendChangeCipherSpecMessage() throws IOException { byte[] message = new byte[]{1}; safeWriteRecord(ContentType.change_cipher_spec, message, 0, message.length); recordStream.sentWriteCipherSpec(); } protected void sendFinishedMessage() throws IOException { byte[] verify_data = createVerifyData(getContext().isServer()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); TlsUtils.writeUint8(HandshakeType.finished, bos); TlsUtils.writeUint24(verify_data.length, bos); bos.write(verify_data); byte[] message = bos.toByteArray(); safeWriteRecord(ContentType.handshake, message, 0, message.length); } protected void sendSupplementalDataMessage(Vector supplementalData) throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); TlsUtils.writeUint8(HandshakeType.supplemental_data, buf); // Reserve space for length TlsUtils.writeUint24(0, buf); writeSupplementalData(buf, supplementalData); byte[] message = buf.toByteArray(); // Patch actual length back in TlsUtils.writeUint24(message.length - 4, message, 1); safeWriteRecord(ContentType.handshake, message, 0, message.length); } protected byte[] createVerifyData(boolean isServer) { TlsContext context = getContext(); if (isServer) { return TlsUtils.calculateVerifyData(context, "server finished", recordStream.getCurrentHash(TlsUtils.SSL_SERVER)); } return TlsUtils.calculateVerifyData(context, "client finished", recordStream.getCurrentHash(TlsUtils.SSL_CLIENT)); } /** * Closes this connection. * * @throws IOException If something goes wrong during closing. */ public void close() throws IOException { handleClose(true); } protected void handleClose(boolean user_canceled) throws IOException { if (!closed) { if (user_canceled && !appDataReady) { raiseWarning(AlertDescription.user_canceled, "User canceled handshake"); } this.failWithError(AlertLevel.warning, AlertDescription.close_notify); } } protected void flush() throws IOException { recordStream.flush(); } protected static boolean arrayContains(short[] a, short n) { for (int i = 0; i < a.length; ++i) { if (a[i] == n) { return true; } } return false; } protected static boolean arrayContains(int[] a, int n) { for (int i = 0; i < a.length; ++i) { if (a[i] == n) { return true; } } return false; } /** * Make sure the InputStream 'buf' now empty. Fail otherwise. * * @param buf The InputStream to check. * @throws IOException If 'buf' is not empty. */ protected static void assertEmpty(ByteArrayInputStream buf) throws IOException { if (buf.available() > 0) { throw new TlsFatalAlert(AlertDescription.decode_error); } } protected static byte[] createRandomBlock(SecureRandom random) { byte[] result = new byte[32]; random.nextBytes(result); TlsUtils.writeGMTUnixTime(result, 0); return result; } protected static byte[] createRenegotiationInfo(byte[] renegotiated_connection) throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); TlsUtils.writeOpaque8(renegotiated_connection, buf); return buf.toByteArray(); } protected static void establishMasterSecret(TlsContext context, TlsKeyExchange keyExchange) throws IOException { byte[] pre_master_secret = keyExchange.generatePremasterSecret(); try { context.getSecurityParameters().masterSecret = TlsUtils.calculateMasterSecret(context, pre_master_secret); } finally { // TODO Is there a way to ensure the data is really overwritten? /* * RFC 2246 8.1. The pre_master_secret should be deleted from memory once the * master_secret has been computed. */ if (pre_master_secret != null) { Arrays.fill(pre_master_secret, (byte)0); } } } protected static Hashtable readExtensions(ByteArrayInputStream input) throws IOException { if (input.available() < 1) { return null; } byte[] extBytes = TlsUtils.readOpaque16(input); assertEmpty(input); ByteArrayInputStream buf = new ByteArrayInputStream(extBytes); // Integer -> byte[] Hashtable extensions = new Hashtable(); while (buf.available() > 0) { Integer extType = Integers.valueOf(TlsUtils.readUint16(buf)); byte[] extValue = TlsUtils.readOpaque16(buf); /* * RFC 3546 2.3 There MUST NOT be more than one extension of the same type. */ if (null != extensions.put(extType, extValue)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } return extensions; } protected static Vector readSupplementalDataMessage(ByteArrayInputStream input) throws IOException { byte[] supp_data = TlsUtils.readOpaque24(input); assertEmpty(input); ByteArrayInputStream buf = new ByteArrayInputStream(supp_data); Vector supplementalData = new Vector(); while (buf.available() > 0) { int supp_data_type = TlsUtils.readUint16(buf); byte[] data = TlsUtils.readOpaque16(buf); supplementalData.addElement(new SupplementalDataEntry(supp_data_type, data)); } return supplementalData; } protected static void writeExtensions(OutputStream output, Hashtable extensions) throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); Enumeration keys = extensions.keys(); while (keys.hasMoreElements()) { Integer extType = (Integer)keys.nextElement(); byte[] extValue = (byte[])extensions.get(extType); TlsUtils.writeUint16(extType.intValue(), buf); TlsUtils.writeOpaque16(extValue, buf); } byte[] extBytes = buf.toByteArray(); TlsUtils.writeOpaque16(extBytes, output); } protected static void writeSupplementalData(OutputStream output, Vector supplementalData) throws IOException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); for (int i = 0; i < supplementalData.size(); ++i) { SupplementalDataEntry entry = (SupplementalDataEntry)supplementalData.elementAt(i); TlsUtils.writeUint16(entry.getDataType(), buf); TlsUtils.writeOpaque16(entry.getData(), buf); } byte[] supp_data = buf.toByteArray(); TlsUtils.writeOpaque24(supp_data, output); } protected static int getPRFAlgorithm(int ciphersuite) { switch (ciphersuite) { case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: case CipherSuite.TLS_RSA_WITH_NULL_SHA256: return PRFAlgorithm.tls_prf_sha256; case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: return PRFAlgorithm.tls_prf_sha384; default: return PRFAlgorithm.tls_prf_legacy; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy