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.
1527 lines
64 KiB
1527 lines
64 KiB
#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
|
|
|