From 6394a2e60292d0f0a20e74f66a28a8a10e8691a2 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 27 Jul 2021 17:36:09 +0700 Subject: Add pre_shared_key to ClientHello --- crypto/src/tls/DtlsClientProtocol.cs | 2 +- crypto/src/tls/OfferedPsks.cs | 15 ++++--- crypto/src/tls/SecurityParameters.cs | 7 ---- crypto/src/tls/TlsClientProtocol.cs | 76 ++++++++++++++++++++++++++++-------- crypto/src/tls/TlsServerProtocol.cs | 14 ++++--- crypto/src/tls/TlsUtilities.cs | 55 +++++++++++++++++++------- 6 files changed, 119 insertions(+), 50 deletions(-) (limited to 'crypto/src') diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs index cd2fff709..a4810b983 100644 --- a/crypto/src/tls/DtlsClientProtocol.cs +++ b/crypto/src/tls/DtlsClientProtocol.cs @@ -446,7 +446,7 @@ namespace Org.BouncyCastle.Tls securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension( state.clientExtensions); - state.clientAgreements = TlsUtilities.AddEarlyKeySharesToClientHello(state.clientContext, state.client, + state.clientAgreements = TlsUtilities.AddKeyShareToClientHello(state.clientContext, state.client, state.clientExtensions); if (TlsUtilities.IsExtendedMasterSecretOptionalDtls(context.ClientSupportedVersions) diff --git a/crypto/src/tls/OfferedPsks.cs b/crypto/src/tls/OfferedPsks.cs index dfa2be034..14b6448b4 100644 --- a/crypto/src/tls/OfferedPsks.cs +++ b/crypto/src/tls/OfferedPsks.cs @@ -9,15 +9,18 @@ namespace Org.BouncyCastle.Tls { public sealed class OfferedPsks { - internal class Config + internal class BindersConfig { internal readonly TlsPsk[] m_psks; + internal readonly short[] m_pskKeyExchangeModes; internal readonly TlsSecret[] m_earlySecrets; internal int m_bindersSize; - internal Config(TlsPsk[] psks, TlsSecret[] earlySecrets, int bindersSize) + internal BindersConfig(TlsPsk[] psks, short[] pskKeyExchangeModes, TlsSecret[] earlySecrets, + int bindersSize) { this.m_psks = psks; + this.m_pskKeyExchangeModes = pskKeyExchangeModes; this.m_earlySecrets = earlySecrets; this.m_bindersSize = bindersSize; } @@ -93,11 +96,11 @@ namespace Org.BouncyCastle.Tls /// internal static void EncodeBinders(Stream output, TlsCrypto crypto, TlsHandshakeHash handshakeHash, - Config config) + BindersConfig bindersConfig) { - TlsPsk[] psks = config.m_psks; - TlsSecret[] earlySecrets = config.m_earlySecrets; - int expectedLengthOfBindersList = config.m_bindersSize - 2; + TlsPsk[] psks = bindersConfig.m_psks; + TlsSecret[] earlySecrets = bindersConfig.m_earlySecrets; + int expectedLengthOfBindersList = bindersConfig.m_bindersSize - 2; TlsUtilities.CheckUint16(expectedLengthOfBindersList); TlsUtilities.WriteUint16(expectedLengthOfBindersList, output); diff --git a/crypto/src/tls/SecurityParameters.cs b/crypto/src/tls/SecurityParameters.cs index 23a83a65f..548e4a4ca 100644 --- a/crypto/src/tls/SecurityParameters.cs +++ b/crypto/src/tls/SecurityParameters.cs @@ -22,7 +22,6 @@ namespace Org.BouncyCastle.Tls internal TlsSecret m_exporterMasterSecret = null; internal TlsSecret m_handshakeSecret = null; internal TlsSecret m_masterSecret = null; - internal TlsSecret m_sharedSecret = null; internal TlsSecret m_trafficSecretClient = null; internal TlsSecret m_trafficSecretServer = null; internal byte[] m_clientRandom = null; @@ -78,7 +77,6 @@ namespace Org.BouncyCastle.Tls this.m_exporterMasterSecret = ClearSecret(m_exporterMasterSecret); this.m_handshakeSecret = ClearSecret(m_handshakeSecret); this.m_masterSecret = ClearSecret(m_masterSecret); - this.m_sharedSecret = ClearSecret(m_sharedSecret); } public ProtocolName ApplicationProtocol @@ -276,11 +274,6 @@ namespace Org.BouncyCastle.Tls get { return m_sessionID; } } - public TlsSecret SharedSecret - { - get { return m_sharedSecret; } - } - public byte[] SrpIdentity { get { return m_srpIdentity; } diff --git a/crypto/src/tls/TlsClientProtocol.cs b/crypto/src/tls/TlsClientProtocol.cs index 190a1927f..5356880ff 100644 --- a/crypto/src/tls/TlsClientProtocol.cs +++ b/crypto/src/tls/TlsClientProtocol.cs @@ -14,6 +14,7 @@ namespace Org.BouncyCastle.Tls internal TlsClientContextImpl m_tlsClientContext = null; protected IDictionary m_clientAgreements = null; + internal OfferedPsks.BindersConfig m_clientBinders = null; protected ClientHello m_clientHello = null; protected TlsKeyExchange m_keyExchange = null; protected TlsAuthentication m_authentication = null; @@ -90,6 +91,7 @@ namespace Org.BouncyCastle.Tls base.CleanupHandshake(); this.m_clientAgreements = null; + this.m_clientBinders = null; this.m_clientHello = null; this.m_keyExchange = null; this.m_authentication = null; @@ -832,6 +834,15 @@ namespace Org.BouncyCastle.Tls throw new TlsFatalAlert(AlertDescription.illegal_parameter); } + if (null != m_clientBinders) + { + if (!Arrays.Contains(m_clientBinders.m_pskKeyExchangeModes, PskKeyExchangeMode.psk_dhe_ke)) + { + // TODO[tls13-psk] Notify client that no PSK was selected. + this.m_clientBinders = null; + } + } + /* * RFC 8446 4.2.8. Upon receipt of this [Key Share] extension in a HelloRetryRequest, the * client MUST verify that (1) the selected_group field corresponds to a group which was @@ -939,10 +950,29 @@ namespace Org.BouncyCastle.Tls */ securityParameters.m_statusRequestVersion = m_clientExtensions.Contains(ExtensionType.status_request) ? 1 : 0; + // TODO[tls13-psk] Use PSK early secret if negotiated + TlsSecret pskEarlySecret = null; + + if (null != m_clientBinders) + { + // TODO[tls13-psk] Process the server's pre_shared_key response, if any + //int selected_identity = TlsExtensionsUtilities.GetPreSharedKeyServerHello(extensions); + + // TODO[tls13-psk] Notify client of selected PSK + // pskEarlySecret = ...; + + this.m_clientBinders = null; + } + + TlsSecret sharedSecret = null; + { KeyShareEntry keyShareEntry = TlsExtensionsUtilities.GetKeyShareServerHello(extensions); if (null == keyShareEntry) + { + // TODO[tls13-psk] This would be OK for PskKeyExchangeMode.psk_ke (and not after HRR) throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } if (!m_clientAgreements.Contains(keyShareEntry.NamedGroup)) throw new TlsFatalAlert(AlertDescription.illegal_parameter); @@ -952,14 +982,11 @@ namespace Org.BouncyCastle.Tls this.m_clientAgreements = null; agreement.ReceivePeerValue(keyShareEntry.KeyExchange); - securityParameters.m_sharedSecret = agreement.CalculateSecret(); - - // TODO[tls13-psk] Use PSK early secret if negotiated - TlsSecret pskEarlySecret = null; - - TlsUtilities.Establish13PhaseSecrets(m_tlsClientContext, pskEarlySecret); + sharedSecret = agreement.CalculateSecret(); } + TlsUtilities.Establish13PhaseSecrets(m_tlsClientContext, pskEarlySecret, sharedSecret); + { InvalidateSession(); @@ -1477,13 +1504,12 @@ namespace Org.BouncyCastle.Tls /// protected virtual void Send13ClientHelloRetry() { - // TODO[tls13-psk] Create a new ClientHello object and handle any changes to the bindersSize - IDictionary clientHelloExtensions = m_clientHello.Extensions; clientHelloExtensions.Remove(ExtensionType.cookie); clientHelloExtensions.Remove(ExtensionType.early_data); clientHelloExtensions.Remove(ExtensionType.key_share); + clientHelloExtensions.Remove(ExtensionType.pre_shared_key); /* * RFC 4.2.2. When sending the new ClientHello, the client MUST copy the contents of the @@ -1492,6 +1518,9 @@ namespace Org.BouncyCastle.Tls */ if (null != m_retryCookie) { + /* + * - Including a "cookie" extension if one was provided in the HelloRetryRequest. + */ TlsExtensionsUtilities.AddCookieExtension(clientHelloExtensions, m_retryCookie); this.m_retryCookie = null; } @@ -1504,14 +1533,24 @@ namespace Org.BouncyCastle.Tls if (m_retryGroup < 0) throw new TlsFatalAlert(AlertDescription.internal_error); + /* + * - If a "key_share" extension was supplied in the HelloRetryRequest, replacing the list of shares + * with a list containing a single KeyShareEntry from the indicated group + */ this.m_clientAgreements = TlsUtilities.AddKeyShareToClientHelloRetry(m_tlsClientContext, clientHelloExtensions, m_retryGroup); /* - * TODO[tls13] Updating the "pre_shared_key" extension if present by recomputing the - * "obfuscated_ticket_age" and binder values and (optionally) removing any PSKs which are - * incompatible with the server's indicated cipher suite. + * - Updating the "pre_shared_key" extension if present by recomputing the "obfuscated_ticket_age" + * and binder values and (optionally) removing any PSKs which are incompatible with the server's + * indicated cipher suite. */ + if (null != m_clientBinders) + { + // TODO[tls13-psk] + //this.m_clientBinders = TlsUtilities.AddPreSharedKeyToClientHelloRetry(m_tlsClientContext, + // m_clientBinders, clientHelloExtensions); + } /* * TODO[tls13] Optionally adding, removing, or changing the length of the "padding" @@ -1627,7 +1666,11 @@ namespace Org.BouncyCastle.Tls securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension( m_clientExtensions); - this.m_clientAgreements = TlsUtilities.AddEarlyKeySharesToClientHello(m_tlsClientContext, m_tlsClient, + this.m_clientBinders = TlsUtilities.AddPreSharedKeyToClientHello(m_tlsClientContext, m_tlsClient, + m_clientExtensions, offeredCipherSuites); + + // TODO[tls13-psk] Perhaps don't add key_share if external PSK(s) offered and 'psk_dhe_ke' not offered + this.m_clientAgreements = TlsUtilities.AddKeyShareToClientHello(m_tlsClientContext, m_tlsClient, m_clientExtensions); if (TlsUtilities.IsExtendedMasterSecretOptionalTls(supportedVersions) @@ -1684,9 +1727,7 @@ namespace Org.BouncyCastle.Tls - // TODO[tls13-psk] Calculate the total length of the binders that will be added. - int bindersSize = 0; - //int bindersSize = 2 + lengthOfBindersList; + int bindersSize = null == m_clientBinders ? 0 : m_clientBinders.m_bindersSize; this.m_clientHello = new ClientHello(legacy_version, securityParameters.ClientRandom, legacy_session_id, null, offeredCipherSuites, m_clientExtensions, bindersSize); @@ -1702,7 +1743,10 @@ namespace Org.BouncyCastle.Tls message.PrepareClientHello(m_handshakeHash, m_clientHello.BindersSize); - // TODO[tls13-psk] Calculate any PSK binders and write them to 'message' here. + if (null != m_clientBinders) + { + OfferedPsks.EncodeBinders(message, Context.Crypto, m_handshakeHash, m_clientBinders); + } message.SendClientHello(this, m_handshakeHash, m_clientHello.BindersSize); } diff --git a/crypto/src/tls/TlsServerProtocol.cs b/crypto/src/tls/TlsServerProtocol.cs index 2afb625a8..89365afad 100644 --- a/crypto/src/tls/TlsServerProtocol.cs +++ b/crypto/src/tls/TlsServerProtocol.cs @@ -309,6 +309,11 @@ namespace Org.BouncyCastle.Tls this.m_expectSessionTicket = false; + // TODO[tls13-psk] Use PSK early secret if negotiated + TlsSecret pskEarlySecret = null; + + TlsSecret sharedSecret = null; + { int namedGroup = clientShare.NamedGroup; @@ -331,14 +336,11 @@ namespace Org.BouncyCastle.Tls TlsExtensionsUtilities.AddKeyShareServerHello(serverHelloExtensions, serverShare); agreement.ReceivePeerValue(clientShare.KeyExchange); - securityParameters.m_sharedSecret = agreement.CalculateSecret(); - - // TODO[tls13-psk] Use PSK early secret if negotiated - TlsSecret pskEarlySecret = null; - - TlsUtilities.Establish13PhaseSecrets(m_tlsServerContext, pskEarlySecret); + sharedSecret = agreement.CalculateSecret(); } + TlsUtilities.Establish13PhaseSecrets(m_tlsServerContext, pskEarlySecret, sharedSecret); + this.m_serverExtensions = serverEncryptedExtensions; ApplyMaxFragmentLengthExtension(securityParameters.MaxFragmentLength); diff --git a/crypto/src/tls/TlsUtilities.cs b/crypto/src/tls/TlsUtilities.cs index a80e6da32..7f529d8c5 100644 --- a/crypto/src/tls/TlsUtilities.cs +++ b/crypto/src/tls/TlsUtilities.cs @@ -1559,7 +1559,8 @@ namespace Org.BouncyCastle.Tls return Prf(securityParameters, master_secret, asciiLabel, prfHash, verify_data_length).Extract(); } - internal static void Establish13PhaseSecrets(TlsContext context, TlsSecret pskEarlySecret) + internal static void Establish13PhaseSecrets(TlsContext context, TlsSecret pskEarlySecret, + TlsSecret sharedSecret) { TlsCrypto crypto = context.Crypto; SecurityParameters securityParameters = context.SecurityParameters; @@ -1575,7 +1576,6 @@ namespace Org.BouncyCastle.Tls .HkdfExtract(cryptoHashAlgorithm, zeros); } - TlsSecret sharedSecret = securityParameters.SharedSecret; if (null == sharedSecret) { sharedSecret = zeros; @@ -1595,7 +1595,6 @@ namespace Org.BouncyCastle.Tls securityParameters.m_earlySecret = earlySecret; securityParameters.m_handshakeSecret = handshakeSecret; securityParameters.m_masterSecret = masterSecret; - securityParameters.m_sharedSecret = null; } private static void Establish13TrafficSecrets(TlsContext context, byte[] transcriptHash, TlsSecret phaseSecret, @@ -4844,7 +4843,7 @@ namespace Org.BouncyCastle.Tls return false; } - internal static IDictionary AddEarlyKeySharesToClientHello(TlsClientContext clientContext, TlsClient client, + internal static IDictionary AddKeyShareToClientHello(TlsClientContext clientContext, TlsClient client, IDictionary clientExtensions) { /* @@ -5435,17 +5434,40 @@ namespace Org.BouncyCastle.Tls #endif /// - internal static OfferedPsks.Config GetOfferedPsksConfig(TlsClientContext clientContext, TlsClient client) + internal static OfferedPsks.BindersConfig AddPreSharedKeyToClientHello(TlsClientContext clientContext, + TlsClient client, IDictionary clientExtensions, int[] offeredCipherSuites) { - TlsPskExternal[] pskExternals = GetPskExternalsClient(client); + if (!IsTlsV13(clientContext.ClientVersion)) + return null; + + TlsPskExternal[] pskExternals = GetPskExternalsClient(client, offeredCipherSuites); if (null == pskExternals) return null; + short[] pskKeyExchangeModes = client.GetPskKeyExchangeModes(); + if (IsNullOrEmpty(pskKeyExchangeModes)) + throw new TlsFatalAlert(AlertDescription.internal_error, + "External PSKs configured but no PskKeyExchangeMode available"); + + // Add the pre_shared_key extension + { + IList identities = Platform.CreateArrayList(pskExternals.Length); + for (int i = 0; i < pskExternals.Length; ++i) + { + TlsPskExternal pskExternal = pskExternals[i]; + + // TODO[tls13-psk] Handle obfuscated_ticket_age for resumption PSKs + identities.Add(new PskIdentity(pskExternal.Identity, 0L)); + } + + TlsExtensionsUtilities.AddPreSharedKeyClientHello(clientExtensions, new OfferedPsks(identities)); + } + TlsSecret[] pskEarlySecrets = GetPskEarlySecrets(clientContext.Crypto, pskExternals); int bindersSize = OfferedPsks.GetBindersSize(pskExternals); - return new OfferedPsks.Config(pskExternals, pskEarlySecrets, bindersSize); + return new OfferedPsks.BindersConfig(pskExternals, pskKeyExchangeModes, pskEarlySecrets, bindersSize); } internal static TlsSecret GetPskEarlySecret(TlsCrypto crypto, TlsPsk psk) @@ -5469,24 +5491,29 @@ namespace Org.BouncyCastle.Tls } /// - internal static TlsPskExternal[] GetPskExternalsClient(TlsClient client) + internal static TlsPskExternal[] GetPskExternalsClient(TlsClient client, int[] offeredCipherSuites) { - // TODO[tl13-psk] Ensure PSK hash algorithms are supported by cipher suites - IList externalPsks = client.GetExternalPsks(); if (IsNullOrEmpty(externalPsks)) return null; + int[] prfAlgorithms = GetPrfAlgorithms13(offeredCipherSuites); + int count = externalPsks.Count; TlsPskExternal[] result = new TlsPskExternal[count]; for (int i = 0; i < count; ++i) { - TlsPskExternal element = externalPsks[i] as TlsPskExternal; - if (null == element) - throw new TlsFatalAlert(AlertDescription.internal_error); + TlsPskExternal pskExternal = externalPsks[i] as TlsPskExternal; + if (null == pskExternal) + throw new TlsFatalAlert(AlertDescription.internal_error, + "External PSKs element is not a TlsPSKExternal"); + + if (!Arrays.Contains(prfAlgorithms, pskExternal.PrfAlgorithm)) + throw new TlsFatalAlert(AlertDescription.internal_error, + "External PSK incompatible with offered cipher suites"); - result[i] = element; + result[i] = pskExternal; } return result; -- cgit 1.4.1