diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-08-26 14:57:25 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-08-26 14:57:25 +0700 |
commit | f95405955ac379f2fc29fe1b99e3b59304a29a3c (patch) | |
tree | 497d78007b3c208311cd2305468a64bbe90a1e6c | |
parent | Add methods to give readable text for alerts (diff) | |
download | BouncyCastle.NET-ed25519-f95405955ac379f2fc29fe1b99e3b59304a29a3c.tar.xz |
Initial implementation of draft-ietf-tls-session-hash-01
-rw-r--r-- | crypto/src/crypto/tls/ExtensionType.cs | 7 | ||||
-rw-r--r-- | crypto/src/crypto/tls/SecurityParameters.cs | 17 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsClientProtocol.cs | 51 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsExtensionsUtilities.cs | 23 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsServerProtocol.cs | 21 | ||||
-rw-r--r-- | crypto/src/crypto/tls/TlsUtilities.cs | 11 | ||||
-rw-r--r-- | crypto/test/src/crypto/tls/test/MockTlsClient.cs | 2 |
7 files changed, 101 insertions, 31 deletions
diff --git a/crypto/src/crypto/tls/ExtensionType.cs b/crypto/src/crypto/tls/ExtensionType.cs index 929c134d5..7b21307d2 100644 --- a/crypto/src/crypto/tls/ExtensionType.cs +++ b/crypto/src/crypto/tls/ExtensionType.cs @@ -54,6 +54,13 @@ namespace Org.BouncyCastle.Crypto.Tls public const int encrypt_then_mac = 22; /* + * draft-ietf-tls-session-hash-01 + * + * WARNING: Placeholder value; the real value is TBA + */ + public static readonly int extended_master_secret = 100; + + /* * RFC 5746 3.2. */ public const int renegotiation_info = 0xff01; diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs index 6a115a911..12bb59f22 100644 --- a/crypto/src/crypto/tls/SecurityParameters.cs +++ b/crypto/src/crypto/tls/SecurityParameters.cs @@ -14,21 +14,13 @@ namespace Org.BouncyCastle.Crypto.Tls internal byte[] masterSecret = null; internal byte[] clientRandom = null; internal byte[] serverRandom = null; + internal byte[] sessionHash = null; // TODO Keep these internal, since it's maybe not the ideal place for them internal short maxFragmentLength = -1; internal bool truncatedHMac = false; internal bool encryptThenMac = false; - - internal void CopySessionParametersFrom(SecurityParameters other) - { - this.entity = other.entity; - this.cipherSuite = other.cipherSuite; - this.compressionAlgorithm = other.compressionAlgorithm; - this.prfAlgorithm = other.prfAlgorithm; - this.verifyDataLength = other.verifyDataLength; - this.masterSecret = Arrays.Clone(other.masterSecret); - } + internal bool extendedMasterSecret = false; internal virtual void Clear() { @@ -90,5 +82,10 @@ namespace Org.BouncyCastle.Crypto.Tls { get { return serverRandom; } } + + public virtual byte[] SessionHash + { + get { return sessionHash; } + } } } diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs index e48c92d30..54a68c4ff 100644 --- a/crypto/src/crypto/tls/TlsClientProtocol.cs +++ b/crypto/src/crypto/tls/TlsClientProtocol.cs @@ -360,11 +360,12 @@ namespace Org.BouncyCastle.Crypto.Tls SendClientKeyExchangeMessage(); this.mConnectionState = CS_CLIENT_KEY_EXCHANGE; + TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish(); + this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, prepareFinishHash, null); + EstablishMasterSecret(Context, mKeyExchange); mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); - TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish(); - if (clientCreds != null && clientCreds is TlsSignerCredentials) { TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds; @@ -386,7 +387,7 @@ namespace Org.BouncyCastle.Crypto.Tls else { signatureAndHashAlgorithm = null; - hash = GetCurrentPrfHash(Context, prepareFinishHash, null); + hash = mSecurityParameters.SessionHash; } byte[] signature = signerCredentials.GenerateCertificateSignature(hash); @@ -562,7 +563,7 @@ namespace Org.BouncyCastle.Crypto.Tls { NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf); - TlsProtocol.AssertEmpty(buf); + AssertEmpty(buf); mTlsClient.NotifyNewSessionTicket(newSessionTicket); } @@ -635,6 +636,15 @@ namespace Org.BouncyCastle.Crypto.Tls this.mServerExtensions = ReadExtensions(buf); /* + * draft-ietf-tls-session-hash-01 5.2. If a server receives the "extended_master_secret" + * extension, it MUST include the "extended_master_secret" extension in its ServerHello + * message. + */ + bool serverSentExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mServerExtensions); + if (serverSentExtendedMasterSecret != mSecurityParameters.extendedMasterSecret) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + + /* * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an * extended client hello message. * @@ -656,6 +666,25 @@ namespace Org.BouncyCastle.Crypto.Tls continue; /* + * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the + * same extension type appeared in the corresponding ClientHello. If a client + * receives an extension type in ServerHello that it did not request in the + * associated ClientHello, it MUST abort the handshake with an unsupported_extension + * fatal alert. + */ + if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType)) + throw new TlsFatalAlert(AlertDescription.unsupported_extension); + + /* + * draft-ietf-tls-session-hash-01 5.2. Implementation note: if the server decides to + * proceed with resumption, the extension does not have any effect. Requiring the + * extension to be included anyway makes the extension negotiation logic easier, + * because it does not depend on whether resumption is accepted or not. + */ + if (extType == ExtensionType.extended_master_secret) + continue; + + /* * 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[.] @@ -667,16 +696,6 @@ namespace Org.BouncyCastle.Crypto.Tls // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats // throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - - /* - * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the - * same extension type appeared in the corresponding ClientHello. If a client - * receives an extension type in ServerHello that it did not request in the - * associated ClientHello, it MUST abort the handshake with an unsupported_extension - * fatal alert. - */ - if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType)) - throw new TlsFatalAlert(AlertDescription.unsupported_extension); } } @@ -718,6 +737,8 @@ namespace Org.BouncyCastle.Crypto.Tls sessionClientExtensions = null; sessionServerExtensions = this.mSessionParameters.ReadServerExtensions(); + + this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); } this.mSecurityParameters.cipherSuite = selectedCipherSuite; @@ -808,6 +829,8 @@ namespace Org.BouncyCastle.Crypto.Tls this.mClientExtensions = this.mTlsClient.GetClientExtensions(); + this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions); + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello); TlsUtilities.WriteVersion(client_version, message); diff --git a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs index 8876911e6..696b86db2 100644 --- a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs +++ b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs @@ -18,6 +18,11 @@ namespace Org.BouncyCastle.Crypto.Tls extensions[ExtensionType.encrypt_then_mac] = CreateEncryptThenMacExtension(); } + public static void AddExtendedMasterSecretExtension(IDictionary extensions) + { + extensions[ExtensionType.extended_master_secret] = CreateExtendedMasterSecretExtension(); + } + /// <exception cref="IOException"></exception> public static void AddHeartbeatExtension(IDictionary extensions, HeartbeatExtension heartbeatExtension) { @@ -83,6 +88,13 @@ namespace Org.BouncyCastle.Crypto.Tls } /// <exception cref="IOException"></exception> + public static bool HasExtendedMasterSecretExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.extended_master_secret); + return extensionData == null ? false : ReadExtendedMasterSecretExtension(extensionData); + } + + /// <exception cref="IOException"></exception> public static bool HasTruncatedHMacExtension(IDictionary extensions) { byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.truncated_hmac); @@ -99,6 +111,11 @@ namespace Org.BouncyCastle.Crypto.Tls return CreateEmptyExtensionData(); } + public static byte[] CreateExtendedMasterSecretExtension() + { + return CreateEmptyExtensionData(); + } + /// <exception cref="IOException"></exception> public static byte[] CreateHeartbeatExtension(HeartbeatExtension heartbeatExtension) { @@ -173,6 +190,12 @@ namespace Org.BouncyCastle.Crypto.Tls } /// <exception cref="IOException"></exception> + public static bool ReadExtendedMasterSecretExtension(byte[] extensionData) + { + return ReadEmptyExtensionData(extensionData); + } + + /// <exception cref="IOException"></exception> public static HeartbeatExtension ReadHeartbeatExtension(byte[] extensionData) { if (extensionData == null) diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs index 589ede802..165d6a147 100644 --- a/crypto/src/crypto/tls/TlsServerProtocol.cs +++ b/crypto/src/crypto/tls/TlsServerProtocol.cs @@ -422,14 +422,14 @@ namespace Org.BouncyCastle.Crypto.Tls // Verify the CertificateVerify message contains a correct signature. try { - byte[] certificateVerifyHash; + byte[] hash; if (TlsUtilities.IsTlsV12(Context)) { - certificateVerifyHash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); + hash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); } else { - certificateVerifyHash = TlsProtocol.GetCurrentPrfHash(Context, mPrepareFinishHash, null); + hash = mSecurityParameters.SessionHash; } X509CertificateStructure x509Cert = mPeerCertificate.GetCertificateAt(0); @@ -439,7 +439,7 @@ namespace Org.BouncyCastle.Crypto.Tls TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)mClientCertificateType); tlsSigner.Init(Context); if (!tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm, - clientCertificateVerify.Signature, publicKey, certificateVerifyHash)) + clientCertificateVerify.Signature, publicKey, hash)) { throw new TlsFatalAlert(AlertDescription.decrypt_error); } @@ -494,6 +494,8 @@ namespace Org.BouncyCastle.Crypto.Tls */ this.mClientExtensions = ReadExtensions(buf); + this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions); + ContextAdmin.SetClientVersion(client_version); mTlsServer.NotifyClientVersion(client_version); @@ -556,11 +558,12 @@ namespace Org.BouncyCastle.Crypto.Tls AssertEmpty(buf); + this.mPrepareFinishHash = mRecordStream.PrepareToFinish(); + this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, mPrepareFinishHash, null); + EstablishMasterSecret(Context, mKeyExchange); mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); - this.mPrepareFinishHash = mRecordStream.PrepareToFinish(); - if (!mExpectSessionTicket) { SendChangeCipherSpecMessage(); @@ -669,6 +672,12 @@ namespace Org.BouncyCastle.Crypto.Tls } } + if (mSecurityParameters.extendedMasterSecret) + { + this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions); + TlsExtensionsUtilities.AddExtendedMasterSecretExtension(mServerExtensions); + } + /* * 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 diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs index f1ea0996d..bbd3e880d 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs @@ -870,7 +870,16 @@ namespace Org.BouncyCastle.Crypto.Tls internal static byte[] CalculateMasterSecret(TlsContext context, byte[] pre_master_secret) { SecurityParameters securityParameters = context.SecurityParameters; - byte[] seed = Concat(securityParameters.ClientRandom, securityParameters.ServerRandom); + + byte[] seed; + if (securityParameters.extendedMasterSecret) + { + seed = securityParameters.SessionHash; + } + else + { + seed = Concat(securityParameters.ClientRandom, securityParameters.ServerRandom); + } if (IsSsl(context)) { diff --git a/crypto/test/src/crypto/tls/test/MockTlsClient.cs b/crypto/test/src/crypto/tls/test/MockTlsClient.cs index 893399c8e..a458e32e6 100644 --- a/crypto/test/src/crypto/tls/test/MockTlsClient.cs +++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs @@ -62,6 +62,8 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests { IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions()); TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions); + // TODO[draft-ietf-tls-session-hash-01] Enable once code-point assigned (only for compatible server though) +// TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions); TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9); TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions); return clientExtensions; |