TlsProtocol
using Org.BouncyCastle.Tls.Crypto;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;
using System;
using System.Collections.Generic;
using System.IO;
namespace Org.BouncyCastle.Tls
{
public abstract class TlsProtocol : TlsCloseable
{
protected const short CS_START = 0;
protected const short CS_CLIENT_HELLO = 1;
protected const short CS_SERVER_HELLO_RETRY_REQUEST = 2;
protected const short CS_CLIENT_HELLO_RETRY = 3;
protected const short CS_SERVER_HELLO = 4;
protected const short CS_SERVER_ENCRYPTED_EXTENSIONS = 5;
protected const short CS_SERVER_SUPPLEMENTAL_DATA = 6;
protected const short CS_SERVER_CERTIFICATE = 7;
protected const short CS_SERVER_CERTIFICATE_STATUS = 8;
protected const short CS_SERVER_CERTIFICATE_VERIFY = 9;
protected const short CS_SERVER_KEY_EXCHANGE = 10;
protected const short CS_SERVER_CERTIFICATE_REQUEST = 11;
protected const short CS_SERVER_HELLO_DONE = 12;
protected const short CS_CLIENT_END_OF_EARLY_DATA = 13;
protected const short CS_CLIENT_SUPPLEMENTAL_DATA = 14;
protected const short CS_CLIENT_CERTIFICATE = 15;
protected const short CS_CLIENT_KEY_EXCHANGE = 16;
protected const short CS_CLIENT_CERTIFICATE_VERIFY = 17;
protected const short CS_CLIENT_FINISHED = 18;
protected const short CS_SERVER_SESSION_TICKET = 19;
protected const short CS_SERVER_FINISHED = 20;
protected const short CS_END = 21;
protected const short ADS_MODE_1_Nsub1 = 0;
protected const short ADS_MODE_0_N = 1;
protected const short ADS_MODE_0_N_FIRSTONLY = 2;
private readonly ByteQueue m_applicationDataQueue = new ByteQueue(0);
private readonly ByteQueue m_alertQueue = new ByteQueue(2);
private readonly ByteQueue m_handshakeQueue = new ByteQueue(0);
internal readonly RecordStream m_recordStream;
internal readonly object m_recordWriteLock = new object();
private int m_maxHandshakeMessageSize = -1;
internal TlsHandshakeHash m_handshakeHash;
private TlsStream m_tlsStream;
private volatile bool m_closed;
private volatile bool m_failedWithError;
private volatile bool m_appDataReady;
private volatile bool m_appDataSplitEnabled = true;
private volatile bool m_keyUpdateEnabled;
private volatile bool m_keyUpdatePendingSend;
private volatile bool m_resumableHandshake;
private volatile int m_appDataSplitMode;
protected TlsSession m_tlsSession;
protected SessionParameters m_sessionParameters;
protected TlsSecret m_sessionMasterSecret;
protected byte[] m_retryCookie;
protected int m_retryGroup = -1;
protected IDictionary<int, byte[]> m_clientExtensions;
protected IDictionary<int, byte[]> m_serverExtensions;
protected short m_connectionState;
protected bool m_selectedPsk13;
protected bool m_receivedChangeCipherSpec;
protected bool m_expectSessionTicket;
protected readonly bool m_blocking;
protected readonly ByteQueueInputStream m_inputBuffers;
protected readonly ByteQueueOutputStream m_outputBuffer;
protected abstract TlsContext Context { get; }
internal abstract AbstractTlsContext ContextAdmin { get; }
protected abstract TlsPeer Peer { get; }
public virtual int ApplicationDataAvailable => m_applicationDataQueue.Available;
public virtual int AppDataSplitMode {
get {
return m_appDataSplitMode;
}
set {
if (value < 0 || value > 2)
throw new InvalidOperationException("Illegal appDataSplitMode mode: " + value.ToString());
m_appDataSplitMode = value;
}
}
public virtual bool IsResumableHandshake {
get {
return m_resumableHandshake;
}
set {
m_resumableHandshake = value;
}
}
public virtual Stream Stream {
get {
if (!m_blocking)
throw new InvalidOperationException("Cannot use Stream in non-blocking mode! Use OfferInput()/OfferOutput() instead.");
return m_tlsStream;
}
}
public virtual int ApplicationDataLimit => m_recordStream.PlaintextLimit;
internal bool IsApplicationDataReady => m_appDataReady;
public virtual bool IsClosed => m_closed;
public virtual bool IsConnected {
get {
if (m_closed)
return false;
return ContextAdmin?.IsConnected ?? false;
}
}
public virtual bool IsHandshaking {
get {
if (m_closed)
return false;
return ContextAdmin?.IsHandshaking ?? false;
}
}
protected bool IsLegacyConnectionState()
{
switch (m_connectionState) {
case 0:
case 1:
case 4:
case 6:
case 7:
case 8:
case 10:
case 11:
case 12:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
return true;
default:
return false;
}
}
protected bool IsTlsV13ConnectionState()
{
switch (m_connectionState) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 7:
case 9:
case 11:
case 13:
case 15:
case 17:
case 18:
case 20:
case 21:
return true;
default:
return false;
}
}
protected TlsProtocol()
{
m_blocking = false;
m_inputBuffers = new ByteQueueInputStream();
m_outputBuffer = new ByteQueueOutputStream();
m_recordStream = new RecordStream(this, m_inputBuffers, m_outputBuffer);
}
public TlsProtocol(Stream stream)
: this(stream, stream)
{
}
public TlsProtocol(Stream input, Stream output)
{
m_blocking = true;
m_inputBuffers = null;
m_outputBuffer = null;
m_recordStream = new RecordStream(this, input, output);
}
public virtual void ResumeHandshake()
{
if (!m_blocking)
throw new InvalidOperationException("Cannot use ResumeHandshake() in non-blocking mode!");
if (!IsHandshaking)
throw new InvalidOperationException("No handshake in progress");
BlockForHandshake();
}
protected virtual void CloseConnection()
{
m_recordStream.Close();
}
protected virtual void HandleAlertMessage(short alertLevel, short alertDescription)
{
Peer.NotifyAlertReceived(alertLevel, alertDescription);
if (alertLevel == 1) {
HandleAlertWarningMessage(alertDescription);
return;
}
HandleFailure();
throw new TlsFatalAlertReceived(alertDescription);
}
protected virtual void HandleAlertWarningMessage(short alertDescription)
{
switch (alertDescription) {
case 0:
if (!m_appDataReady)
throw new TlsFatalAlert(40);
HandleClose(false);
break;
case 41:
throw new TlsFatalAlert(10);
case 100:
throw new TlsFatalAlert(40);
}
}
protected virtual void HandleChangeCipherSpecMessage()
{
}
protected virtual void HandleClose(bool user_canceled)
{
if (!m_closed) {
m_closed = true;
if (!m_appDataReady) {
CleanupHandshake();
if (user_canceled)
RaiseAlertWarning(90, "User canceled handshake");
}
RaiseAlertWarning(0, "Connection closed");
CloseConnection();
TlsUtilities.NotifyConnectionClosed(Peer);
}
}
protected virtual void HandleException(short alertDescription, string message, Exception e)
{
if (!m_closed) {
RaiseAlertFatal(alertDescription, message, e);
HandleFailure();
}
}
protected virtual void HandleFailure()
{
m_closed = true;
m_failedWithError = true;
InvalidateSession();
if (!m_appDataReady)
CleanupHandshake();
CloseConnection();
TlsUtilities.NotifyConnectionClosed(Peer);
}
protected abstract void HandleHandshakeMessage(short type, HandshakeMessageInput buf);
protected virtual void ApplyMaxFragmentLengthExtension(short maxFragmentLength)
{
if (maxFragmentLength >= 0) {
if (!MaxFragmentLength.IsValid(maxFragmentLength))
throw new TlsFatalAlert(80);
int plaintextLimit = 1 << 8 + maxFragmentLength;
m_recordStream.SetPlaintextLimit(plaintextLimit);
}
}
protected virtual void CheckReceivedChangeCipherSpec(bool expected)
{
if (expected != m_receivedChangeCipherSpec)
throw new TlsFatalAlert(10);
}
protected virtual void BlockForHandshake()
{
while (true) {
if (m_connectionState == 21)
return;
if (IsClosed)
break;
SafeReadRecord();
}
throw new TlsFatalAlert(80);
}
protected virtual void BeginHandshake()
{
AbstractTlsContext contextAdmin = ContextAdmin;
TlsPeer peer = Peer;
m_maxHandshakeMessageSize = System.Math.Max(1024, peer.GetMaxHandshakeMessageSize());
m_handshakeHash = new DeferredHash(contextAdmin);
m_connectionState = 0;
m_selectedPsk13 = false;
contextAdmin.HandshakeBeginning(peer);
contextAdmin.SecurityParameters.m_extendedPadding = peer.ShouldUseExtendedPadding();
}
protected virtual void CleanupHandshake()
{
Context?.SecurityParameters?.Clear();
m_tlsSession = null;
m_sessionParameters = null;
m_sessionMasterSecret = null;
m_retryCookie = null;
m_retryGroup = -1;
m_clientExtensions = null;
m_serverExtensions = null;
m_selectedPsk13 = false;
m_receivedChangeCipherSpec = false;
m_expectSessionTicket = false;
}
protected virtual void CompleteHandshake()
{
try {
AbstractTlsContext contextAdmin = ContextAdmin;
SecurityParameters securityParameters = contextAdmin.SecurityParameters;
if (!contextAdmin.IsHandshaking || securityParameters.LocalVerifyData == null || securityParameters.PeerVerifyData == null)
throw new TlsFatalAlert(80);
m_recordStream.FinaliseHandshake();
m_connectionState = 21;
m_handshakeHash = new DeferredHash(contextAdmin);
m_alertQueue.Shrink();
m_handshakeQueue.Shrink();
ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
m_appDataSplitEnabled = !TlsUtilities.IsTlsV11(negotiatedVersion);
m_appDataReady = true;
m_keyUpdateEnabled = TlsUtilities.IsTlsV13(negotiatedVersion);
if (m_blocking)
m_tlsStream = new TlsStream(this);
if (m_sessionParameters == null) {
m_sessionMasterSecret = securityParameters.MasterSecret;
m_sessionParameters = new SessionParameters.Builder().SetCipherSuite(securityParameters.CipherSuite).SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret).SetLocalCertificate(securityParameters.LocalCertificate)
.SetMasterSecret(contextAdmin.Crypto.AdoptSecret(m_sessionMasterSecret))
.SetNegotiatedVersion(securityParameters.NegotiatedVersion)
.SetPeerCertificate(securityParameters.PeerCertificate)
.SetPskIdentity(securityParameters.PskIdentity)
.SetSrpIdentity(securityParameters.SrpIdentity)
.SetServerExtensions(m_serverExtensions)
.Build();
m_tlsSession = TlsUtilities.ImportSession(securityParameters.SessionID, m_sessionParameters);
} else {
securityParameters.m_localCertificate = m_sessionParameters.LocalCertificate;
securityParameters.m_peerCertificate = m_sessionParameters.PeerCertificate;
securityParameters.m_pskIdentity = m_sessionParameters.PskIdentity;
securityParameters.m_srpIdentity = m_sessionParameters.SrpIdentity;
}
contextAdmin.HandshakeComplete(Peer, m_tlsSession);
} finally {
CleanupHandshake();
}
}
internal void ProcessRecord(short protocol, byte[] buf, int off, int len)
{
switch (protocol) {
case 21:
m_alertQueue.AddData(buf, off, len);
ProcessAlertQueue();
break;
case 23:
if (!m_appDataReady)
throw new TlsFatalAlert(10);
m_applicationDataQueue.AddData(buf, off, len);
ProcessApplicationDataQueue();
break;
case 20:
ProcessChangeCipherSpec(buf, off, len);
break;
case 22:
if (m_handshakeQueue.Available > 0) {
m_handshakeQueue.AddData(buf, off, len);
ProcessHandshakeQueue(m_handshakeQueue);
} else {
ByteQueue byteQueue = new ByteQueue(buf, off, len);
ProcessHandshakeQueue(byteQueue);
int available = byteQueue.Available;
if (available > 0)
m_handshakeQueue.AddData(buf, off + len - available, available);
}
break;
default:
throw new TlsFatalAlert(10);
}
}
private void ProcessHandshakeQueue(ByteQueue queue)
{
short num2;
int num3;
while (true) {
if (queue.Available < 4)
return;
int num = queue.ReadInt32();
num2 = (short)((uint)num >> 24);
if (!HandshakeType.IsRecognized(num2))
throw new TlsFatalAlert(10, "Handshake message of unrecognized type: " + num2.ToString());
num3 = (num & 16777215);
if (num3 > m_maxHandshakeMessageSize)
break;
int num4 = 4 + num3;
if (queue.Available < num4)
return;
if (num2 != 0) {
ProtocolVersion serverVersion = Context.ServerVersion;
if (serverVersion == null || !TlsUtilities.IsTlsV13(serverVersion))
CheckReceivedChangeCipherSpec(20 == num2);
}
HandshakeMessageInput handshakeMessageInput = queue.ReadHandshakeMessage(num4);
switch (num2) {
case 4: {
ProtocolVersion serverVersion2 = Context.ServerVersion;
if (serverVersion2 != null && !TlsUtilities.IsTlsV13(serverVersion2))
handshakeMessageInput.UpdateHash(m_handshakeHash);
break;
}
default:
handshakeMessageInput.UpdateHash(m_handshakeHash);
break;
case 0:
case 1:
case 2:
case 15:
case 20:
case 24:
break;
}
handshakeMessageInput.Seek(4, SeekOrigin.Current);
HandleHandshakeMessage(num2, handshakeMessageInput);
}
throw new TlsFatalAlert(80, "Handshake message length exceeds the maximum: " + HandshakeType.GetText(num2) + ", " + num3.ToString() + " > " + m_maxHandshakeMessageSize.ToString());
}
private void ProcessApplicationDataQueue()
{
}
private void ProcessAlertQueue()
{
while (m_alertQueue.Available >= 2) {
byte[] array = m_alertQueue.RemoveData(2, 0);
short alertLevel = array[0];
short alertDescription = array[1];
HandleAlertMessage(alertLevel, alertDescription);
}
}
private void ProcessChangeCipherSpec(byte[] buf, int off, int len)
{
ProtocolVersion serverVersion = Context.ServerVersion;
if (serverVersion == null || TlsUtilities.IsTlsV13(serverVersion))
throw new TlsFatalAlert(10);
int num = 0;
while (true) {
if (num >= len)
return;
if (TlsUtilities.ReadUint8(buf, off + num) != 1)
throw new TlsFatalAlert(50);
if (m_receivedChangeCipherSpec || m_alertQueue.Available > 0 || m_handshakeQueue.Available > 0)
break;
m_recordStream.NotifyChangeCipherSpecReceived();
m_receivedChangeCipherSpec = true;
HandleChangeCipherSpecMessage();
num++;
}
throw new TlsFatalAlert(10);
}
public virtual int ReadApplicationData(byte[] buffer, int offset, int count)
{
Streams.ValidateBufferArguments(buffer, offset, count);
return ReadApplicationData(buffer.AsSpan(offset, count));
}
public virtual int ReadApplicationData(Span<byte> buffer)
{
if (!m_appDataReady)
throw new InvalidOperationException("Cannot read application data until initial handshake completed.");
while (m_applicationDataQueue.Available < 1) {
if (m_closed) {
if (m_failedWithError)
throw new IOException("Cannot read application data on failed TLS connection");
return 0;
}
SafeReadRecord();
}
int num = buffer.Length;
if (num > 0) {
num = System.Math.Min(num, m_applicationDataQueue.Available);
m_applicationDataQueue.RemoveData(buffer.Slice(0, num), 0);
}
return num;
}
protected virtual RecordPreview SafePreviewRecordHeader(byte[] recordHeader)
{
try {
return m_recordStream.PreviewRecordHeader(recordHeader);
} catch (TlsFatalAlert tlsFatalAlert) {
HandleException(tlsFatalAlert.AlertDescription, "Failed to read record", tlsFatalAlert);
throw;
} catch (IOException e) {
HandleException(80, "Failed to read record", e);
throw;
} catch (Exception ex) {
HandleException(80, "Failed to read record", ex);
throw new TlsFatalAlert(80, ex);
}
}
protected virtual void SafeReadRecord()
{
try {
if (m_recordStream.ReadRecord())
return;
if (!m_appDataReady)
throw new TlsFatalAlert(40);
if (!Peer.RequiresCloseNotify()) {
HandleClose(false);
return;
}
} catch (TlsFatalAlertReceived) {
throw;
} catch (TlsFatalAlert tlsFatalAlert) {
HandleException(tlsFatalAlert.AlertDescription, "Failed to read record", tlsFatalAlert);
throw;
} catch (IOException e) {
HandleException(80, "Failed to read record", e);
throw;
} catch (Exception ex) {
HandleException(80, "Failed to read record", ex);
throw new TlsFatalAlert(80, ex);
}
HandleFailure();
throw new TlsNoCloseNotifyException();
}
protected virtual bool SafeReadFullRecord(byte[] input, int inputOff, int inputLen)
{
try {
return m_recordStream.ReadFullRecord(input, inputOff, inputLen);
} catch (TlsFatalAlert tlsFatalAlert) {
HandleException(tlsFatalAlert.AlertDescription, "Failed to process record", tlsFatalAlert);
throw;
} catch (IOException e) {
HandleException(80, "Failed to process record", e);
throw;
} catch (Exception ex) {
HandleException(80, "Failed to process record", ex);
throw new TlsFatalAlert(80, ex);
}
}
protected virtual void SafeWriteRecord(short type, byte[] buf, int offset, int len)
{
try {
m_recordStream.WriteRecord(type, buf, offset, len);
} catch (TlsFatalAlert tlsFatalAlert) {
HandleException(tlsFatalAlert.AlertDescription, "Failed to write record", tlsFatalAlert);
throw;
} catch (IOException e) {
HandleException(80, "Failed to write record", e);
throw;
} catch (Exception ex) {
HandleException(80, "Failed to write record", ex);
throw new TlsFatalAlert(80, ex);
}
}
protected virtual void SafeWriteRecord(short type, ReadOnlySpan<byte> buffer)
{
try {
m_recordStream.WriteRecord(type, buffer);
} catch (TlsFatalAlert tlsFatalAlert) {
HandleException(tlsFatalAlert.AlertDescription, "Failed to write record", tlsFatalAlert);
throw;
} catch (IOException e) {
HandleException(80, "Failed to write record", e);
throw;
} catch (Exception ex) {
HandleException(80, "Failed to write record", ex);
throw new TlsFatalAlert(80, ex);
}
}
public virtual void WriteApplicationData(byte[] buffer, int offset, int count)
{
Streams.ValidateBufferArguments(buffer, offset, count);
WriteApplicationData(buffer.AsSpan(offset, count));
}
public virtual void WriteApplicationData(ReadOnlySpan<byte> buffer)
{
if (!m_appDataReady)
throw new InvalidOperationException("Cannot write application data until initial handshake completed.");
lock (m_recordWriteLock) {
while (true) {
if (buffer.IsEmpty)
return;
if (m_closed)
break;
if (m_appDataSplitEnabled) {
switch (m_appDataSplitMode) {
case 2:
m_appDataSplitEnabled = false;
SafeWriteRecord(23, TlsUtilities.EmptyBytes, 0, 0);
break;
case 1:
SafeWriteRecord(23, TlsUtilities.EmptyBytes, 0, 0);
break;
default:
if (buffer.Length > 1) {
SafeWriteRecord(23, buffer.Slice(0, 1));
buffer = buffer.Slice(1, buffer.Length - 1);
}
break;
}
} else if (m_keyUpdateEnabled) {
if (m_keyUpdatePendingSend)
Send13KeyUpdate(false);
else if (m_recordStream.NeedsKeyUpdate()) {
Send13KeyUpdate(true);
}
}
int num = System.Math.Min(buffer.Length, m_recordStream.PlaintextLimit);
SafeWriteRecord(23, buffer.Slice(0, num));
int num2 = num;
buffer = buffer.Slice(num2, buffer.Length - num2);
}
throw new IOException("Cannot write application data on closed/failed TLS connection");
}
}
internal void WriteHandshakeMessage(byte[] buf, int off, int len)
{
if (len < 4)
throw new TlsFatalAlert(80);
switch (TlsUtilities.ReadUint8(buf, off)) {
case 4: {
ProtocolVersion serverVersion = Context.ServerVersion;
if (serverVersion != null && !TlsUtilities.IsTlsV13(serverVersion))
m_handshakeHash.Update(buf, off, len);
break;
}
default:
m_handshakeHash.Update(buf, off, len);
break;
case 0:
case 1:
case 24:
break;
}
int num = 0;
do {
int num2 = System.Math.Min(len - num, m_recordStream.PlaintextLimit);
SafeWriteRecord(22, buf, off + num, num2);
num += num2;
} while (num < len);
}
public virtual void CloseInput()
{
if (m_blocking)
throw new InvalidOperationException("Cannot use CloseInput() in blocking mode!");
if (!m_closed) {
if (m_inputBuffers.Available > 0)
throw new EndOfStreamException();
if (!m_appDataReady)
throw new TlsFatalAlert(40);
if (Peer.RequiresCloseNotify()) {
HandleFailure();
throw new TlsNoCloseNotifyException();
}
HandleClose(false);
}
}
public virtual RecordPreview PreviewInputRecord(byte[] recordHeader)
{
if (m_blocking)
throw new InvalidOperationException("Cannot use PreviewInputRecord() in blocking mode!");
if (m_inputBuffers.Available != 0)
throw new InvalidOperationException("Can only use PreviewInputRecord() for record-aligned input.");
if (m_closed)
throw new IOException("Connection is closed, cannot accept any more input");
return SafePreviewRecordHeader(recordHeader);
}
public virtual int PreviewOutputRecord()
{
if (m_blocking)
throw new InvalidOperationException("Cannot use PreviewOutputRecord() in blocking mode!");
ByteQueue buffer = m_outputBuffer.Buffer;
int available = buffer.Available;
if (available < 1)
return 0;
if (available >= 5) {
int num = buffer.ReadUint16(3);
int num2 = 5 + num;
if (available >= num2)
return num2;
}
throw new InvalidOperationException("Can only use PreviewOutputRecord() for record-aligned output.");
}
public virtual RecordPreview PreviewOutputRecord(int applicationDataSize)
{
if (!m_appDataReady)
throw new InvalidOperationException("Cannot use PreviewOutputRecord() until initial handshake completed.");
if (m_blocking)
throw new InvalidOperationException("Cannot use PreviewOutputRecord() in blocking mode!");
if (m_outputBuffer.Buffer.Available != 0)
throw new InvalidOperationException("Can only use PreviewOutputRecord() for record-aligned output.");
if (m_closed)
throw new IOException("Connection is closed, cannot produce any more output");
if (applicationDataSize < 1)
return new RecordPreview(0, 0);
if (m_appDataSplitEnabled) {
int appDataSplitMode = m_appDataSplitMode;
if (appDataSplitMode != 0 && (uint)(appDataSplitMode - 1) <= 1) {
RecordPreview a = m_recordStream.PreviewOutputRecord(0);
RecordPreview b = m_recordStream.PreviewOutputRecord(applicationDataSize);
return RecordPreview.CombineAppData(a, b);
}
RecordPreview recordPreview = m_recordStream.PreviewOutputRecord(1);
if (applicationDataSize > 1) {
RecordPreview b2 = m_recordStream.PreviewOutputRecord(applicationDataSize - 1);
recordPreview = RecordPreview.CombineAppData(recordPreview, b2);
}
return recordPreview;
}
RecordPreview recordPreview2 = m_recordStream.PreviewOutputRecord(applicationDataSize);
if (m_keyUpdateEnabled && (m_keyUpdatePendingSend || m_recordStream.NeedsKeyUpdate())) {
int length = HandshakeMessageOutput.GetLength(1);
int recordSize = m_recordStream.PreviewOutputRecordSize(length);
recordPreview2 = RecordPreview.ExtendRecordSize(recordPreview2, recordSize);
}
return recordPreview2;
}
public virtual void OfferInput(byte[] input)
{
OfferInput(input, 0, input.Length);
}
public virtual void OfferInput(byte[] input, int inputOff, int inputLen)
{
if (m_blocking)
throw new InvalidOperationException("Cannot use OfferInput() in blocking mode! Use Stream instead.");
if (m_closed)
throw new IOException("Connection is closed, cannot accept any more input");
if (m_inputBuffers.Available == 0 && SafeReadFullRecord(input, inputOff, inputLen)) {
if (m_closed && !m_appDataReady)
throw new TlsFatalAlert(80);
} else {
m_inputBuffers.AddBytes(input, inputOff, inputLen);
do {
if (m_inputBuffers.Available < 5)
return;
byte[] array = new byte[5];
if (5 != m_inputBuffers.Peek(array))
throw new TlsFatalAlert(80);
RecordPreview recordPreview = SafePreviewRecordHeader(array);
if (m_inputBuffers.Available < recordPreview.RecordSize)
return;
SafeReadRecord();
} while (!m_closed);
if (!m_appDataReady)
throw new TlsFatalAlert(80);
}
}
public virtual int GetAvailableInputBytes()
{
if (m_blocking)
throw new InvalidOperationException("Cannot use GetAvailableInputBytes() in blocking mode!");
return ApplicationDataAvailable;
}
public virtual int ReadInput(byte[] buf, int off, int len)
{
if (m_blocking)
throw new InvalidOperationException("Cannot use ReadInput() in blocking mode! Use Stream instead.");
len = System.Math.Min(len, ApplicationDataAvailable);
if (len < 1)
return 0;
m_applicationDataQueue.RemoveData(buf, off, len, 0);
return len;
}
public virtual int GetAvailableOutputBytes()
{
if (m_blocking)
throw new InvalidOperationException("Cannot use GetAvailableOutputBytes() in blocking mode! Use Stream instead.");
return m_outputBuffer.Buffer.Available;
}
public virtual int ReadOutput(byte[] buffer, int offset, int length)
{
if (m_blocking)
throw new InvalidOperationException("Cannot use ReadOutput() in blocking mode! Use 'Stream() instead.");
int num = System.Math.Min(GetAvailableOutputBytes(), length);
m_outputBuffer.Buffer.RemoveData(buffer, offset, num, 0);
return num;
}
protected virtual bool EstablishSession(TlsSession sessionToResume)
{
m_tlsSession = null;
m_sessionParameters = null;
m_sessionMasterSecret = null;
if (sessionToResume == null || !sessionToResume.IsResumable)
return false;
SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
if (sessionParameters == null)
return false;
ProtocolVersion negotiatedVersion = sessionParameters.NegotiatedVersion;
if (negotiatedVersion == null || !negotiatedVersion.IsTls)
return false;
if (!TlsUtilities.IsExtendedMasterSecretOptional(negotiatedVersion) && sessionParameters.IsExtendedMasterSecret == negotiatedVersion.IsSsl)
return false;
TlsSecret sessionMasterSecret = TlsUtilities.GetSessionMasterSecret(Context.Crypto, sessionParameters.MasterSecret);
if (sessionMasterSecret == null)
return false;
m_tlsSession = sessionToResume;
m_sessionParameters = sessionParameters;
m_sessionMasterSecret = sessionMasterSecret;
return true;
}
protected virtual void CancelSession()
{
if (m_sessionMasterSecret != null) {
m_sessionMasterSecret.Destroy();
m_sessionMasterSecret = null;
}
if (m_sessionParameters != null) {
m_sessionParameters.Clear();
m_sessionParameters = null;
}
m_tlsSession = null;
}
protected virtual void InvalidateSession()
{
if (m_tlsSession != null)
m_tlsSession.Invalidate();
CancelSession();
}
protected unsafe virtual void ProcessFinishedMessage(MemoryStream buf)
{
TlsContext context = Context;
SecurityParameters securityParameters = context.SecurityParameters;
bool isServer = context.IsServer;
int verifyDataLength = securityParameters.VerifyDataLength;
Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)verifyDataLength], verifyDataLength);
TlsUtilities.ReadFully(span, buf);
AssertEmpty(buf);
byte[] array = TlsUtilities.CalculateVerifyData(context, m_handshakeHash, !isServer);
if (!Arrays.FixedTimeEquals(array, span))
throw new TlsFatalAlert(51);
securityParameters.m_peerVerifyData = array;
if ((!securityParameters.IsResumedSession || securityParameters.IsExtendedMasterSecret) && securityParameters.LocalVerifyData == null)
securityParameters.m_tlsUnique = array;
}
protected unsafe virtual void Process13FinishedMessage(MemoryStream buf)
{
TlsContext context = Context;
SecurityParameters securityParameters = context.SecurityParameters;
bool isServer = context.IsServer;
int verifyDataLength = securityParameters.VerifyDataLength;
Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)verifyDataLength], verifyDataLength);
TlsUtilities.ReadFully(span, buf);
AssertEmpty(buf);
byte[] array = TlsUtilities.CalculateVerifyData(context, m_handshakeHash, !isServer);
if (!Arrays.FixedTimeEquals(array, span))
throw new TlsFatalAlert(51);
securityParameters.m_peerVerifyData = array;
securityParameters.m_tlsUnique = null;
}
protected virtual void RaiseAlertFatal(short alertDescription, string message, Exception cause)
{
Peer.NotifyAlertRaised(2, alertDescription, message, cause);
byte[] plaintext = new byte[2] {
2,
(byte)alertDescription
};
try {
m_recordStream.WriteRecord(21, plaintext, 0, 2);
} catch (Exception) {
}
}
protected virtual void RaiseAlertWarning(short alertDescription, string message)
{
Peer.NotifyAlertRaised(1, alertDescription, message, null);
byte[] buf = new byte[2] {
1,
(byte)alertDescription
};
SafeWriteRecord(21, buf, 0, 2);
}
protected virtual void Receive13KeyUpdate(MemoryStream buf)
{
if (!m_appDataReady || !m_keyUpdateEnabled)
throw new TlsFatalAlert(10);
short num = TlsUtilities.ReadUint8(buf);
AssertEmpty(buf);
if (!KeyUpdateRequest.IsValid(num))
throw new TlsFatalAlert(47);
bool flag = 1 == num;
TlsUtilities.Update13TrafficSecretPeer(Context);
m_recordStream.NotifyKeyUpdateReceived();
m_keyUpdatePendingSend |= flag;
}
protected virtual void SendCertificateMessage(Certificate certificate, Stream endPointHash)
{
TlsContext context = Context;
SecurityParameters securityParameters = context.SecurityParameters;
if (securityParameters.LocalCertificate != null)
throw new TlsFatalAlert(80);
if (certificate == null)
certificate = Certificate.EmptyChain;
if (certificate.IsEmpty && !context.IsServer && securityParameters.NegotiatedVersion.IsSsl) {
string message = "SSLv3 client didn't provide credentials";
RaiseAlertWarning(41, message);
} else {
HandshakeMessageOutput handshakeMessageOutput = new HandshakeMessageOutput(11);
certificate.Encode(context, handshakeMessageOutput, endPointHash);
handshakeMessageOutput.Send(this);
}
securityParameters.m_localCertificate = certificate;
}
protected virtual void Send13CertificateMessage(Certificate certificate)
{
if (certificate == null)
throw new TlsFatalAlert(80);
TlsContext context = Context;
SecurityParameters securityParameters = context.SecurityParameters;
if (securityParameters.LocalCertificate != null)
throw new TlsFatalAlert(80);
HandshakeMessageOutput handshakeMessageOutput = new HandshakeMessageOutput(11);
certificate.Encode(context, handshakeMessageOutput, null);
handshakeMessageOutput.Send(this);
securityParameters.m_localCertificate = certificate;
}
protected virtual void Send13CertificateVerifyMessage(DigitallySigned certificateVerify)
{
HandshakeMessageOutput handshakeMessageOutput = new HandshakeMessageOutput(15);
certificateVerify.Encode(handshakeMessageOutput);
handshakeMessageOutput.Send(this);
}
protected virtual void SendChangeCipherSpec()
{
SendChangeCipherSpecMessage();
m_recordStream.EnablePendingCipherWrite();
}
protected virtual void SendChangeCipherSpecMessage()
{
byte[] array = new byte[1] {
1
};
SafeWriteRecord(20, array, 0, array.Length);
}
protected virtual void SendFinishedMessage()
{
TlsContext context = Context;
SecurityParameters securityParameters = context.SecurityParameters;
byte[] array = securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(isServer: context.IsServer, context: context, handshakeHash: m_handshakeHash);
if ((!securityParameters.IsResumedSession || securityParameters.IsExtendedMasterSecret) && securityParameters.PeerVerifyData == null)
securityParameters.m_tlsUnique = array;
HandshakeMessageOutput.Send(this, 20, array);
}
protected virtual void Send13FinishedMessage()
{
TlsContext context = Context;
SecurityParameters securityParameters = context.SecurityParameters;
byte[] body = securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(isServer: context.IsServer, context: context, handshakeHash: m_handshakeHash);
securityParameters.m_tlsUnique = null;
HandshakeMessageOutput.Send(this, 20, body);
}
protected virtual void Send13KeyUpdate(bool updateRequested)
{
if (!m_appDataReady || !m_keyUpdateEnabled)
throw new TlsFatalAlert(80);
short u = updateRequested ? ((short)1) : ((short)0);
HandshakeMessageOutput.Send(this, 24, TlsUtilities.EncodeUint8(u));
TlsUtilities.Update13TrafficSecretLocal(Context);
m_recordStream.NotifyKeyUpdateSent();
m_keyUpdatePendingSend &= updateRequested;
}
protected virtual void SendSupplementalDataMessage(IList<SupplementalDataEntry> supplementalData)
{
HandshakeMessageOutput handshakeMessageOutput = new HandshakeMessageOutput(23);
WriteSupplementalData(handshakeMessageOutput, supplementalData);
handshakeMessageOutput.Send(this);
}
public virtual void Close()
{
HandleClose(true);
}
public virtual void Flush()
{
}
[Obsolete("Will be removed")]
protected virtual short ProcessMaxFragmentLengthExtension(IDictionary<int, byte[]> clientExtensions, IDictionary<int, byte[]> serverExtensions, short alertDescription)
{
return TlsUtilities.ProcessMaxFragmentLengthExtension(clientExtensions, serverExtensions, alertDescription);
}
protected virtual void RefuseRenegotiation()
{
if (TlsUtilities.IsSsl(Context))
throw new TlsFatalAlert(40);
RaiseAlertWarning(100, "Renegotiation not supported");
}
internal static void AssertEmpty(MemoryStream buf)
{
if (buf.Position < buf.Length)
throw new TlsFatalAlert(50);
}
internal static byte[] CreateRandomBlock(bool useGmtUnixTime, TlsContext context)
{
byte[] array = context.NonceGenerator.GenerateNonce(32);
if (useGmtUnixTime)
TlsUtilities.WriteGmtUnixTime(array, 0);
return array;
}
internal static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
{
return TlsUtilities.EncodeOpaque8(renegotiated_connection);
}
internal static void EstablishMasterSecret(TlsContext context, TlsKeyExchange keyExchange)
{
TlsSecret tlsSecret = keyExchange.GeneratePreMasterSecret();
if (tlsSecret == null)
throw new TlsFatalAlert(80);
try {
context.SecurityParameters.m_masterSecret = TlsUtilities.CalculateMasterSecret(context, tlsSecret);
} finally {
tlsSecret.Destroy();
}
}
internal static IDictionary<int, byte[]> ReadExtensions(MemoryStream input)
{
if (input.Position >= input.Length)
return null;
byte[] extBytes = TlsUtilities.ReadOpaque16(input);
AssertEmpty(input);
return ReadExtensionsData(extBytes);
}
internal static IDictionary<int, byte[]> ReadExtensionsData(byte[] extBytes)
{
Dictionary<int, byte[]> dictionary = new Dictionary<int, byte[]>();
if (extBytes.Length != 0) {
MemoryStream memoryStream = new MemoryStream(extBytes, false);
do {
int num = TlsUtilities.ReadUint16(memoryStream);
byte[] value = TlsUtilities.ReadOpaque16(memoryStream);
if (dictionary.ContainsKey(num))
throw new TlsFatalAlert(47, "Repeated extension: " + ExtensionType.GetText(num));
dictionary.Add(num, value);
} while (memoryStream.Position < memoryStream.Length);
}
return dictionary;
}
internal static IDictionary<int, byte[]> ReadExtensionsData13(int handshakeType, byte[] extBytes)
{
Dictionary<int, byte[]> dictionary = new Dictionary<int, byte[]>();
if (extBytes.Length != 0) {
MemoryStream memoryStream = new MemoryStream(extBytes, false);
do {
int num = TlsUtilities.ReadUint16(memoryStream);
if (!TlsUtilities.IsPermittedExtensionType13(handshakeType, num))
throw new TlsFatalAlert(47, "Invalid extension: " + ExtensionType.GetText(num));
byte[] value = TlsUtilities.ReadOpaque16(memoryStream);
if (dictionary.ContainsKey(num))
throw new TlsFatalAlert(47, "Repeated extension: " + ExtensionType.GetText(num));
dictionary.Add(num, value);
} while (memoryStream.Position < memoryStream.Length);
}
return dictionary;
}
internal static IDictionary<int, byte[]> ReadExtensionsDataClientHello(byte[] extBytes)
{
Dictionary<int, byte[]> dictionary = new Dictionary<int, byte[]>();
if (extBytes.Length != 0) {
MemoryStream memoryStream = new MemoryStream(extBytes, false);
bool flag = false;
int num;
do {
num = TlsUtilities.ReadUint16(memoryStream);
byte[] value = TlsUtilities.ReadOpaque16(memoryStream);
if (dictionary.ContainsKey(num))
throw new TlsFatalAlert(47, "Repeated extension: " + ExtensionType.GetText(num));
dictionary.Add(num, value);
flag |= (41 == num);
} while (memoryStream.Position < memoryStream.Length);
if (flag && 41 != num)
throw new TlsFatalAlert(47, "'pre_shared_key' MUST be last in ClientHello");
}
return dictionary;
}
internal static IList<SupplementalDataEntry> ReadSupplementalDataMessage(MemoryStream input)
{
byte[] buffer = TlsUtilities.ReadOpaque24(input, 1);
AssertEmpty(input);
MemoryStream memoryStream = new MemoryStream(buffer, false);
List<SupplementalDataEntry> list = new List<SupplementalDataEntry>();
while (memoryStream.Position < memoryStream.Length) {
int dataType = TlsUtilities.ReadUint16(memoryStream);
byte[] data = TlsUtilities.ReadOpaque16(memoryStream);
list.Add(new SupplementalDataEntry(dataType, data));
}
return list;
}
internal static void WriteExtensions(Stream output, IDictionary<int, byte[]> extensions)
{
WriteExtensions(output, extensions, 0);
}
internal static void WriteExtensions(Stream output, IDictionary<int, byte[]> extensions, int bindersSize)
{
if (extensions != null && extensions.Count >= 1) {
byte[] array = WriteExtensionsData(extensions, bindersSize);
int i = array.Length + bindersSize;
TlsUtilities.CheckUint16(i);
TlsUtilities.WriteUint16(i, output);
output.Write(array, 0, array.Length);
}
}
internal static byte[] WriteExtensionsData(IDictionary<int, byte[]> extensions)
{
return WriteExtensionsData(extensions, 0);
}
internal static byte[] WriteExtensionsData(IDictionary<int, byte[]> extensions, int bindersSize)
{
MemoryStream memoryStream = new MemoryStream();
WriteExtensionsData(extensions, memoryStream, bindersSize);
return memoryStream.ToArray();
}
internal static void WriteExtensionsData(IDictionary<int, byte[]> extensions, MemoryStream buf)
{
WriteExtensionsData(extensions, buf, 0);
}
internal static void WriteExtensionsData(IDictionary<int, byte[]> extensions, MemoryStream buf, int bindersSize)
{
WriteSelectedExtensions(buf, extensions, true);
WriteSelectedExtensions(buf, extensions, false);
WritePreSharedKeyExtension(buf, extensions, bindersSize);
}
internal static void (MemoryStream buf, IDictionary<int, byte[]> extensions, int bindersSize)
{
if (extensions.TryGetValue(41, out byte[] value)) {
TlsUtilities.CheckUint16(41);
TlsUtilities.WriteUint16(41, buf);
int i = value.Length + bindersSize;
TlsUtilities.CheckUint16(i);
TlsUtilities.WriteUint16(i, buf);
buf.Write(value, 0, value.Length);
}
}
internal static void WriteSelectedExtensions(Stream output, IDictionary<int, byte[]> extensions, bool selectEmpty)
{
foreach (KeyValuePair<int, byte[]> extension in extensions) {
int key = extension.Key;
if (41 != key) {
byte[] value = extension.Value;
if (selectEmpty == (value.Length == 0)) {
TlsUtilities.CheckUint16(key);
TlsUtilities.WriteUint16(key, output);
TlsUtilities.WriteOpaque16(value, output);
}
}
}
}
internal static void WriteSupplementalData(Stream output, IList<SupplementalDataEntry> supplementalData)
{
MemoryStream memoryStream = new MemoryStream();
foreach (SupplementalDataEntry supplementalDatum in supplementalData) {
int dataType = supplementalDatum.DataType;
TlsUtilities.CheckUint16(dataType);
TlsUtilities.WriteUint16(dataType, memoryStream);
TlsUtilities.WriteOpaque16(supplementalDatum.Data, memoryStream);
}
TlsUtilities.WriteOpaque24(memoryStream.ToArray(), output);
}
}
}