2004-11-10 Sebastien Pouliot <sebastien@ximian.com>
authorSebastien Pouliot <sebastien@ximian.com>
Wed, 10 Nov 2004 17:05:59 +0000 (17:05 -0000)
committerSebastien Pouliot <sebastien@ximian.com>
Wed, 10 Nov 2004 17:05:59 +0000 (17:05 -0000)
* CipherSuiteFactory.cs, Context.cs, RecordProtocol.cs: Merge from
HEAD to add support for ClientHelloV2.

svn path=/branches/mono-1-0/mcs/; revision=35979

mcs/class/Mono.Security/Mono.Security.Protocol.Tls/ChangeLog
mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteFactory.cs
mcs/class/Mono.Security/Mono.Security.Protocol.Tls/Context.cs
mcs/class/Mono.Security/Mono.Security.Protocol.Tls/RecordProtocol.cs

index 971879f8b9a07ecab93fae326411926cbd38cef1..58bad7fea9bf4e0a27865fa2234d64ea7255e047 100644 (file)
@@ -1,3 +1,8 @@
+2004-11-10  Sebastien Pouliot  <sebastien@ximian.com>
+
+       * CipherSuiteFactory.cs, Context.cs, RecordProtocol.cs: Merge from 
+       HEAD to add support for ClientHelloV2.
+
 2004-10-05  Sebastien Pouliot  <sebastien@ximian.com>
 
        * SslClientStream.cs: Changed InputBuffer to internal (was protected).
