From 0af4be8e434b6c31949a9a948a6a493877f97e0f Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 10 Jul 2023 21:11:02 +0700 Subject: DTLS: server support for session resumption --- crypto/src/tls/DtlsClientProtocol.cs | 3 +- crypto/src/tls/DtlsServerProtocol.cs | 147 +++++++++++++++++++++++++---------- 2 files changed, 107 insertions(+), 43 deletions(-) diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs index da39320ea..5c5686bbb 100644 --- a/crypto/src/tls/DtlsClientProtocol.cs +++ b/crypto/src/tls/DtlsClientProtocol.cs @@ -148,8 +148,7 @@ namespace Org.BouncyCastle.Tls clientContext.HandshakeComplete(client, state.tlsSession); - recordLayer.InitHeartbeat(state.heartbeat, - HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy); + recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy); return new DtlsTransport(recordLayer, client.IgnoreCorruptDtlsRecords); } diff --git a/crypto/src/tls/DtlsServerProtocol.cs b/crypto/src/tls/DtlsServerProtocol.cs index 8b2d9db2c..30d491990 100644 --- a/crypto/src/tls/DtlsServerProtocol.cs +++ b/crypto/src/tls/DtlsServerProtocol.cs @@ -114,10 +114,14 @@ namespace Org.BouncyCastle.Tls { throw new TlsFatalAlert(AlertDescription.unexpected_message); } + + clientMessage = null; } else { ProcessClientHello(state, request.ClientHello); + + request = null; } { @@ -135,6 +139,41 @@ namespace Org.BouncyCastle.Tls handshake.HandshakeHash.NotifyPrfDetermined(); + if (securityParameters.IsResumedSession) + { + securityParameters.m_masterSecret = state.sessionMasterSecret; + recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(serverContext)); + + // NOTE: Calculated exclusive of the Finished message itself + securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(serverContext, + handshake.HandshakeHash, true); + handshake.SendMessage(HandshakeType.finished, securityParameters.LocalVerifyData); + + // NOTE: Calculated exclusive of the actual Finished message from the client + securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(serverContext, + handshake.HandshakeHash, false); + ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), + securityParameters.PeerVerifyData); + + handshake.Finish(); + + if (securityParameters.IsExtendedMasterSecret) + { + securityParameters.m_tlsUnique = securityParameters.LocalVerifyData; + } + + securityParameters.m_localCertificate = state.sessionParameters.LocalCertificate; + securityParameters.m_peerCertificate = state.sessionParameters.PeerCertificate; + securityParameters.m_pskIdentity = state.sessionParameters.PskIdentity; + securityParameters.m_srpIdentity = state.sessionParameters.SrpIdentity; + + serverContext.HandshakeComplete(server, state.tlsSession); + + recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy); + + return new DtlsTransport(recordLayer, server.IgnoreCorruptDtlsRecords); + } + var serverSupplementalData = server.GetServerSupplementalData(); if (serverSupplementalData != null) { @@ -322,6 +361,8 @@ namespace Org.BouncyCastle.Tls } } + clientMessage = null; + // NOTE: Calculated exclusive of the actual Finished message from the client securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(serverContext, handshake.HandshakeHash, false); @@ -450,20 +491,11 @@ namespace Org.BouncyCastle.Tls } } - /* - * 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[resumption] - { - state.tlsSession = null; - state.sessionParameters = null; - state.sessionMasterSecret = null; - } + var clientHelloExtensions = state.clientHello.Extensions; - bool resumedSession = false; + TlsSession sessionToResume = server.GetSessionToResume(state.clientHello.SessionID); + + bool resumedSession = EstablishSession(state, sessionToResume); if (resumedSession && !serverVersion.Equals(state.sessionParameters.NegotiatedVersion)) { @@ -481,7 +513,7 @@ namespace Org.BouncyCastle.Tls if (TlsUtilities.IsExtendedMasterSecretOptional(serverVersion) && server.ShouldUseExtendedMasterSecret()) { - if (TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions)) + if (TlsExtensionsUtilities.HasExtendedMasterSecretExtension(clientHelloExtensions)) { negotiateEms = true; } @@ -538,7 +570,7 @@ namespace Org.BouncyCastle.Tls int cipherSuite = ValidateSelectedCipherSuite(server.GetSelectedCipherSuite(), AlertDescription.internal_error); - if (!TlsUtilities.IsValidCipherSuiteSelection(state.offeredCipherSuites, cipherSuite) || + if (!TlsUtilities.IsValidCipherSuiteSelection(state.clientHello.CipherSuites, cipherSuite) || !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, securityParameters.NegotiatedVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); @@ -618,7 +650,7 @@ namespace Org.BouncyCastle.Tls var serverConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.serverExtensions); if (serverConnectionID != null) { - var clientConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.clientExtensions) + var clientConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(clientHelloExtensions) ?? throw new TlsFatalAlert(AlertDescription.internal_error); securityParameters.m_connectionIDLocal = clientConnectionID; @@ -632,7 +664,7 @@ namespace Org.BouncyCastle.Tls state.serverExtensions); securityParameters.m_maxFragmentLength = TlsUtilities.ProcessMaxFragmentLengthExtension( - resumedSession ? null : state.clientExtensions, state.serverExtensions, + resumedSession ? null : clientHelloExtensions, state.serverExtensions, AlertDescription.internal_error); securityParameters.m_truncatedHmac = TlsExtensionsUtilities.HasTruncatedHmacExtension( @@ -657,11 +689,13 @@ namespace Org.BouncyCastle.Tls } } - ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength); - ServerHello serverHello = new ServerHello(serverVersion, securityParameters.ServerRandom, securityParameters.SessionID, securityParameters.CipherSuite, state.serverExtensions); + state.clientHello = null; + + ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength); + MemoryStream buf = new MemoryStream(); serverHello.Encode(serverContext, buf); return buf.ToArray(); @@ -684,6 +718,42 @@ namespace Org.BouncyCastle.Tls state.tlsSession = null; } + protected virtual bool EstablishSession(ServerHandshakeState state, TlsSession sessionToResume) + { + state.tlsSession = null; + state.sessionParameters = null; + state.sessionMasterSecret = null; + + if (null == sessionToResume || !sessionToResume.IsResumable) + return false; + + SessionParameters sessionParameters = sessionToResume.ExportSessionParameters(); + if (null == sessionParameters) + return false; + + ProtocolVersion sessionVersion = sessionParameters.NegotiatedVersion; + if (null == sessionVersion || !sessionVersion.IsDtls) + return false; + + bool isEms = sessionParameters.IsExtendedMasterSecret; + if (!TlsUtilities.IsExtendedMasterSecretOptional(sessionVersion)) + { + if (!isEms) + return false; + } + + TlsCrypto crypto = state.serverContext.Crypto; + TlsSecret sessionMasterSecret = TlsUtilities.GetSessionMasterSecret(crypto, sessionParameters.MasterSecret); + if (null == sessionMasterSecret) + return false; + + state.tlsSession = sessionToResume; + state.sessionParameters = sessionParameters; + state.sessionMasterSecret = sessionMasterSecret; + + return true; + } + protected virtual void InvalidateSession(ServerHandshakeState state) { if (state.tlsSession != null) @@ -752,16 +822,12 @@ namespace Org.BouncyCastle.Tls /// protected virtual void ProcessClientHello(ServerHandshakeState state, ClientHello clientHello) { + state.clientHello = clientHello; + // TODO Read RFCs for guidance on the expected record layer version number ProtocolVersion legacy_version = clientHello.Version; - state.offeredCipherSuites = clientHello.CipherSuites; - - /* - * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore - * extensions appearing in the client hello, and send a server hello containing no - * extensions. - */ - state.clientExtensions = clientHello.Extensions; + int[] offeredCipherSuites = clientHello.CipherSuites; + var clientHelloExtensions = clientHello.Extensions; @@ -775,7 +841,7 @@ namespace Org.BouncyCastle.Tls serverContext.SetRsaPreMasterSecretVersion(legacy_version); serverContext.SetClientSupportedVersions( - TlsExtensionsUtilities.GetSupportedVersionsExtensionClient(state.clientExtensions)); + TlsExtensionsUtilities.GetSupportedVersionsExtensionClient(clientHelloExtensions)); ProtocolVersion client_version = legacy_version; if (null == serverContext.ClientSupportedVersions) @@ -801,15 +867,15 @@ namespace Org.BouncyCastle.Tls securityParameters.m_clientRandom = clientHello.Random; - server.NotifyFallback(Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)); + server.NotifyFallback(Arrays.Contains(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)); - server.NotifyOfferedCipherSuites(state.offeredCipherSuites); + server.NotifyOfferedCipherSuites(offeredCipherSuites); /* * TODO[resumption] Check RFC 7627 5.4. for required behaviour */ - byte[] clientRenegExtData = TlsUtilities.GetExtensionData(state.clientExtensions, + byte[] clientRenegExtData = TlsUtilities.GetExtensionData(clientHelloExtensions, ExtensionType.renegotiation_info); // NOT renegotiatiing @@ -829,7 +895,7 @@ namespace Org.BouncyCastle.Tls * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag * to TRUE. */ - if (Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) + if (Arrays.Contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { securityParameters.m_secureRenegotiation = true; } @@ -853,13 +919,13 @@ namespace Org.BouncyCastle.Tls server.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation); - if (state.clientExtensions != null) + if (clientHelloExtensions != null) { // NOTE: Validates the padding extension data, if present - TlsExtensionsUtilities.GetPaddingExtension(state.clientExtensions); + TlsExtensionsUtilities.GetPaddingExtension(clientHelloExtensions); securityParameters.m_clientServerNames = TlsExtensionsUtilities.GetServerNameExtensionClient( - state.clientExtensions); + clientHelloExtensions); /* * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior @@ -867,16 +933,16 @@ namespace Org.BouncyCastle.Tls */ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(client_version)) { - TlsUtilities.EstablishClientSigAlgs(securityParameters, state.clientExtensions); + TlsUtilities.EstablishClientSigAlgs(securityParameters, clientHelloExtensions); } securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension( - state.clientExtensions); + clientHelloExtensions); // Heartbeats { HeartbeatExtension heartbeatExtension = TlsExtensionsUtilities.GetHeartbeatExtension( - state.clientExtensions); + clientHelloExtensions); if (null != heartbeatExtension) { if (HeartbeatMode.peer_allowed_to_send == heartbeatExtension.Mode) @@ -888,7 +954,7 @@ namespace Org.BouncyCastle.Tls } } - server.ProcessClientExtensions(state.clientExtensions); + server.ProcessClientExtensions(clientHelloExtensions); } } @@ -927,8 +993,7 @@ namespace Org.BouncyCastle.Tls internal SessionParameters sessionParameters = null; internal TlsSecret sessionMasterSecret = null; internal SessionParameters.Builder sessionParametersBuilder = null; - internal int[] offeredCipherSuites = null; - internal IDictionary clientExtensions = null; + internal ClientHello clientHello = null; internal IDictionary serverExtensions = null; internal bool expectSessionTicket = false; internal TlsKeyExchange keyExchange = null; -- cgit 1.4.1