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))
{
|