You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1528 lines
64 KiB
1528 lines
64 KiB
1 year ago
|
#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;
|
||
|
|
||
|
/// <summary>Constructor for non-blocking mode.</summary>
|
||
|
/// <remarks>
|
||
|
/// When data is received, use <see cref="TlsProtocol.OfferInput(byte[])"/> to provide the received ciphertext,
|
||
|
/// then use <see cref="TlsProtocol.ReadInput(byte[],int,int)"/> to read the corresponding cleartext.<br/><br/>
|
||
|
/// Similarly, when data needs to be sent, use <see cref="TlsProtocol.WriteApplicationData(byte[],int,int)"/>
|
||
|
/// to provide the cleartext, then use <see cref="TlsProtocol.ReadOutput(byte[],int,int)"/> to get the
|
||
|
/// corresponding ciphertext.
|
||
|
/// </remarks>
|
||
|
public TlsServerProtocol()
|
||
|
: base()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/// <summary>Constructor for blocking mode.</summary>
|
||
|
/// <param name="stream">The <see cref="Stream"/> of data to/from the server.</param>
|
||
|
public TlsServerProtocol(Stream stream)
|
||
|
: base(stream)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/// <summary>Constructor for blocking mode.</summary>
|
||
|
/// <param name="input">The <see cref="Stream"/> of data from the server.</param>
|
||
|
/// <param name="output">The <see cref="Stream"/> of data to the server.</param>
|
||
|
public TlsServerProtocol(Stream input, Stream output)
|
||
|
: base(input, output)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/// <summary>Receives a TLS handshake in the role of server.</summary>
|
||
|
/// <remarks>
|
||
|
/// In blocking mode, this will not return until the handshake is complete. In non-blocking mode, use
|
||
|
/// <see cref="TlsPeer.NotifyHandshakeComplete"/> to receive a callback when the handshake is complete.
|
||
|
/// </remarks>
|
||
|
/// <param name="tlsServer">The <see cref="TlsServer"/> to use for the handshake.</param>
|
||
|
/// <exception cref="IOException">If in blocking mode and handshake was not successful.</exception>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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; }
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual void Receive13ClientFinished(MemoryStream buf)
|
||
|
{
|
||
|
Process13FinishedMessage(buf);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual ClientHello ReceiveClientHelloMessage(MemoryStream buf)
|
||
|
{
|
||
|
return ClientHello.Parse(buf, null);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual void SendCertificateRequestMessage(CertificateRequest certificateRequest)
|
||
|
{
|
||
|
HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.certificate_request);
|
||
|
certificateRequest.Encode(m_tlsServerContext, message);
|
||
|
message.Send(this);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual void SendHelloRequestMessage()
|
||
|
{
|
||
|
HandshakeMessageOutput.Send(this, HandshakeType.hello_request, TlsUtilities.EmptyBytes);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual void SendServerHelloDoneMessage()
|
||
|
{
|
||
|
HandshakeMessageOutput.Send(this, HandshakeType.server_hello_done, TlsUtilities.EmptyBytes);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual void SendServerHelloMessage(ServerHello serverHello)
|
||
|
{
|
||
|
HandshakeMessageOutput message = new HandshakeMessageOutput(HandshakeType.server_hello);
|
||
|
serverHello.Encode(m_tlsServerContext, message);
|
||
|
message.Send(this);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual void SendServerKeyExchangeMessage(byte[] serverKeyExchange)
|
||
|
{
|
||
|
HandshakeMessageOutput.Send(this, HandshakeType.server_key_exchange, serverKeyExchange);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual void Skip13ClientCertificate()
|
||
|
{
|
||
|
if (null != m_certificateRequest)
|
||
|
throw new TlsFatalAlert(AlertDescription.unexpected_message);
|
||
|
}
|
||
|
|
||
|
/// <exception cref="IOException"/>
|
||
|
protected virtual void Skip13ClientCertificateVerify()
|
||
|
{
|
||
|
if (ExpectCertificateVerifyMessage())
|
||
|
throw new TlsFatalAlert(AlertDescription.unexpected_message);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#pragma warning restore
|
||
|
#endif
|