using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class TlsProtocol
{
private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
/*
* Our Connection states
*/
protected const short CS_START = 0;
protected const short CS_CLIENT_HELLO = 1;
protected const short CS_SERVER_HELLO = 2;
protected const short CS_SERVER_SUPPLEMENTAL_DATA = 3;
protected const short CS_SERVER_CERTIFICATE = 4;
protected const short CS_CERTIFICATE_STATUS = 5;
protected const short CS_SERVER_KEY_EXCHANGE = 6;
protected const short CS_CERTIFICATE_REQUEST = 7;
protected const short CS_SERVER_HELLO_DONE = 8;
protected const short CS_CLIENT_SUPPLEMENTAL_DATA = 9;
protected const short CS_CLIENT_CERTIFICATE = 10;
protected const short CS_CLIENT_KEY_EXCHANGE = 11;
protected const short CS_CERTIFICATE_VERIFY = 12;
protected const short CS_CLIENT_FINISHED = 13;
protected const short CS_SERVER_SESSION_TICKET = 14;
protected const short CS_SERVER_FINISHED = 15;
protected const short CS_END = 16;
/*
* Queues for data from some protocols.
*/
private ByteQueue mApplicationDataQueue = new ByteQueue();
private ByteQueue mAlertQueue = new ByteQueue(2);
private ByteQueue mHandshakeQueue = new ByteQueue();
// private ByteQueue mHeartbeatQueue = new ByteQueue();
/*
* The Record Stream we use
*/
internal RecordStream mRecordStream;
protected SecureRandom mSecureRandom;
private TlsStream mTlsStream = null;
private volatile bool mClosed = false;
private volatile bool mFailedWithError = false;
private volatile bool mAppDataReady = false;
private volatile bool mSplitApplicationDataRecords = true;
private byte[] mExpectedVerifyData = null;
protected TlsSession mTlsSession = null;
protected SessionParameters mSessionParameters = null;
protected SecurityParameters mSecurityParameters = null;
protected Certificate mPeerCertificate = null;
protected int[] mOfferedCipherSuites = null;
protected byte[] mOfferedCompressionMethods = null;
protected IDictionary mClientExtensions = null;
protected IDictionary mServerExtensions = null;
protected short mConnectionState = CS_START;
protected bool mResumedSession = false;
protected bool mReceivedChangeCipherSpec = false;
protected bool mSecureRenegotiation = false;
protected bool mAllowCertificateStatus = false;
protected bool mExpectSessionTicket = false;
public TlsProtocol(Stream stream, SecureRandom secureRandom)
: this(stream, stream, secureRandom)
{
}
public TlsProtocol(Stream input, Stream output, SecureRandom secureRandom)
{
this.mRecordStream = new RecordStream(this, input, output);
this.mSecureRandom = secureRandom;
}
protected abstract TlsContext Context { get; }
internal abstract AbstractTlsContext ContextAdmin { get; }
protected abstract TlsPeer Peer { get; }
protected virtual void HandleChangeCipherSpecMessage()
{
}
protected abstract void HandleHandshakeMessage(byte type, byte[] buf);
protected virtual void HandleWarningMessage(byte description)
{
}
protected virtual void CleanupHandshake()
{
if (this.mExpectedVerifyData != null)
{
Arrays.Fill(this.mExpectedVerifyData, (byte)0);
this.mExpectedVerifyData = null;
}
this.mSecurityParameters.Clear();
this.mPeerCertificate = null;
this.mOfferedCipherSuites = null;
this.mOfferedCompressionMethods = null;
this.mClientExtensions = null;
this.mServerExtensions = null;
this.mResumedSession = false;
this.mReceivedChangeCipherSpec = false;
this.mSecureRenegotiation = false;
this.mAllowCertificateStatus = false;
this.mExpectSessionTicket = false;
}
protected virtual void CompleteHandshake()
{
try
{
/*
* We will now read data, until we have completed the handshake.
*/
while (this.mConnectionState != CS_END)
{
if (this.mClosed)
{
// TODO What kind of exception/alert?
}
SafeReadRecord();
}
this.mRecordStream.FinaliseHandshake();
this.mSplitApplicationDataRecords = !TlsUtilities.IsTlsV11(Context);
/*
* If this was an initial handshake, we are now ready to send and receive application data.
*/
if (!mAppDataReady)
{
this.mAppDataReady = true;
this.mTlsStream = new TlsStream(this);
}
if (this.mTlsSession != null)
{
if (this.mSessionParameters == null)
{
this.mSessionParameters = new SessionParameters.Builder()
.SetCipherSuite(this.mSecurityParameters.cipherSuite)
.SetCompressionAlgorithm(this.mSecurityParameters.compressionAlgorithm)
.SetMasterSecret(this.mSecurityParameters.masterSecret)
.SetPeerCertificate(this.mPeerCertificate)
// TODO Consider filtering extensions that aren't relevant to resumed sessions
.SetServerExtensions(this.mServerExtensions)
.Build();
this.mTlsSession = new TlsSessionImpl(this.mTlsSession.SessionID, this.mSessionParameters);
}
ContextAdmin.SetResumableSession(this.mTlsSession);
}
Peer.NotifyHandshakeComplete();
}
finally
{
CleanupHandshake();
}
}
protected internal void ProcessRecord(byte protocol, byte[] buf, int offset, int len)
{
/*
* Have a look at the protocol type, and add it to the correct queue.
*/
switch (protocol)
{
case ContentType.alert:
{
mAlertQueue.AddData(buf, offset, len);
ProcessAlert();
break;
}
case ContentType.application_data:
{
if (!mAppDataReady)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
mApplicationDataQueue.AddData(buf, offset, len);
ProcessApplicationData();
break;
}
case ContentType.change_cipher_spec:
{
ProcessChangeCipherSpec(buf, offset, len);
break;
}
case ContentType.handshake:
{
mHandshakeQueue.AddData(buf, offset, len);
ProcessHandshake();
break;
}
case ContentType.heartbeat:
{
if (!mAppDataReady)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
// TODO[RFC 6520]
// mHeartbeatQueue.AddData(buf, offset, len);
// ProcessHeartbeat();
break;
}
default:
/*
* Uh, we don't know this protocol.
*
* RFC2246 defines on page 13, that we should ignore this.
*/
break;
}
}
private void ProcessHandshake()
{
bool read;
do
{
read = false;
/*
* We need the first 4 bytes, they contain type and length of the message.
*/
if (mHandshakeQueue.Available >= 4)
{
byte[] beginning = new byte[4];
mHandshakeQueue.Read(beginning, 0, 4, 0);
byte type = TlsUtilities.ReadUint8(beginning, 0);
int len = TlsUtilities.ReadUint24(beginning, 1);
/*
* Check if we have enough bytes in the buffer to read the full message.
*/
if (mHandshakeQueue.Available >= (len + 4))
{
/*
* Read the message.
*/
byte[] buf = mHandshakeQueue.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:
default:
if (type == HandshakeType.finished && this.mExpectedVerifyData == null)
{
this.mExpectedVerifyData = CreateVerifyData(!Context.IsServer);
}
mRecordStream.UpdateHandshakeData(beginning, 0, 4);
mRecordStream.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()
{
while (mAlertQueue.Available >= 2)
{
/*
* An alert is always 2 bytes. Read the alert.
*/
byte[] tmp = mAlertQueue.RemoveData(2, 0);
byte level = tmp[0];
byte description = tmp[1];
Peer.NotifyAlertReceived(level, description);
if (level == AlertLevel.fatal)
{
/*
* RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
* without proper close_notify messages with level equal to warning.
*/
InvalidateSession();
this.mFailedWithError = true;
this.mClosed = true;
mRecordStream.SafeClose();
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(byte[] buf, int off, int len)
{
for (int i = 0; i < len; ++i)
{
byte message = TlsUtilities.ReadUint8(buf, off + i);
if (message != ChangeCipherSpec.change_cipher_spec)
throw new TlsFatalAlert(AlertDescription.decode_error);
if (this.mReceivedChangeCipherSpec
|| mAlertQueue.Available > 0
|| mHandshakeQueue.Available > 0)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
mRecordStream.ReceivedReadCipherSpec();
this.mReceivedChangeCipherSpec = true;
HandleChangeCipherSpecMessage();
}
}
protected internal virtual int ApplicationDataAvailable()
{
return mApplicationDataQueue.Available;
}
/**
* 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 internal virtual int ReadApplicationData(byte[] buf, int offset, int len)
{
if (len < 1)
return 0;
while (mApplicationDataQueue.Available == 0)
{
/*
* We need to read some data.
*/
if (this.mClosed)
{
if (this.mFailedWithError)
{
/*
* 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 0;
}
SafeReadRecord();
}
len = System.Math.Min(len, mApplicationDataQueue.Available);
mApplicationDataQueue.RemoveData(buf, offset, len, 0);
return len;
}
protected virtual void SafeReadRecord()
{
try
{
if (!mRecordStream.ReadRecord())
{
// TODO It would be nicer to allow graceful connection close if between records
// this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
throw new EndOfStreamException();
}
}
catch (TlsFatalAlert e)
{
if (!mClosed)
{
this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to read record", e);
}
throw e;
}
catch (Exception e)
{
if (!mClosed)
{
this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
}
throw e;
}
}
protected virtual void SafeWriteRecord(byte type, byte[] buf, int offset, int len)
{
try
{
mRecordStream.WriteRecord(type, buf, offset, len);
}
catch (TlsFatalAlert e)
{
if (!mClosed)
{
this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to write record", e);
}
throw e;
}
catch (Exception e)
{
if (!mClosed)
{
this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
}
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 internal virtual void WriteData(byte[] buf, int offset, int len)
{
if (this.mClosed)
{
if (this.mFailedWithError)
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.
*
* NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting.
*/
if (this.mSplitApplicationDataRecords)
{
/*
* Protect against known IV attack!
*
* DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
*/
SafeWriteRecord(ContentType.application_data, buf, offset, 1);
++offset;
--len;
}
if (len > 0)
{
// Fragment data according to the current fragment limit.
int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit());
SafeWriteRecord(ContentType.application_data, buf, offset, toWrite);
offset += toWrite;
len -= toWrite;
}
}
}
protected virtual void WriteHandshakeMessage(byte[] buf, int off, int len)
{
while (len > 0)
{
// Fragment data according to the current fragment limit.
int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit());
SafeWriteRecord(ContentType.handshake, buf, off, toWrite);
off += toWrite;
len -= toWrite;
}
}
/// The secure bidirectional stream for this connection
public virtual Stream Stream
{
get { return this.mTlsStream; }
}
/**
* Terminate this connection with an alert. Can be used for normal closure too.
*
* @param alertLevel
* See {@link AlertLevel} for values.
* @param alertDescription
* See {@link AlertDescription} for values.
* @throws IOException
* If alert was fatal.
*/
protected virtual void FailWithError(byte alertLevel, byte alertDescription, string message, Exception cause)
{
/*
* Check if the connection is still open.
*/
if (!mClosed)
{
/*
* Prepare the message
*/
this.mClosed = true;
if (alertLevel == AlertLevel.fatal)
{
/*
* RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
* without proper close_notify messages with level equal to warning.
*/
// TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete.
InvalidateSession();
this.mFailedWithError = true;
}
RaiseAlert(alertLevel, alertDescription, message, cause);
mRecordStream.SafeClose();
if (alertLevel != AlertLevel.fatal)
{
return;
}
}
throw new IOException(TLS_ERROR_MESSAGE);
}
protected virtual void InvalidateSession()
{
if (this.mSessionParameters != null)
{
this.mSessionParameters.Clear();
this.mSessionParameters = null;
}
if (this.mTlsSession != null)
{
this.mTlsSession.Invalidate();
this.mTlsSession = null;
}
}
protected virtual void ProcessFinishedMessage(MemoryStream buf)
{
byte[] verify_data = TlsUtilities.ReadFully(mExpectedVerifyData.Length, buf);
AssertEmpty(buf);
/*
* Compare both checksums.
*/
if (!Arrays.ConstantTimeAreEqual(mExpectedVerifyData, verify_data))
{
/*
* Wrong checksum in the finished message.
*/
throw new TlsFatalAlert(AlertDescription.decrypt_error);
}
}
protected virtual void RaiseAlert(byte alertLevel, byte alertDescription, string message, Exception cause)
{
Peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause);
byte[] error = new byte[]{ alertLevel, alertDescription };
SafeWriteRecord(ContentType.alert, error, 0, 2);
}
protected virtual void RaiseWarning(byte alertDescription, string message)
{
RaiseAlert(AlertLevel.warning, alertDescription, message, null);
}
protected virtual void SendCertificateMessage(Certificate certificate)
{
if (certificate == null)
{
certificate = Certificate.EmptyChain;
}
if (certificate.IsEmpty)
{
TlsContext context = Context;
if (!context.IsServer)
{
ProtocolVersion serverVersion = Context.ServerVersion;
if (serverVersion.IsSsl)
{
string errorMessage = serverVersion.ToString() + " client didn't provide credentials";
RaiseWarning(AlertDescription.no_certificate, errorMessage);
return;
}
}
}
HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate);
certificate.Encode(message);
message.WriteToRecordStream(this);
}
protected virtual void SendChangeCipherSpecMessage()
{
byte[] message = new byte[]{ 1 };
SafeWriteRecord(ContentType.change_cipher_spec, message, 0, message.Length);
mRecordStream.SentWriteCipherSpec();
}
protected virtual void SendFinishedMessage()
{
byte[] verify_data = CreateVerifyData(Context.IsServer);
HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.Length);
message.Write(verify_data, 0, verify_data.Length);
message.WriteToRecordStream(this);
}
protected virtual void SendSupplementalDataMessage(IList supplementalData)
{
HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data);
WriteSupplementalData(message, supplementalData);
message.WriteToRecordStream(this);
}
protected virtual byte[] CreateVerifyData(bool isServer)
{
TlsContext context = Context;
string asciiLabel = isServer ? ExporterLabel.server_finished : ExporterLabel.client_finished;
byte[] sslSender = isServer ? TlsUtilities.SSL_SERVER : TlsUtilities.SSL_CLIENT;
byte[] hash = GetCurrentPrfHash(context, mRecordStream.HandshakeHash, sslSender);
return TlsUtilities.CalculateVerifyData(context, asciiLabel, hash);
}
/**
* Closes this connection.
*
* @throws IOException If something goes wrong during closing.
*/
public virtual void Close()
{
HandleClose(true);
}
protected virtual void HandleClose(bool user_canceled)
{
if (!mClosed)
{
if (user_canceled && !mAppDataReady)
{
RaiseWarning(AlertDescription.user_canceled, "User canceled handshake");
}
this.FailWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null);
}
}
protected internal virtual void Flush()
{
mRecordStream.Flush();
}
protected internal virtual bool IsClosed
{
get { return mClosed; }
}
protected virtual short ProcessMaxFragmentLengthExtension(IDictionary clientExtensions, IDictionary serverExtensions,
byte alertDescription)
{
short maxFragmentLength = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(serverExtensions);
if (maxFragmentLength >= 0 && !this.mResumedSession)
{
if (maxFragmentLength != TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions))
throw new TlsFatalAlert(alertDescription);
}
return maxFragmentLength;
}
/**
* Make sure the InputStream 'buf' now empty. Fail otherwise.
*
* @param buf The InputStream to check.
* @throws IOException If 'buf' is not empty.
*/
protected internal static void AssertEmpty(MemoryStream buf)
{
if (buf.Position < buf.Length)
throw new TlsFatalAlert(AlertDescription.decode_error);
}
protected internal static byte[] CreateRandomBlock(bool useGmtUnixTime, IRandomGenerator randomGenerator)
{
byte[] result = new byte[32];
randomGenerator.NextBytes(result);
if (useGmtUnixTime)
{
TlsUtilities.WriteGmtUnixTime(result, 0);
}
return result;
}
protected internal static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
{
return TlsUtilities.EncodeOpaque8(renegotiated_connection);
}
protected internal static void EstablishMasterSecret(TlsContext context, TlsKeyExchange keyExchange)
{
byte[] pre_master_secret = keyExchange.GeneratePremasterSecret();
try
{
context.SecurityParameters.masterSecret = TlsUtilities.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);
}
}
}
/**
* 'sender' only relevant to SSLv3
*/
protected internal static byte[] GetCurrentPrfHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender)
{
IDigest d = handshakeHash.ForkPrfHash();
if (sslSender != null && TlsUtilities.IsSsl(context))
{
d.BlockUpdate(sslSender, 0, sslSender.Length);
}
return DigestUtilities.DoFinal(d);
}
protected internal static IDictionary ReadExtensions(MemoryStream input)
{
if (input.Position >= input.Length)
return null;
byte[] extBytes = TlsUtilities.ReadOpaque16(input);
AssertEmpty(input);
MemoryStream buf = new MemoryStream(extBytes, false);
// Integer -> byte[]
IDictionary extensions = Platform.CreateHashtable();
while (buf.Position < buf.Length)
{
int extension_type = TlsUtilities.ReadUint16(buf);
byte[] extension_data = TlsUtilities.ReadOpaque16(buf);
/*
* RFC 3546 2.3 There MUST NOT be more than one extension of the same type.
*/
if (extensions.Contains(extension_type))
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
extensions.Add(extension_type, extension_data);
}
return extensions;
}
protected internal static IList ReadSupplementalDataMessage(MemoryStream input)
{
byte[] supp_data = TlsUtilities.ReadOpaque24(input);
AssertEmpty(input);
MemoryStream buf = new MemoryStream(supp_data, false);
IList supplementalData = Platform.CreateArrayList();
while (buf.Position < buf.Length)
{
int supp_data_type = TlsUtilities.ReadUint16(buf);
byte[] data = TlsUtilities.ReadOpaque16(buf);
supplementalData.Add(new SupplementalDataEntry(supp_data_type, data));
}
return supplementalData;
}
protected internal static void WriteExtensions(Stream output, IDictionary extensions)
{
MemoryStream buf = new MemoryStream();
foreach (int extension_type in extensions.Keys)
{
byte[] extension_data = (byte[])extensions[extension_type];
TlsUtilities.CheckUint16(extension_type);
TlsUtilities.WriteUint16(extension_type, buf);
TlsUtilities.WriteOpaque16(extension_data, buf);
}
byte[] extBytes = buf.ToArray();
TlsUtilities.WriteOpaque16(extBytes, output);
}
protected internal static void WriteSupplementalData(Stream output, IList supplementalData)
{
MemoryStream buf = new MemoryStream();
foreach (SupplementalDataEntry entry in supplementalData)
{
int supp_data_type = entry.DataType;
TlsUtilities.CheckUint16(supp_data_type);
TlsUtilities.WriteUint16(supp_data_type, buf);
TlsUtilities.WriteOpaque16(entry.Data, buf);
}
byte[] supp_data = buf.ToArray();
TlsUtilities.WriteOpaque24(supp_data, output);
}
protected internal static int GetPrfAlgorithm(TlsContext context, int ciphersuite)
{
bool isTLSv12 = TlsUtilities.IsTlsV12(context);
switch (ciphersuite)
{
case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
{
if (isTLSv12)
{
return PrfAlgorithm.tls_prf_sha256;
}
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
{
if (isTLSv12)
{
return PrfAlgorithm.tls_prf_sha384;
}
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
{
if (isTLSv12)
{
return PrfAlgorithm.tls_prf_sha384;
}
return PrfAlgorithm.tls_prf_legacy;
}
default:
{
if (isTLSv12)
{
return PrfAlgorithm.tls_prf_sha256;
}
return PrfAlgorithm.tls_prf_legacy;
}
}
}
internal class HandshakeMessage
: MemoryStream
{
internal HandshakeMessage(byte handshakeType)
: this(handshakeType, 60)
{
}
internal HandshakeMessage(byte handshakeType, int length)
: base(length + 4)
{
TlsUtilities.WriteUint8(handshakeType, this);
// Reserve space for length
TlsUtilities.WriteUint24(0, this);
}
internal void Write(byte[] data)
{
Write(data, 0, data.Length);
}
internal void WriteToRecordStream(TlsProtocol protocol)
{
// Patch actual length back in
long length = Length - 4;
TlsUtilities.CheckUint24(length);
this.Position = 1;
TlsUtilities.WriteUint24((int)length, this);
protocol.WriteHandshakeMessage(GetBuffer(), 0, (int)Length);
this.Close();
}
}
}
}