index ef41e4b2fda7d278ec795c4d2537e60b07e16f3c..f03cd18a959caff543d0679f43c280e416d6c76a 100644 (file)
@@ -1,3 +1,5 @@
+// Transport Security Layer (TLS)
+// Copyright (c) 2003-2004 Carlos Guzman Alvarez
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-/* Transport Security Layer (TLS)
- * Copyright (c) 2003-2004 Carlos Guzman Alvarez
- * 
- * Permission is hereby granted, free of charge, to any person 
- * obtaining a copy of this software and associated documentation 
- * files (the "Software"), to deal in the Software without restriction, 
- * including without limitation the rights to use, copy, modify, merge, 
- * publish, distribute, sublicense, and/or sell copies of the Software, 
- * and to permit persons to whom the Software is furnished to do so, 
- * subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included 
- * in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
 
 using System;
 
@@ -151,8 +130,9 @@ namespace Mono.Security.Protocol.Tls
                {
                        CipherSuiteCollection scs = new CipherSuiteCollection(SecurityProtocolType.Ssl3);
 
-                       // Supported ciphers
-                       scs.Add((0x00 << 0x08) | 0x0A, "SSL_RSA_WITH_3DES_EDE_CBC_SHA", CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaSign, false, true, 24, 24, 168, 8, 8);
+                       // Supported ciphers\r
+                       scs.Add((0x00 << 0x08) | 0x35, "SSL_RSA_WITH_AES_256_CBC_SHA", CipherAlgorithmType.Rijndael, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaSign, false, true, 32, 32, 256, 16, 16);\r
+                       scs.Add((0x00 << 0x08) | 0x0A, "SSL_RSA_WITH_3DES_EDE_CBC_SHA", CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaSign, false, true, 24, 24, 168, 8, 8);\r
                        scs.Add((0x00 << 0x08) | 0x05, "SSL_RSA_WITH_RC4_128_SHA", CipherAlgorithmType.Rc4, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaSign, false, false, 16, 16, 128, 0, 0);
                        scs.Add((0x00 << 0x08) | 0x04, "SSL_RSA_WITH_RC4_128_MD5", CipherAlgorithmType.Rc4, HashAlgorithmType.Md5, ExchangeAlgorithmType.RsaSign, false, false, 16, 16, 128, 0, 0);
                        scs.Add((0x00 << 0x08) | 0x09, "SSL_RSA_WITH_DES_CBC_SHA", CipherAlgorithmType.Des, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaSign, false, true, 8, 8, 56, 8, 8);
index f452db3bc47336d92fb42836754860013934f875..579548d5e3b9c97237370e38cd3faf43c1d7f356 100644 (file)
@@ -1,3 +1,5 @@
+// Transport Security Layer (TLS)
+// Copyright (c) 2003-2004 Carlos Guzman Alvarez
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-/* Transport Security Layer (TLS)
- * Copyright (c) 2003-2004 Carlos Guzman Alvarez
- * 
- * Permission is hereby granted, free of charge, to any person 
- * obtaining a copy of this software and associated documentation 
- * files (the "Software"), to deal in the Software without restriction, 
- * including without limitation the rights to use, copy, modify, merge, 
- * publish, distribute, sublicense, and/or sell copies of the Software, 
- * and to permit persons to whom the Software is furnished to do so, 
- * subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included 
- * in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
 
 using System;
 using System.Text;
@@ -427,6 +406,24 @@ namespace Mono.Security.Protocol.Tls
                        }
                }
 
+               public void ChangeProtocol(short protocol)
+               {
+                       SecurityProtocolType protocolType = this.DecodeProtocolCode(protocol);
+
+                       if ((protocolType & this.SecurityProtocolFlags) == protocolType ||
+                               (this.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default)
+                       {
+                               this.SecurityProtocol = protocolType;
+                               this.SupportedCiphers.Clear();
+                               this.SupportedCiphers = null;
+                               this.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(protocolType);
+                       }
+                       else
+                       {
+                               throw new TlsException(AlertDescription.ProtocolVersion, "Incorrect protocol version received from server");
+                       }
+               }
+
                #endregion
        }
 }
index 564d10c456af9bdbf96b9c9e5543eeac779558db..d5965ab7e313d0d9df2aedd266bf32a8705bd1f9 100644 (file)
@@ -1,3 +1,5 @@
+// Transport Security Layer (TLS)
+// Copyright (c) 2003-2004 Carlos Guzman Alvarez
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-/* Transport Security Layer (TLS)
- * Copyright (c) 2003-2004 Carlos Guzman Alvarez
- * 
- * Permission is hereby granted, free of charge, to any person 
- * obtaining a copy of this software and associated documentation 
- * files (the "Software"), to deal in the Software without restriction, 
- * including without limitation the rights to use, copy, modify, merge, 
- * publish, distribute, sublicense, and/or sell copies of the Software, 
- * and to permit persons to whom the Software is furnished to do so, 
- * subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included 
- * in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
 
 using System;
+using System.Collections;
 using System.IO;
 using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
@@ -100,58 +80,28 @@ namespace Mono.Security.Protocol.Tls
                                        AlertDescription.InternalError,
                                        "The session is finished and it's no longer valid.");
                        }
-                       
+       
                        // Try to read the Record Content Type
                        int type = this.innerStream.ReadByte();
-
-                       // There are no more data for read
                        if (type == -1)
                        {
                                return null;
                        }
 
                        ContentType     contentType     = (ContentType)type;
-                       short           protocol        = this.readShort();
-                       short           length          = this.readShort();
-                       
-                       // Read Record data
-                       int             received        = 0;
-                       byte[]  buffer          = new byte[length];
-                       while (received != length)
-                       {
-                               received += this.innerStream.Read(
-                                       buffer, received, buffer.Length - received);
-                       }
-
-                       DebugHelper.WriteLine(
-                               ">>>> Read record ({0}|{1})", 
-                               this.context.DecodeProtocolCode(protocol),
-                               contentType);
-                       DebugHelper.WriteLine("Record data", buffer);
+                       byte[] buffer = this.ReadRecordBuffer(type);
 
                        TlsStream message = new TlsStream(buffer);
                
-                       // Check that the message has a valid protocol version
-                       if (protocol != this.context.Protocol && 
-                               this.context.ProtocolNegotiated)
-                       {
-                               throw new TlsException(
-                                       AlertDescription.ProtocolVersion,
-                                       "Invalid protocol version on message received from server");
-                       }
-
                        // Decrypt message contents if needed
-                       if (contentType == ContentType.Alert && length == 2)
+                       if (contentType == ContentType.Alert && buffer.Length == 2)
                        {
                        }
                        else
                        {
-                               if (this.context.IsActual &&
-                                       contentType != ContentType.ChangeCipherSpec)
+                               if (this.context.IsActual && contentType != ContentType.ChangeCipherSpec)
                                {
-                                       message = this.decryptRecordFragment(
-                                               contentType, 
-                                               message.ToArray());
+                                       message = this.decryptRecordFragment(contentType, message.ToArray());
 
                                        DebugHelper.WriteLine("Decrypted record data", message.ToArray());
                                }
@@ -166,9 +116,8 @@ namespace Mono.Security.Protocol.Tls
                        switch (contentType)
                        {
                                case ContentType.Alert:
-                                       this.processAlert(
-                                               (AlertLevel)message.ReadByte(),
-                                               (AlertDescription)message.ReadByte());
+                                       this.ProcessAlert((AlertLevel)message.ReadByte(), (AlertDescription)message.ReadByte());
+                                       result = null;
                                        break;
 
                                case ContentType.ChangeCipherSpec:
@@ -188,16 +137,126 @@ namespace Mono.Security.Protocol.Tls
                                        this.context.HandshakeMessages.Write(message.ToArray());
                                        break;
 
+// FIXME / MCS bug - http://bugzilla.ximian.com/show_bug.cgi?id=67711
+//                             case (ContentType)0x80:
+//                                     this.context.HandshakeMessages.Write (result);
+//                                     break;
+
                                default:
-                                       throw new TlsException(
-                                               AlertDescription.UnexpectedMessage,
-                                               "Unknown record received from server.");
+                                       if (contentType != (ContentType)0x80)
+                                       {
+                                               throw new TlsException(
+                                                       AlertDescription.UnexpectedMessage,
+                                                       "Unknown record received from server.");
+                                       }
+                                       this.context.HandshakeMessages.Write (result);
+                                       break;
                        }
 
                        return result;
                }
 
-               private short readShort()
+               private byte[] ReadRecordBuffer(int contentType)
+               {
+                       switch (contentType)
+                       {
+                               case 0x80:
+                                       return this.ReadClientHelloV2();
+
+                               default:
+                                       if (!Enum.IsDefined(typeof(ContentType), (ContentType)contentType))
+                                       {
+                                               throw new TlsException(AlertDescription.DecodeError);
+                                       }
+                                       return this.ReadStandardRecordBuffer();
+                       }
+               }
+
+               private byte[] ReadClientHelloV2()
+               {
+                       int msgLength                   = this.innerStream.ReadByte();
+                       byte[] message = new byte [msgLength];
+                       this.innerStream.Read (message, 0, msgLength);
+
+                       int msgType             = message [0];
+                       if (msgType != 1)
+                       {
+                               throw new TlsException(AlertDescription.DecodeError);
+                       }
+                       int protocol = (message [1] << 8 | message [2]);
+                       int cipherSpecLength = (message [3] << 8 | message [4]);
+                       int sessionIdLength = (message [5] << 8 | message [6]);
+                       int challengeLength = (message [7] << 8 | message [8]);
+                       int length = (challengeLength > 32) ? 32 : challengeLength;
+
+                       // Read CipherSpecs
+                       byte[] cipherSpecV2 = new byte[cipherSpecLength];
+                       Buffer.BlockCopy (message, 9, cipherSpecV2, 0, cipherSpecLength);
+
+                       // Read session ID
+                       byte[] sessionId = new byte[sessionIdLength];
+                       Buffer.BlockCopy (message, 9 + cipherSpecLength, sessionId, 0, sessionIdLength);
+
+                       // Read challenge ID
+                       byte[] challenge = new byte[challengeLength];
+                       Buffer.BlockCopy (message, 9 + cipherSpecLength + sessionIdLength, challenge, 0, challengeLength);
+               
+                       if (challengeLength < 16 || cipherSpecLength == 0 || (cipherSpecLength % 3) != 0)
+                       {
+                               throw new TlsException(AlertDescription.DecodeError);
+                       }
+
+                       // Updated the Session ID
+                       if (sessionId.Length > 0)
+                       {
+                               this.context.SessionId = sessionId;
+                       }
+
+                       // Update the protocol version
+                       this.Context.ChangeProtocol((short)protocol);
+
+                       // Select the Cipher suite
+                       this.ProcessCipherSpecV2Buffer(this.Context.SecurityProtocol, cipherSpecV2);
+
+                       // Updated the Client Random\r
+                       this.context.ClientRandom = new byte [32]; // Always 32\r
+                       // 1. if challenge is bigger than 32 bytes only use the last 32 bytes\r
+                       // 2. right justify (0) challenge in ClientRandom if less than 32\r
+                       Buffer.BlockCopy (challenge, challenge.Length - length, this.context.ClientRandom, 32 - length, length);\r
+\r
+                       // Set 
+                       this.context.LastHandshakeMsg = HandshakeType.ClientHello;
+                       this.context.ProtocolNegotiated = true;
+
+                       return message;
+               }
+
+               private byte[] ReadStandardRecordBuffer()
+               {
+                       short protocol  = this.ReadShort();
+                       short length    = this.ReadShort();
+                       
+                       // Read Record data
+                       int             received        = 0;
+                       byte[]  buffer          = new byte[length];
+                       while (received != length)
+                       {
+                               received += this.innerStream.Read(buffer, received, buffer.Length - received);
+                       }
+
+                       // Check that the message has a valid protocol version
+                       if (protocol != this.context.Protocol && this.context.ProtocolNegotiated)
+                       {
+                               throw new TlsException(
+                                       AlertDescription.ProtocolVersion, "Invalid protocol version on message received");
+                       }
+
+                       DebugHelper.WriteLine("Record data", buffer);
+
+                       return buffer;
+               }
+
+               private short ReadShort()
                {
                        byte[] b = new byte[2];
                        this.innerStream.Read(b, 0, b.Length);
@@ -207,9 +266,7 @@ namespace Mono.Security.Protocol.Tls
                        return System.Net.IPAddress.HostToNetworkOrder(val);
                }
 
-               private void processAlert(
-                       AlertLevel                      alertLevel, 
-                       AlertDescription        alertDesc)
+               private void ProcessAlert(AlertLevel alertLevel, AlertDescription alertDesc)
                {
                        switch (alertLevel)
                        {
@@ -467,5 +524,96 @@ namespace Mono.Security.Protocol.Tls
                }
 
                #endregion
+
+               #region CipherSpecV2 processing
+
+               private void ProcessCipherSpecV2Buffer(SecurityProtocolType protocol, byte[] buffer)
+               {
+                       TlsStream codes = new TlsStream(buffer);
+
+                       string prefix = (protocol == SecurityProtocolType.Ssl3) ? "SSL_" : "TLS_";
+
+                       while (codes.Position < codes.Length)
+                       {
+                               byte check = codes.ReadByte();
+
+                               if (check == 0)
+                               {
+                                       // SSL/TLS cipher spec
+                                       int index = 0;
+                                       short code = codes.ReadInt16();                                 
+                                       if ((index = this.Context.SupportedCiphers.IndexOf(code)) != -1)
+                                       {
+                                               this.Context.Cipher     = this.Context.SupportedCiphers[index];
+                                               break;
+                                       }
+                               }
+                               else
+                               {
+                                       byte[] tmp = new byte[2];
+                                       codes.Read(tmp, 0, tmp.Length);
+
+                                       int tmpCode = ((check & 0xff) << 16) | ((tmp[0] & 0xff) << 8) | (tmp[1] & 0xff);
+                                       CipherSuite cipher = this.MapV2CipherCode(prefix, tmpCode);
+
+                                       if (cipher != null)
+                                       {
+                                               this.Context.Cipher = cipher;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (this.Context.Cipher == null)
+                       {
+                               throw new TlsException(AlertDescription.InsuficientSecurity, "Insuficient Security");
+                       }
+               }
+
+               private CipherSuite MapV2CipherCode(string prefix, int code)
+               {
+                       try
+                       {
+                               switch (code)
+                               {
+                                       case 65664:
+                                               // TLS_RC4_128_WITH_MD5
+                                               return this.Context.SupportedCiphers[prefix + "RSA_WITH_RC4_128_MD5"];
+                                       
+                                       case 131200:
+                                               // TLS_RC4_128_EXPORT40_WITH_MD5
+                                               return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC4_40_MD5"];
+                                       
+                                       case 196736:
+                                               // TLS_RC2_CBC_128_CBC_WITH_MD5
+                                               return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
+                                       
+                                       case 262272:
+                                               // TLS_RC2_CBC_128_CBC_EXPORT40_WITH_MD5
+                                               return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
+                                       
+                                       case 327808:
+                                               // TLS_IDEA_128_CBC_WITH_MD5
+                                               return null;
+                                       
+                                       case 393280:
+                                               // TLS_DES_64_CBC_WITH_MD5
+                                               return null;
+
+                                       case 458944:
+                                               // TLS_DES_192_EDE3_CBC_WITH_MD5
+                                               return null;
+
+                                       default:
+                                               return null;
+                               }
+                       }
+                       catch
+                       {
+                               return null;
+                       }
+               }
+
+               #endregion
        }
 }