#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.Collections;
using System.IO;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Tls
{
public class TlsServerProtocol
: TlsProtocol
{
protected TlsServer m_tlsServer = null;
internal TlsServerContextImpl m_tlsServerContext = null;
protected int[] m_offeredCipherSuites = null;
protected TlsKeyExchange m_keyExchange = null;
protected CertificateRequest m_certificateRequest = null;
/// Constructor for non-blocking mode.
///
/// When data is received, use to provide the received ciphertext,
/// then use to read the corresponding cleartext.
/// Similarly, when data needs to be sent, use
/// to provide the cleartext, then use to get the
/// corresponding ciphertext.
///
public TlsServerProtocol()
: base()
{
}
/// Constructor for blocking mode.
/// The of data to/from the server.
public TlsServerProtocol(Stream stream)
: base(stream)
{
}
/// Constructor for blocking mode.
/// The of data from the server.
/// The of data to the server.
public TlsServerProtocol(Stream input, Stream output)
: base(input, output)
{
}
/// Receives a TLS handshake in the role of server.
///
/// In blocking mode, this will not return until the handshake is complete. In non-blocking mode, use
/// to receive a callback when the handshake is complete.
///
/// The to use for the handshake.
/// If in blocking mode and handshake was not successful.
public void Accept(TlsServer tlsServer)
{
if (tlsServer == null)
throw new ArgumentNullException("tlsServer");
if (m_tlsServer != null)
throw new InvalidOperationException("'Accept' can only be called once");
this.m_tlsServer = tlsServer;
this.m_tlsServerContext = new TlsServerContextImpl(tlsServer.Crypto);
tlsServer.Init(m_tlsServerContext);
tlsServer.NotifyCloseHandle(this);
BeginHandshake(false);
if (m_blocking)
{
BlockForHandshake();
}
}
protected override void CleanupHandshake()
{
base.CleanupHandshake();
this.m_offeredCipherSuites = null;
this.m_keyExchange = null;
this.m_certificateRequest = null;
}
protected virtual bool ExpectCertificateVerifyMessage()
{
if (null == m_certificateRequest)
return false;
Certificate clientCertificate = m_tlsServerContext.SecurityParameters.PeerCertificate;
return null != clientCertificate && !clientCertificate.IsEmpty
&& (null == m_keyExchange || m_keyExchange.RequiresCertificateVerify);
}
///
protected virtual ServerHello Generate13HelloRetryRequest(ClientHello clientHello)
{
// TODO[tls13] In future there might be other reasons for a HelloRetryRequest.
if (m_retryGroup < 0)
throw new TlsFatalAlert(AlertDescription.internal_error);
SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
ProtocolVersion serverVersion = securityParameters.NegotiatedVersion;
IDictionary serverHelloExtensions = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable();
TlsExtensionsUtilities.AddSupportedVersionsExtensionServer(serverHelloExtensions, serverVersion);
if (m_retryGroup >= 0)
{
TlsExtensionsUtilities.AddKeyShareHelloRetryRequest(serverHelloExtensions, m_retryGroup);
}
if (null != m_retryCookie)
{
TlsExtensionsUtilities.AddCookieExtension(serverHelloExtensions, m_retryCookie);
}
TlsUtilities.CheckExtensionData13(serverHelloExtensions, HandshakeType.hello_retry_request,
AlertDescription.internal_error);
return new ServerHello(clientHello.SessionID, securityParameters.CipherSuite, serverHelloExtensions);
}
///
protected virtual ServerHello Generate13ServerHello(ClientHello clientHello,
HandshakeMessageInput clientHelloMessage, bool afterHelloRetryRequest)
{
SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
byte[] legacy_session_id = clientHello.SessionID;
IDictionary clientHelloExtensions = clientHello.Extensions;
if (null == clientHelloExtensions)
throw new TlsFatalAlert(AlertDescription.missing_extension);
ProtocolVersion serverVersion = securityParameters.NegotiatedVersion;
TlsCrypto crypto = m_tlsServerContext.Crypto;
// NOTE: Will only select for psk_dhe_ke
OfferedPsks.SelectedConfig selectedPsk = TlsUtilities.SelectPreSharedKey(m_tlsServerContext, m_tlsServer,
clientHelloExtensions, clientHelloMessage, m_handshakeHash, afterHelloRetryRequest);
IList clientShares = TlsExtensionsUtilities.GetKeyShareClientHello(clientHelloExtensions);
KeyShareEntry clientShare = null;
if (afterHelloRetryRequest)
{
if (m_retryGroup < 0)
throw new TlsFatalAlert(AlertDescription.internal_error);
if (null == selectedPsk)
{
/*
* RFC 8446 4.2.3. If a server is authenticating via a certificate and the client has
* not sent a "signature_algorithms" extension, then the server MUST abort the handshake
* with a "missing_extension" alert.
*/
if (null == securityParameters.ClientSigAlgs)
throw new TlsFatalAlert(AlertDescription.missing_extension);
}
else
{
// TODO[tls13] Maybe filter the offered PSKs by PRF algorithm before server selection instead
if (selectedPsk.m_psk.PrfAlgorithm != securityParameters.PrfAlgorithm)
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
/*
* TODO[tls13] Confirm fields in the ClientHello haven't changed
*
* RFC 8446 4.1.2 [..] when the server has responded to its ClientHello with a
* HelloRetryRequest [..] the client MUST send the same ClientHello without
* modification, except as follows: [key_share, early_data, cookie, pre_shared_key,
* padding].
*/
byte[] cookie = TlsExtensionsUtilities.GetCookieExtension(clientHelloExtensions);
if (!Arrays.AreEqual(m_retryCookie, cookie))
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
this.m_retryCookie = null;
clientShare = TlsUtilities.SelectKeyShare(clientShares, m_retryGroup);
if (null == clientShare)
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
else
{
this.m_clientExtensions = clientHelloExtensions;
securityParameters.m_secureRenegotiation = false;
// NOTE: Validates the padding extension data, if present
TlsExtensionsUtilities.GetPaddingExtension(clientHelloExtensions);
securityParameters.m_clientServerNames = TlsExtensionsUtilities
.GetServerNameExtensionClient(clientHelloExtensions);
TlsUtilities.EstablishClientSigAlgs(securityParameters, clientHelloExtensions);
/*
* RFC 8446 4.2.3. If a server is authenticating via a certificate and the client has
* not sent a "signature_algorithms" extension, then the server MUST abort the handshake
* with a "missing_extension" alert.
*/
if (null == selectedPsk && null == securityParameters.ClientSigAlgs)
throw new TlsFatalAlert(AlertDescription.missing_extension);
m_tlsServer.ProcessClientExtensions(clientHelloExtensions);
/*
* NOTE: Currently no server support for session resumption
*
* If adding support, ensure securityParameters.tlsUnique is set to the localVerifyData, but
* ONLY when extended_master_secret has been negotiated (otherwise NULL).
*/
{
// TODO[tls13] Resumption/PSK
this.m_tlsSession = TlsUtilities.ImportSession(TlsUtilities.EmptyBytes, null);
this.m_sessionParameters = null;
this.m_sessionMasterSecret = null;
}
securityParameters.m_sessionID = m_tlsSession.SessionID;
m_tlsServer.NotifySession(m_tlsSession);
TlsUtilities.NegotiatedVersionTlsServer(m_tlsServerContext);
{
securityParameters.m_serverRandom = CreateRandomBlock(false, m_tlsServerContext);
if (!serverVersion.Equals(ProtocolVersion.GetLatestTls(m_tlsServer.GetProtocolVersions())))
{
TlsUtilities.WriteDowngradeMarker(serverVersion, securityParameters.ServerRandom);
}
}
{
// TODO[tls13] Constrain selection when PSK selected
int cipherSuite = m_tlsServer.GetSelectedCipherSuite();
if (!TlsUtilities.IsValidCipherSuiteSelection(m_offeredCipherSuites, cipherSuite) ||
!TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, serverVersion))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
}
int[] clientSupportedGroups = securityParameters.ClientSupportedGroups;
int[] serverSupportedGroups = securityParameters.ServerSupportedGroups;
clientShare = TlsUtilities.SelectKeyShare(crypto, serverVersion, clientShares, clientSupportedGroups,
serverSupportedGroups);
if (null == clientShare)
{
this.m_retryGroup = TlsUtilities.SelectKeyShareGroup(crypto, serverVersion, clientSupportedGroups,
serverSupportedGroups);
if (m_retryGroup < 0)
throw new TlsFatalAlert(AlertDescription.handshake_failure);
this.m_retryCookie = m_tlsServerContext.NonceGenerator.GenerateNonce(16);
return Generate13HelloRetryRequest(clientHello);
}
if (clientShare.NamedGroup != serverSupportedGroups[0])
{
/*
* TODO[tls13] RFC 8446 4.2.7. As of TLS 1.3, servers are permitted to send the
* "supported_groups" extension to the client. Clients MUST NOT act upon any
* information found in "supported_groups" prior to successful completion of the
* handshake but MAY use the information learned from a successfully completed
* handshake to change what groups they use in their "key_share" extension in
* subsequent connections. If the server has a group it prefers to the ones in the
* "key_share" extension but is still willing to accept the ClientHello, it SHOULD
* send "supported_groups" to update the client's view of its preferences; this
* extension SHOULD contain all groups the server supports, regardless of whether
* they are currently supported by the client.
*/
}
}
IDictionary serverHelloExtensions = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable();
IDictionary serverEncryptedExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
m_tlsServer.GetServerExtensions());
m_tlsServer.GetServerExtensionsForConnection(serverEncryptedExtensions);
ProtocolVersion serverLegacyVersion = ProtocolVersion.TLSv12;
TlsExtensionsUtilities.AddSupportedVersionsExtensionServer(serverHelloExtensions, serverVersion);
/*
* RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server
* Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate
* the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used.
*/
securityParameters.m_extendedMasterSecret = true;
/*
* RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous
* contents of this extension are irrelevant, and only the values in the new handshake
* messages are considered.
*/
securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
serverEncryptedExtensions);
securityParameters.m_applicationProtocolSet = true;
if (serverEncryptedExtensions.Count > 0)
{
securityParameters.m_maxFragmentLength = ProcessMaxFragmentLengthExtension(clientHelloExtensions,
serverEncryptedExtensions, AlertDescription.internal_error);
}
securityParameters.m_encryptThenMac = false;
securityParameters.m_truncatedHmac = false;
/*
* TODO[tls13] RFC 8446 4.4.2.1. OCSP Status and SCT Extensions.
*
* OCSP information is carried in an extension for a CertificateEntry.
*/
securityParameters.m_statusRequestVersion = clientHelloExtensions.Contains(ExtensionType.status_request)
? 1 : 0;
this.m_expectSessionTicket = false;
TlsSecret pskEarlySecret = null;
if (null != selectedPsk)
{
pskEarlySecret = selectedPsk.m_earlySecret;
this.m_selectedPsk13 = true;
TlsExtensionsUtilities.AddPreSharedKeyServerHello(serverHelloExtensions, selectedPsk.m_index);
}
TlsSecret sharedSecret;
{
int namedGroup = clientShare.NamedGroup;
TlsAgreement agreement;
if (NamedGroup.RefersToASpecificCurve(namedGroup))
{
agreement = crypto.CreateECDomain(new TlsECConfig(namedGroup)).CreateECDH();
}
else if (NamedGroup.RefersToASpecificFiniteField(namedGroup))
{
agreement = crypto.CreateDHDomain(new TlsDHConfig(namedGroup, true)).CreateDH();
}
else
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
byte[] key_exchange = agreement.GenerateEphemeral();
KeyShareEntry serverShare = new KeyShareEntry(namedGroup, key_exchange);
TlsExtensionsUtilities.AddKeyShareServerHello(serverHelloExtensions, serverShare);
agreement.ReceivePeerValue(clientShare.KeyExchange);
sharedSecret = agreement.CalculateSecret();
}
TlsUtilities.Establish13PhaseSecrets(m_tlsServerContext, pskEarlySecret, sharedSecret);
this.m_serverExtensions = serverEncryptedExtensions;
ApplyMaxFragmentLengthExtension(securityParameters.MaxFragmentLength);
TlsUtilities.CheckExtensionData13(serverHelloExtensions, HandshakeType.server_hello,
AlertDescription.internal_error);
return new ServerHello(serverLegacyVersion, securityParameters.ServerRandom, legacy_session_id,
securityParameters.CipherSuite, serverHelloExtensions);
}
///
protected virtual ServerHello GenerateServerHello(ClientHello clientHello,
HandshakeMessageInput clientHelloMessage)
{
ProtocolVersion clientLegacyVersion = clientHello.Version;
if (!clientLegacyVersion.IsTls)
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
this.m_offeredCipherSuites = clientHello.CipherSuites;
SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
m_tlsServerContext.SetClientSupportedVersions(
TlsExtensionsUtilities.GetSupportedVersionsExtensionClient(clientHello.Extensions));
ProtocolVersion clientVersion = clientLegacyVersion;
if (null == m_tlsServerContext.ClientSupportedVersions)
{
if (clientVersion.IsLaterVersionOf(ProtocolVersion.TLSv12))
{
clientVersion = ProtocolVersion.TLSv12;
}
m_tlsServerContext.SetClientSupportedVersions(clientVersion.DownTo(ProtocolVersion.SSLv3));
}
else
{
clientVersion = ProtocolVersion.GetLatestTls(m_tlsServerContext.ClientSupportedVersions);
}
// Set the legacy_record_version to use for early alerts
m_recordStream.SetWriteVersion(clientVersion);
if (!ProtocolVersion.SERVER_EARLIEST_SUPPORTED_TLS.IsEqualOrEarlierVersionOf(clientVersion))
throw new TlsFatalAlert(AlertDescription.protocol_version);
// NOT renegotiating
{
m_tlsServerContext.SetClientVersion(clientVersion);
}
m_tlsServer.NotifyClientVersion(m_tlsServerContext.ClientVersion);
securityParameters.m_clientRandom = clientHello.Random;
m_tlsServer.NotifyFallback(Arrays.Contains(m_offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV));
m_tlsServer.NotifyOfferedCipherSuites(m_offeredCipherSuites);
// TODO[tls13] Negotiate cipher suite first?
ProtocolVersion serverVersion;
// NOT renegotiating
{
serverVersion = m_tlsServer.GetServerVersion();
if (!ProtocolVersion.Contains(m_tlsServerContext.ClientSupportedVersions, serverVersion))
throw new TlsFatalAlert(AlertDescription.internal_error);
securityParameters.m_negotiatedVersion = serverVersion;
}
securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
clientHello.Extensions);
securityParameters.m_serverSupportedGroups = m_tlsServer.GetSupportedGroups();
if (ProtocolVersion.TLSv13.IsEqualOrEarlierVersionOf(serverVersion))
{
// See RFC 8446 D.4.
m_recordStream.SetIgnoreChangeCipherSpec(true);
m_recordStream.SetWriteVersion(ProtocolVersion.TLSv12);
return Generate13ServerHello(clientHello, clientHelloMessage, false);
}
m_recordStream.SetWriteVersion(serverVersion);
this.m_clientExtensions = clientHello.Extensions;
byte[] clientRenegExtData = TlsUtilities.GetExtensionData(m_clientExtensions, ExtensionType.renegotiation_info);
// NOT renegotiating
{
/*
* RFC 5746 3.6. Server Behavior: Initial Handshake (both full and session-resumption)
*/
/*
* RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
* or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
* ClientHello. Including both is NOT RECOMMENDED.
*/
/*
* When a ClientHello is received, the server MUST check if it includes the
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
* to TRUE.
*/
if (Arrays.Contains(m_offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
{
securityParameters.m_secureRenegotiation = true;
}
/*
* The server MUST check if the "renegotiation_info" extension is included in the
* ClientHello.
*/
if (clientRenegExtData != null)
{
/*
* If the extension is present, set secure_renegotiation flag to TRUE. The
* server MUST then verify that the length of the "renegotiated_connection"
* field is zero, and if it is not, MUST abort the handshake.
*/
securityParameters.m_secureRenegotiation = true;
if (!Arrays.ConstantTimeAreEqual(clientRenegExtData,
CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
{
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
}
}
m_tlsServer.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
bool offeredExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(
m_clientExtensions);
if (m_clientExtensions != null)
{
// NOTE: Validates the padding extension data, if present
TlsExtensionsUtilities.GetPaddingExtension(m_clientExtensions);
securityParameters.m_clientServerNames = TlsExtensionsUtilities.GetServerNameExtensionClient(
m_clientExtensions);
/*
* RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior
* to 1.2. Clients MUST NOT offer it if they are offering prior versions.
*/
if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion))
{
TlsUtilities.EstablishClientSigAlgs(securityParameters, m_clientExtensions);
}
securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
m_clientExtensions);
m_tlsServer.ProcessClientExtensions(m_clientExtensions);
}
this.m_resumedSession = EstablishSession(m_tlsServer.GetSessionToResume(clientHello.SessionID));
if (!m_resumedSession)
{
byte[] newSessionID = m_tlsServer.GetNewSessionID();
if (null == newSessionID)
{
newSessionID = TlsUtilities.EmptyBytes;
}
this.m_tlsSession = TlsUtilities.ImportSession(newSessionID, null);
this.m_sessionParameters = null;
this.m_sessionMasterSecret = null;
}
securityParameters.m_sessionID = m_tlsSession.SessionID;
m_tlsServer.NotifySession(m_tlsSession);
TlsUtilities.NegotiatedVersionTlsServer(m_tlsServerContext);
{
bool useGmtUnixTime = m_tlsServer.ShouldUseGmtUnixTime();
securityParameters.m_serverRandom = CreateRandomBlock(useGmtUnixTime, m_tlsServerContext);
if (!serverVersion.Equals(ProtocolVersion.GetLatestTls(m_tlsServer.GetProtocolVersions())))
{
TlsUtilities.WriteDowngradeMarker(serverVersion, securityParameters.ServerRandom);
}
}
{
int cipherSuite = m_resumedSession
? m_sessionParameters.CipherSuite
: m_tlsServer.GetSelectedCipherSuite();
if (!TlsUtilities.IsValidCipherSuiteSelection(m_offeredCipherSuites, cipherSuite) ||
!TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, serverVersion))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
}
m_tlsServerContext.SetRsaPreMasterSecretVersion(clientLegacyVersion);
{
IDictionary sessionServerExtensions = m_resumedSession
? m_sessionParameters.ReadServerExtensions()
: m_tlsServer.GetServerExtensions();
this.m_serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(sessionServerExtensions);
}
m_tlsServer.GetServerExtensionsForConnection(m_serverExtensions);
// NOT renegotiating
{
/*
* RFC 5746 3.6. Server Behavior: Initial Handshake (both full and session-resumption)
*/
if (securityParameters.IsSecureRenegotiation)
{
byte[] serverRenegExtData = TlsUtilities.GetExtensionData(m_serverExtensions,
ExtensionType.renegotiation_info);
bool noRenegExt = (null == serverRenegExtData);
if (noRenegExt)
{
/*
* Note that sending a "renegotiation_info" extension in response to a ClientHello
* containing only the SCSV is an explicit exception to the prohibition in RFC 5246,
* Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed
* because the client is signaling its willingness to receive the extension via the
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
*/
/*
* If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
* "renegotiation_info" extension in the ServerHello message.
*/
this.m_serverExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(
TlsUtilities.EmptyBytes);
}
}
}
/*
* RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
* master secret [..]. (and see 5.2, 5.3)
*/
if (m_resumedSession)
{
if (!m_sessionParameters.IsExtendedMasterSecret)
{
/*
* TODO[resumption] ProvTlsServer currently only resumes EMS sessions. Revisit this
* in relation to 'tlsServer.allowLegacyResumption()'.
*/
throw new TlsFatalAlert(AlertDescription.internal_error);
}
if (!offeredExtendedMasterSecret)
throw new TlsFatalAlert(AlertDescription.handshake_failure);
securityParameters.m_extendedMasterSecret = true;
TlsExtensionsUtilities.AddExtendedMasterSecretExtension(m_serverExtensions);
}
else
{
securityParameters.m_extendedMasterSecret = offeredExtendedMasterSecret && !serverVersion.IsSsl
&& m_tlsServer.ShouldUseExtendedMasterSecret();
if (securityParameters.IsExtendedMasterSecret)
{
TlsExtensionsUtilities.AddExtendedMasterSecretExtension(m_serverExtensions);
}
else if (m_tlsServer.RequiresExtendedMasterSecret())
{
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
}
securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(m_serverExtensions);
securityParameters.m_applicationProtocolSet = true;
if (m_serverExtensions.Count > 0)
{
securityParameters.m_encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(
m_serverExtensions);
securityParameters.m_maxFragmentLength = ProcessMaxFragmentLengthExtension(m_clientExtensions,
m_serverExtensions, AlertDescription.internal_error);
securityParameters.m_truncatedHmac = TlsExtensionsUtilities.HasTruncatedHmacExtension(
m_serverExtensions);
if (!m_resumedSession)
{
if (TlsUtilities.HasExpectedEmptyExtensionData(m_serverExtensions, ExtensionType.status_request_v2,
AlertDescription.internal_error))
{
securityParameters.m_statusRequestVersion = 2;
}
else if (TlsUtilities.HasExpectedEmptyExtensionData(m_serverExtensions, ExtensionType.status_request,
AlertDescription.internal_error))
{
securityParameters.m_statusRequestVersion = 1;
}
this.m_expectSessionTicket = TlsUtilities.HasExpectedEmptyExtensionData(m_serverExtensions,
ExtensionType.session_ticket, AlertDescription.internal_error);
}
}
ApplyMaxFragmentLengthExtension(securityParameters.MaxFragmentLength);
return new ServerHello(serverVersion, securityParameters.ServerRandom, m_tlsSession.SessionID,
securityParameters.CipherSuite, m_serverExtensions);
}
protected override TlsContext Context
{
get { return m_tlsServerContext; }
}
internal override AbstractTlsContext ContextAdmin
{
get { return m_tlsServerContext; }
}
protected override TlsPeer Peer
{
get { return m_tlsServer; }
}
///
protected virtual void Handle13HandshakeMessage(short type, HandshakeMessageInput buf)
{
if (!IsTlsV13ConnectionState())
throw new TlsFatalAlert(AlertDescription.internal_error);
if (m_resumedSession)
{
/*
* TODO[tls13] Abbreviated handshakes (PSK resumption)
*
* NOTE: No CertificateRequest, Certificate, CertificateVerify messages, but client
* might now send EndOfEarlyData after receiving server Finished message.
*/
throw new TlsFatalAlert(AlertDescription.internal_error);
}
switch (type)
{
case HandshakeType.certificate:
{
switch (m_connectionState)
{
case CS_SERVER_FINISHED:
{
Receive13ClientCertificate(buf);
this.m_connectionState = CS_CLIENT_CERTIFICATE;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.certificate_verify:
{
switch (m_connectionState)
{
case CS_CLIENT_CERTIFICATE:
{
Receive13ClientCertificateVerify(buf);
buf.UpdateHash(m_handshakeHash);
this.m_connectionState = CS_CLIENT_CERTIFICATE_VERIFY;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.client_hello:
{
switch (m_connectionState)
{
case CS_START:
{
// NOTE: Legacy handler should be dispatching initial ClientHello.
throw new TlsFatalAlert(AlertDescription.internal_error);
}
case CS_SERVER_HELLO_RETRY_REQUEST:
{
ClientHello clientHelloRetry = ReceiveClientHelloMessage(buf);
this.m_connectionState = CS_CLIENT_HELLO_RETRY;
ServerHello serverHello = Generate13ServerHello(clientHelloRetry, buf, true);
SendServerHelloMessage(serverHello);
this.m_connectionState = CS_SERVER_HELLO;
Send13ServerHelloCoda(serverHello, true);
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.finished:
{
switch (m_connectionState)
{
case CS_SERVER_FINISHED:
case CS_CLIENT_CERTIFICATE:
case CS_CLIENT_CERTIFICATE_VERIFY:
{
if (m_connectionState == CS_SERVER_FINISHED)
{
Skip13ClientCertificate();
}
if (m_connectionState != CS_CLIENT_CERTIFICATE_VERIFY)
{
Skip13ClientCertificateVerify();
}
Receive13ClientFinished(buf);
this.m_connectionState = CS_CLIENT_FINISHED;
// See RFC 8446 D.4.
m_recordStream.SetIgnoreChangeCipherSpec(false);
// NOTE: Completes the switch to application-data phase (server entered after CS_SERVER_FINISHED).
m_recordStream.EnablePendingCipherRead(false);
CompleteHandshake();
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.key_update:
{
Receive13KeyUpdate(buf);
break;
}
case HandshakeType.certificate_request:
case HandshakeType.certificate_status:
case HandshakeType.certificate_url:
case HandshakeType.client_key_exchange:
case HandshakeType.encrypted_extensions:
case HandshakeType.end_of_early_data:
case HandshakeType.hello_request:
case HandshakeType.hello_verify_request:
case HandshakeType.message_hash:
case HandshakeType.new_session_ticket:
case HandshakeType.server_hello:
case HandshakeType.server_hello_done:
case HandshakeType.server_key_exchange:
case HandshakeType.supplemental_data:
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
protected override void HandleHandshakeMessage(short type, HandshakeMessageInput buf)
{
SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
if (m_connectionState > CS_CLIENT_HELLO
&& TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
{
Handle13HandshakeMessage(type, buf);
return;
}
if (!IsLegacyConnectionState())
throw new TlsFatalAlert(AlertDescription.internal_error);
if (m_resumedSession)
{
if (type != HandshakeType.finished || m_connectionState != CS_SERVER_FINISHED)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
ProcessFinishedMessage(buf);
this.m_connectionState = CS_CLIENT_FINISHED;
CompleteHandshake();
return;
}
switch (type)
{
case HandshakeType.client_hello:
{
if (IsApplicationDataReady)
{
RefuseRenegotiation();
break;
}
switch (m_connectionState)
{
case CS_END:
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
case CS_START:
{
ClientHello clientHello = ReceiveClientHelloMessage(buf);
this.m_connectionState = CS_CLIENT_HELLO;
ServerHello serverHello = GenerateServerHello(clientHello, buf);
m_handshakeHash.NotifyPrfDetermined();
if (TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
{
m_handshakeHash.SealHashAlgorithms();
if (serverHello.IsHelloRetryRequest())
{
TlsUtilities.AdjustTranscriptForRetry(m_handshakeHash);
SendServerHelloMessage(serverHello);
this.m_connectionState = CS_SERVER_HELLO_RETRY_REQUEST;
// See RFC 8446 D.4.
SendChangeCipherSpecMessage();
}
else
{
SendServerHelloMessage(serverHello);
this.m_connectionState = CS_SERVER_HELLO;
// See RFC 8446 D.4.
SendChangeCipherSpecMessage();
Send13ServerHelloCoda(serverHello, false);
}
break;
}
// For TLS 1.3+, this was already done by GenerateServerHello
buf.UpdateHash(m_handshakeHash);
SendServerHelloMessage(serverHello);
this.m_connectionState = CS_SERVER_HELLO;
if (m_resumedSession)
{
securityParameters.m_masterSecret = m_sessionMasterSecret;
m_recordStream.SetPendingCipher(TlsUtilities.InitCipher(m_tlsServerContext));
SendChangeCipherSpec();
SendFinishedMessage();
this.m_connectionState = CS_SERVER_FINISHED;
break;
}
IList serverSupplementalData = m_tlsServer.GetServerSupplementalData();
if (serverSupplementalData != null)
{
SendSupplementalDataMessage(serverSupplementalData);
this.m_connectionState = CS_SERVER_SUPPLEMENTAL_DATA;
}
this.m_keyExchange = TlsUtilities.InitKeyExchangeServer(m_tlsServerContext, m_tlsServer);
TlsCredentials serverCredentials = TlsUtilities.EstablishServerCredentials(m_tlsServer);
// Server certificate
{
Certificate serverCertificate = null;
MemoryStream endPointHash = new MemoryStream();
if (null == serverCredentials)
{
m_keyExchange.SkipServerCredentials();
}
else
{
m_keyExchange.ProcessServerCredentials(serverCredentials);
serverCertificate = serverCredentials.Certificate;
SendCertificateMessage(serverCertificate, endPointHash);
this.m_connectionState = CS_SERVER_CERTIFICATE;
}
securityParameters.m_tlsServerEndPoint = endPointHash.ToArray();
// TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes
// CertificateStatus
if (null == serverCertificate || serverCertificate.IsEmpty)
{
securityParameters.m_statusRequestVersion = 0;
}
}
if (securityParameters.StatusRequestVersion > 0)
{
CertificateStatus certificateStatus = m_tlsServer.GetCertificateStatus();
if (certificateStatus != null)
{
SendCertificateStatusMessage(certificateStatus);
this.m_connectionState = CS_SERVER_CERTIFICATE_STATUS;
}
}
byte[] serverKeyExchange = m_keyExchange.GenerateServerKeyExchange();
if (serverKeyExchange != null)
{
SendServerKeyExchangeMessage(serverKeyExchange);
this.m_connectionState = CS_SERVER_KEY_EXCHANGE;
}
if (null != serverCredentials)
{
this.m_certificateRequest = m_tlsServer.GetCertificateRequest();
if (null == m_certificateRequest)
{
/*
* For static agreement key exchanges, CertificateRequest is required since
* the client Certificate message is mandatory but can only be sent if the
* server requests it.
*/
if (!m_keyExchange.RequiresCertificateVerify)
throw new TlsFatalAlert(AlertDescription.internal_error);
}
else
{
if (TlsUtilities.IsTlsV12(m_tlsServerContext)
!= (m_certificateRequest.SupportedSignatureAlgorithms != null))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
this.m_certificateRequest = TlsUtilities.ValidateCertificateRequest(m_certificateRequest,
m_keyExchange);
TlsUtilities.EstablishServerSigAlgs(securityParameters, m_certificateRequest);
TlsUtilities.TrackHashAlgorithms(m_handshakeHash, securityParameters.ServerSigAlgs);
SendCertificateRequestMessage(m_certificateRequest);
this.m_connectionState = CS_SERVER_CERTIFICATE_REQUEST;
}
}
SendServerHelloDoneMessage();
this.m_connectionState = CS_SERVER_HELLO_DONE;
bool forceBuffering = false;
TlsUtilities.SealHandshakeHash(m_tlsServerContext, m_handshakeHash, forceBuffering);
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.supplemental_data:
{
switch (m_connectionState)
{
case CS_SERVER_HELLO_DONE:
{
m_tlsServer.ProcessClientSupplementalData(ReadSupplementalDataMessage(buf));
this.m_connectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.certificate:
{
switch (m_connectionState)
{
case CS_SERVER_HELLO_DONE:
case CS_CLIENT_SUPPLEMENTAL_DATA:
{
if (m_connectionState != CS_CLIENT_SUPPLEMENTAL_DATA)
{
m_tlsServer.ProcessClientSupplementalData(null);
}
ReceiveCertificateMessage(buf);
this.m_connectionState = CS_CLIENT_CERTIFICATE;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.client_key_exchange:
{
switch (m_connectionState)
{
case CS_SERVER_HELLO_DONE:
case CS_CLIENT_SUPPLEMENTAL_DATA:
case CS_CLIENT_CERTIFICATE:
{
if (m_connectionState == CS_SERVER_HELLO_DONE)
{
m_tlsServer.ProcessClientSupplementalData(null);
}
if (m_connectionState != CS_CLIENT_CERTIFICATE)
{
if (null == m_certificateRequest)
{
m_keyExchange.SkipClientCredentials();
}
else if (TlsUtilities.IsTlsV12(m_tlsServerContext))
{
/*
* RFC 5246 If no suitable certificate is available, the client MUST send a
* certificate message containing no certificates.
*
* NOTE: In previous RFCs, this was SHOULD instead of MUST.
*/
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
else if (TlsUtilities.IsSsl(m_tlsServerContext))
{
/*
* SSL 3.0 If the server has sent a certificate request Message, the client must
* send either the certificate message or a no_certificate alert.
*/
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
else
{
NotifyClientCertificate(Certificate.EmptyChain);
}
}
ReceiveClientKeyExchangeMessage(buf);
this.m_connectionState = CS_CLIENT_KEY_EXCHANGE;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.certificate_verify:
{
switch (m_connectionState)
{
case CS_CLIENT_KEY_EXCHANGE:
{
/*
* RFC 5246 7.4.8 This message is only sent following a client certificate that has
* signing capability (i.e., all certificates except those containing fixed
* Diffie-Hellman parameters).
*/
if (!ExpectCertificateVerifyMessage())
throw new TlsFatalAlert(AlertDescription.unexpected_message);
ReceiveCertificateVerifyMessage(buf);
buf.UpdateHash(m_handshakeHash);
this.m_connectionState = CS_CLIENT_CERTIFICATE_VERIFY;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.finished:
{
switch (m_connectionState)
{
case CS_CLIENT_KEY_EXCHANGE:
case CS_CLIENT_CERTIFICATE_VERIFY:
{
if (m_connectionState != CS_CLIENT_CERTIFICATE_VERIFY)
{
if (ExpectCertificateVerifyMessage())
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
ProcessFinishedMessage(buf);
buf.UpdateHash(m_handshakeHash);
this.m_connectionState = CS_CLIENT_FINISHED;
if (m_expectSessionTicket)
{
/*
* TODO[new_session_ticket] Check the server-side rules regarding the session ID, since
* the client is going to ignore any session ID it received once it sees the
* new_session_ticket message.
*/
SendNewSessionTicketMessage(m_tlsServer.GetNewSessionTicket());
this.m_connectionState = CS_SERVER_SESSION_TICKET;
}
SendChangeCipherSpec();
SendFinishedMessage();
this.m_connectionState = CS_SERVER_FINISHED;
CompleteHandshake();
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.certificate_request:
case HandshakeType.certificate_status:
case HandshakeType.certificate_url:
case HandshakeType.encrypted_extensions:
case HandshakeType.end_of_early_data:
case HandshakeType.hello_request:
case HandshakeType.hello_verify_request:
case HandshakeType.key_update:
case HandshakeType.message_hash:
case HandshakeType.new_session_ticket:
case HandshakeType.server_hello:
case HandshakeType.server_hello_done:
case HandshakeType.server_key_exchange:
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
protected override void HandleAlertWarningMessage(short alertDescription)
{
/*
* SSL 3.0 If the server has sent a certificate request Message, the client must send
* either the certificate message or a no_certificate alert.
*/
if (AlertDescription.no_certificate == alertDescription && null != m_certificateRequest
&& TlsUtilities.IsSsl(m_tlsServerContext))
{
switch (m_connectionState)
{
case CS_SERVER_HELLO_DONE:
case CS_CLIENT_SUPPLEMENTAL_DATA:
{
if (m_connectionState != CS_CLIENT_SUPPLEMENTAL_DATA)
{
m_tlsServer.ProcessClientSupplementalData(null);
}
NotifyClientCertificate(Certificate.EmptyChain);
this.m_connectionState = CS_CLIENT_CERTIFICATE;
return;
}
}
}
base.HandleAlertWarningMessage(alertDescription);
}
///
protected virtual void NotifyClientCertificate(Certificate clientCertificate)
{
if (null == m_certificateRequest)
throw new TlsFatalAlert(AlertDescription.internal_error);
TlsUtilities.ProcessClientCertificate(m_tlsServerContext, clientCertificate, m_keyExchange, m_tlsServer);
}
///
protected virtual void Receive13ClientCertificate(MemoryStream buf)
{
// TODO[tls13] This currently just duplicates 'receiveCertificateMessage'
if (null == m_certificateRequest)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
Certificate.ParseOptions options = new Certificate.ParseOptions()
.SetMaxChainLength(m_tlsServer.GetMaxCertificateChainLength());
Certificate clientCertificate = Certificate.Parse(options, m_tlsServerContext, buf, null);
AssertEmpty(buf);
NotifyClientCertificate(clientCertificate);
}
///
protected void Receive13ClientCertificateVerify(MemoryStream buf)
{
Certificate clientCertificate = m_tlsServerContext.SecurityParameters.PeerCertificate;
if (null == clientCertificate || clientCertificate.IsEmpty)
throw new TlsFatalAlert(AlertDescription.internal_error);
// TODO[tls13] Actual structure is 'CertificateVerify' in RFC 8446, consider adding for clarity
DigitallySigned certificateVerify = DigitallySigned.Parse(m_tlsServerContext, buf);
AssertEmpty(buf);
TlsUtilities.Verify13CertificateVerifyClient(m_tlsServerContext, m_certificateRequest, certificateVerify,
m_handshakeHash);
}
///
protected virtual void Receive13ClientFinished(MemoryStream buf)
{
Process13FinishedMessage(buf);
}
///
protected virtual void ReceiveCertificateMessage(MemoryStream buf)
{
if (null == m_certificateRequest)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
Certificate.ParseOptions options = new Certificate.ParseOptions()
.SetMaxChainLength(m_tlsServer.GetMaxCertificateChainLength());
Certificate clientCertificate = Certificate.Parse(options, m_tlsServerContext, buf, null);
AssertEmpty(buf);
NotifyClientCertificate(clientCertificate);
}
///
protected virtual void ReceiveCertificateVerifyMessage(MemoryStream buf)
{
DigitallySigned certificateVerify = DigitallySigned.Parse(m_tlsServerContext, buf);
AssertEmpty(buf);
TlsUtilities.VerifyCertificateVerifyClient(m_tlsServerContext, m_certificateRequest, certificateVerify,
m_handshakeHash);
this.m_handshakeHash = m_handshakeHash.StopTracking();
}
///
protected virtual ClientHello ReceiveClientHelloMessage(MemoryStream buf)
{
return ClientHello.Parse(buf, null);
}
///
protected virtual void ReceiveClientKeyExchangeMessage(MemoryStream buf)
{
m_keyExchange.ProcessClientKeyExchange(buf);
AssertEmpty(buf);
bool isSsl = TlsUtilities.IsSsl(m_tlsServerContext);
if (isSsl)
{
// NOTE: For SSLv3 (only), master_secret needed to calculate session hash
EstablishMasterSecret(m_tlsServerContext, m_keyExchange);
}
m_tlsServerContext.SecurityParameters.m_sessionHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
if (!isSsl)
{
// NOTE: For (D)TLS, session hash potentially needed for extended_master_secret
EstablishMasterSecret(m_tlsServerContext, m_keyExchange);
}
m_recordStream.SetPendingCipher(TlsUtilities.InitCipher(m_tlsServerContext));
if (!ExpectCertificateVerifyMessage())
{
this.m_handshakeHash = m_handshakeHash.StopTracking();
}
}
///
protected virtual void Send13EncryptedExtensionsMessage(IDictionary serverExtensions)
{
// TODO[tls13] Avoid extra copy; use placeholder to write opaque-16 data directly to message buffer
byte[] extBytes = WriteExtensionsData(serverExtensions);
HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.encrypted_extensions);
TlsUtilities.WriteOpaque16(extBytes, message);
message.Send(this);
}
///
protected virtual void Send13ServerHelloCoda(ServerHello serverHello, bool afterHelloRetryRequest)
{
SecurityParameters securityParameters = m_tlsServerContext.SecurityParameters;
byte[] serverHelloTranscriptHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
TlsUtilities.Establish13PhaseHandshake(m_tlsServerContext, serverHelloTranscriptHash, m_recordStream);
m_recordStream.EnablePendingCipherWrite();
m_recordStream.EnablePendingCipherRead(true);
Send13EncryptedExtensionsMessage(m_serverExtensions);
this.m_connectionState = CS_SERVER_ENCRYPTED_EXTENSIONS;
if (m_selectedPsk13)
{
/*
* For PSK-only key exchange, there's no CertificateRequest, Certificate, CertificateVerify.
*/
}
else
{
// CertificateRequest
{
this.m_certificateRequest = m_tlsServer.GetCertificateRequest();
if (null != m_certificateRequest)
{
if (!m_certificateRequest.HasCertificateRequestContext(TlsUtilities.EmptyBytes))
throw new TlsFatalAlert(AlertDescription.internal_error);
TlsUtilities.EstablishServerSigAlgs(securityParameters, m_certificateRequest);
SendCertificateRequestMessage(m_certificateRequest);
this.m_connectionState = CS_SERVER_CERTIFICATE_REQUEST;
}
}
TlsCredentialedSigner serverCredentials = TlsUtilities.Establish13ServerCredentials(m_tlsServer);
if (null == serverCredentials)
throw new TlsFatalAlert(AlertDescription.internal_error);
// Certificate
{
/*
* TODO[tls13] Note that we are expecting the TlsServer implementation to take care of
* e.g. adding optional "status_request" extension to each CertificateEntry.
*/
/*
* No CertificateStatus message is sent; TLS 1.3 uses per-CertificateEntry
* "status_request" extension instead.
*/
Certificate serverCertificate = serverCredentials.Certificate;
Send13CertificateMessage(serverCertificate);
securityParameters.m_tlsServerEndPoint = null;
this.m_connectionState = CS_SERVER_CERTIFICATE;
}
// CertificateVerify
{
DigitallySigned certificateVerify = TlsUtilities.Generate13CertificateVerify(m_tlsServerContext,
serverCredentials, m_handshakeHash);
Send13CertificateVerifyMessage(certificateVerify);
this.m_connectionState = CS_CLIENT_CERTIFICATE_VERIFY;
}
}
// Finished
{
Send13FinishedMessage();
this.m_connectionState = CS_SERVER_FINISHED;
}
byte[] serverFinishedTranscriptHash = TlsUtilities.GetCurrentPrfHash(m_handshakeHash);
TlsUtilities.Establish13PhaseApplication(m_tlsServerContext, serverFinishedTranscriptHash, m_recordStream);
m_recordStream.EnablePendingCipherWrite();
}
///
protected virtual void SendCertificateRequestMessage(CertificateRequest certificateRequest)
{
HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate_request);
certificateRequest.Encode(m_tlsServerContext, message);
message.Send(this);
}
///
protected virtual void SendCertificateStatusMessage(CertificateStatus certificateStatus)
{
HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate_status);
// TODO[tls13] Ensure this cannot happen for (D)TLS1.3+
certificateStatus.Encode(message);
message.Send(this);
}
///
protected virtual void SendHelloRequestMessage()
{
HandshakeMessageOutput.Send(this, HandshakeType.hello_request, TlsUtilities.EmptyBytes);
}
///
protected virtual void SendNewSessionTicketMessage(NewSessionTicket newSessionTicket)
{
if (newSessionTicket == null)
throw new TlsFatalAlert(AlertDescription.internal_error);
HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.new_session_ticket);
newSessionTicket.Encode(message);
message.Send(this);
}
///
protected virtual void SendServerHelloDoneMessage()
{
HandshakeMessageOutput.Send(this, HandshakeType.server_hello_done, TlsUtilities.EmptyBytes);
}
///
protected virtual void SendServerHelloMessage(ServerHello serverHello)
{
HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.server_hello);
serverHello.Encode(m_tlsServerContext, message);
message.Send(this);
}
///
protected virtual void SendServerKeyExchangeMessage(byte[] serverKeyExchange)
{
HandshakeMessageOutput.Send(this, HandshakeType.server_key_exchange, serverKeyExchange);
}
///
protected virtual void Skip13ClientCertificate()
{
if (null != m_certificateRequest)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
///
protected virtual void Skip13ClientCertificateVerify()
{
if (ExpectCertificateVerifyMessage())
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
}
#pragma warning restore
#endif