summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2014-08-26 14:57:25 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2014-08-26 14:57:25 +0700
commitf95405955ac379f2fc29fe1b99e3b59304a29a3c (patch)
tree497d78007b3c208311cd2305468a64bbe90a1e6c
parentAdd methods to give readable text for alerts (diff)
downloadBouncyCastle.NET-ed25519-f95405955ac379f2fc29fe1b99e3b59304a29a3c.tar.xz
Initial implementation of draft-ietf-tls-session-hash-01
-rw-r--r--crypto/src/crypto/tls/ExtensionType.cs7
-rw-r--r--crypto/src/crypto/tls/SecurityParameters.cs17
-rw-r--r--crypto/src/crypto/tls/TlsClientProtocol.cs51
-rw-r--r--crypto/src/crypto/tls/TlsExtensionsUtilities.cs23
-rw-r--r--crypto/src/crypto/tls/TlsServerProtocol.cs21
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs11
-rw-r--r--crypto/test/src/crypto/tls/test/MockTlsClient.cs2
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;