[SSL/TLS] Add support to select and/or re-order ciphers suites [#16327]
authorSebastien Pouliot <sebastien@xamarin.com>
Wed, 19 Feb 2014 13:29:23 +0000 (08:29 -0500)
committerSebastien Pouliot <sebastien@xamarin.com>
Wed, 19 Feb 2014 13:51:51 +0000 (08:51 -0500)
mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerHello.cs
mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientHello.cs
mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteCollection.cs
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/SecurityProtocolType.cs
mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs
mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslServerStream.cs

index bb662a02d0581399a0bc1073db72ae77cd2fcfca..0cbecb5f2611ca4578e368dffe23d72761fadc8a 100644 (file)
@@ -133,9 +133,7 @@ namespace Mono.Security.Protocol.Tls.Handshake.Client
                                (this.Context.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default)
                        {
                                this.Context.SecurityProtocol = serverProtocol;
-                               this.Context.SupportedCiphers.Clear();
-                               this.Context.SupportedCiphers = null;
-                               this.Context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(serverProtocol);
+                               this.Context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (false, serverProtocol);
 
                                DebugHelper.WriteLine("Selected protocol {0}", serverProtocol);
                        }
index 8a54ba785c4b476f57bd26090903025840ae57c1..1172626f3c1a8fd382037c40ab6e44bc2a96a4d2 100644 (file)
@@ -115,9 +115,7 @@ namespace Mono.Security.Protocol.Tls.Handshake.Server
                                (this.Context.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default)
                        {
                                this.Context.SecurityProtocol = clientProtocol;
-                               this.Context.SupportedCiphers.Clear();
-                               this.Context.SupportedCiphers = null;
-                               this.Context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(clientProtocol);
+                               this.Context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (true, clientProtocol);
                        }
                        else
                        {
index 8240e82a27f42fcf4e62b9ab6e5249f8f95ed55d..973f4b788b8f863742bad0e074b2e390a3300071 100644 (file)
@@ -1,5 +1,6 @@
 // Transport Security Layer (TLS)
 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
+// Copyright 2013-2014 Xamarin Inc.
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 //
 
 using System;
-using System.Collections;
-using System.Globalization;
-using System.Security.Cryptography;
-
-namespace Mono.Security.Protocol.Tls
-{
-       internal sealed class CipherSuiteCollection : ICollection, IList, IEnumerable
-       {
-               #region Fields
-
-               private ArrayList                               cipherSuites;
-               private SecurityProtocolType    protocol;
-
-               #endregion
-
-               #region Indexers
+using System.Collections.Generic;
 
-               public CipherSuite this[string name] 
-               {
-                       get { return (CipherSuite)this.cipherSuites[this.IndexOf(name)]; }
-                       set { this.cipherSuites[this.IndexOf(name)] = (CipherSuite)value; }
-               }
-
-               public CipherSuite this[int index] 
-               {
-                       get { return (CipherSuite)this.cipherSuites[index]; }
-                       set { this.cipherSuites[index] = (CipherSuite)value; }
-               }
-
-               public CipherSuite this[short code] 
-               {
-                       get { return (CipherSuite)this.cipherSuites[this.IndexOf(code)]; }
-                       set { this.cipherSuites[this.IndexOf(code)] = (CipherSuite)value; }
-               }
-
-               object IList.this[int index]
-               {
-                       get { return this[index]; }
-                       set { this[index] = (CipherSuite)value; }
-               }
-
-               #endregion
+namespace Mono.Security.Protocol.Tls {
 
-               #region ICollection Properties
+       internal sealed class CipherSuiteCollection : List<CipherSuite> {
 
-               bool ICollection.IsSynchronized
-               {
-                       get { return this.cipherSuites.IsSynchronized; }
-               }
+               #region Fields
 
-               object ICollection.SyncRoot 
-               {
-                       get { return this.cipherSuites.SyncRoot; }
-               }
+               SecurityProtocolType protocol;
 
-               public int Count 
-               {
-                       get { return this.cipherSuites.Count; }
-               }
-               
                #endregion
 
-               #region IList Properties
+               #region Indexers
 
-               public bool IsFixedSize 
-               {
-                       get { return this.cipherSuites.IsFixedSize; }
+               public CipherSuite this [string name]  {
+                       get {
+                               int n = IndexOf (name);
+                               return n == -1 ? null : this [n];
+                       }
                }
 
-               public bool IsReadOnly
-               {
-                       get { return this.cipherSuites.IsReadOnly; }
+               public CipherSuite this [short code] {
+                       get {
+                               int n = IndexOf (code);
+                               return n == -1 ? null : this [n];
+                       }
                }
 
                #endregion
 
                #region Constructors
 
-               public CipherSuiteCollection(SecurityProtocolType protocol) : base()
-               {
-                       this.protocol           = protocol;
-                       this.cipherSuites       = new ArrayList();
-               }
-
-               #endregion
-
-               #region ICollection Methods
-
-               public void CopyTo(Array array, int index)
-               {
-                       this.cipherSuites.CopyTo(array, index);
-               }
-               
-               #endregion
-
-               #region IEnumerable Methods
-
-               IEnumerator IEnumerable.GetEnumerator()
-               {
-                       return this.cipherSuites.GetEnumerator();
+               public CipherSuiteCollection (SecurityProtocolType protocol)
+               {
+                       switch (protocol) {
+                       case SecurityProtocolType.Default:
+                       case SecurityProtocolType.Tls:
+                       case SecurityProtocolType.Ssl3:
+                               this.protocol = protocol;
+                               break;
+                       case SecurityProtocolType.Ssl2:
+                       default:
+                               throw new NotSupportedException ("Unsupported security protocol type.");
+                       }
                }
 
                #endregion
 
-               #region IList Methods
+               #region Methods
 
-               public void Clear()
-               {
-                       this.cipherSuites.Clear();
-               }
-                       
-               bool IList.Contains(object value)
-               {
-                       return this.cipherSuites.Contains(value as CipherSuite);
-               }
-
-               public int IndexOf(string name)
+               public int IndexOf (string name)
                {
                        int index = 0;
-
-                       foreach (CipherSuite cipherSuite in this.cipherSuites)
-                       {
-                               if (this.cultureAwareCompare(cipherSuite.Name, name))
-                               {
+                       foreach (CipherSuite cipherSuite in this) {
+                               if (String.CompareOrdinal (name, cipherSuite.Name) == 0)
                                        return index;
-                               }
                                index++;
                        }
-
                        return -1;
                }
 
-               public int IndexOf(short code)
+               public int IndexOf (short code)
                {
                        int index = 0;
-
-                       foreach (CipherSuite cipherSuite in this.cipherSuites)
-                       {
+                       foreach (CipherSuite cipherSuite in this) {
                                if (cipherSuite.Code == code)
-                               {
                                        return index;
-                               }
                                index++;
                        }
-
                        return -1;
                }
 
-               int IList.IndexOf(object value)
-               {
-                       return this.cipherSuites.IndexOf(value as CipherSuite);
-               }
-
-               void IList.Insert(int index, object value)
-               {
-                       this.cipherSuites.Insert(index, value as CipherSuite);
-               }
-
-               void IList.Remove(object value)
-               {
-                       this.cipherSuites.Remove(value as CipherSuite);
-               }
-
-               void IList.RemoveAt(int index)
-               {
-                       this.cipherSuites.RemoveAt(index);
-               }
-
-               public CipherSuite Add(
+               public void Add (
                        short code, string name, CipherAlgorithmType cipherType, 
                        HashAlgorithmType hashType, ExchangeAlgorithmType exchangeType,
                        bool exportable, bool blockMode, byte keyMaterialSize, 
                        byte expandedKeyMaterialSize, short effectiveKeyBytes, 
                        byte ivSize, byte blockSize)
                {
-                       switch (this.protocol)
-                       {
-                               case SecurityProtocolType.Default:
-                               case SecurityProtocolType.Tls:
-                                       return this.add(
-                                               new TlsCipherSuite(
-                                               code, name, cipherType, hashType, exchangeType, exportable, 
-                                               blockMode, keyMaterialSize, expandedKeyMaterialSize, 
-                                               effectiveKeyBytes, ivSize, blockSize));
-
-                               case SecurityProtocolType.Ssl3:
-                                       return this.add(
-                                               new SslCipherSuite(
-                                               code, name, cipherType, hashType, exchangeType, exportable, 
-                                               blockMode, keyMaterialSize, expandedKeyMaterialSize, 
-                                               effectiveKeyBytes, ivSize, blockSize));
-
-                               case SecurityProtocolType.Ssl2:
-                               default:
-                                       throw new NotSupportedException("Unsupported security protocol type.");
+                       switch (protocol) {
+                       case SecurityProtocolType.Default:
+                       case SecurityProtocolType.Tls:
+                               Add (new TlsCipherSuite (code, name, cipherType, hashType, exchangeType, exportable, blockMode, 
+                                       keyMaterialSize, expandedKeyMaterialSize, effectiveKeyBytes, ivSize, blockSize));
+                               break;
+
+                       case SecurityProtocolType.Ssl3:
+                               Add (new SslCipherSuite (code, name, cipherType, hashType, exchangeType, exportable, blockMode, 
+                                       keyMaterialSize, expandedKeyMaterialSize, effectiveKeyBytes, ivSize, blockSize));
+                               break;
                        }
                }
 
-               private TlsCipherSuite add(TlsCipherSuite cipherSuite)
-               {
-                       this.cipherSuites.Add(cipherSuite);
-
-                       return cipherSuite;
-               }
-
-               private SslCipherSuite add(SslCipherSuite cipherSuite)
-               {
-                       this.cipherSuites.Add(cipherSuite);
-
-                       return cipherSuite;
-               }
-
-               int IList.Add(object value)
-               {
-                       return this.cipherSuites.Add(value as CipherSuite);
-               }
-               
-               private bool cultureAwareCompare(string strA, string strB)
+               public IList<string> GetNames ()
                {
-                       return CultureInfo.CurrentCulture.CompareInfo.Compare(
-                               strA, 
-                               strB, 
-                               CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | 
-                               CompareOptions.IgnoreCase) == 0 ? true : false;
+                       var list = new List<string> (Count);
+                       foreach (CipherSuite cipherSuite in this)
+                               list.Add (cipherSuite.Name);
+                       return list;
                }
 
                #endregion
index 3fa618a280a39080626aa61000c999dec6a2c5e6..a9d05ed78bbad83df788faf31d4e93ee587ad9c8 100644 (file)
@@ -1,5 +1,6 @@
 // Transport Security Layer (TLS)
 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
+// Copyright 2013-2014 Xamarin Inc.
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 //
 
 using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Net;
 
 namespace Mono.Security.Protocol.Tls
 {
-       internal class CipherSuiteFactory
+       internal static class CipherSuiteFactory
        {
-               public static CipherSuiteCollection GetSupportedCiphers(SecurityProtocolType protocol)
+#if !INSIDE_SYSTEM && !BOOTSTRAP_BASIC
+               static Type spm = typeof (ServicePointManager);
+               static PropertyInfo client_callback;
+               static PropertyInfo server_callback;
+#endif
+
+               public static CipherSuiteCollection GetSupportedCiphers (bool server, SecurityProtocolType protocol)
                {
-                       switch (protocol)
-                       {
-                               case SecurityProtocolType.Default:
-                               case SecurityProtocolType.Tls:                          
-                                       return CipherSuiteFactory.GetTls1SupportedCiphers();
-
-                               case SecurityProtocolType.Ssl3:
-                                       return CipherSuiteFactory.GetSsl3SupportedCiphers();
-
-                               case SecurityProtocolType.Ssl2:
-                               default:
-                                       throw new NotSupportedException("Unsupported security protocol type");
+                       CipherSuiteCollection suites;
+                       switch (protocol) {
+                       case SecurityProtocolType.Default:
+                       case SecurityProtocolType.Tls:                          
+                               suites = CipherSuiteFactory.GetTls1SupportedCiphers ();
+                               break;
+                       case SecurityProtocolType.Ssl3:
+                               suites = CipherSuiteFactory.GetSsl3SupportedCiphers ();
+                               break;
+                       case SecurityProtocolType.Ssl2:
+                       default:
+                               throw new NotSupportedException ("Unsupported security protocol type");
+                       }
+
+                       IEnumerable<string> list = null;
+#if INSIDE_SYSTEM
+                       // if SSL/TLS support is built-in System.dll (e.g. monotouch) then we can access ServicePointManager
+                       // extension directly
+                       var cb = server ? ServicePointManager.ServerCipherSuitesCallback : ClientCipherSuitesCallback;
+                       if (cb == null)
+                               return suites; // e.g. no callback was set
+
+                       list = cb ((System.Net.SecurityProtocolType) (int) protocol, suites.GetNames ());
+#elif !BOOTSTRAP_BASIC
+                       // Mono.Security must work on MS.NET so it cannot depend on any Mono-specific extensions
+                       PropertyInfo pi = null;
+                       if (server) {
+                               if (server_callback == null)
+                                       server_callback = spm.GetProperty ("ServerCipherSuitesCallback", BindingFlags.Static | BindingFlags.Public);
+                               pi = server_callback;
+                       } else {
+                               if (client_callback == null)
+                                       client_callback = spm.GetProperty ("ClientCipherSuitesCallback", BindingFlags.Static | BindingFlags.Public);
+                               pi = client_callback;
+                       }
+                       if (pi == null)
+                               return suites; // e.g. MS runtime - return every supported suites
+
+                       var cb = (Delegate) pi.GetGetMethod ().Invoke (null, null);
+                       if (cb == null)
+                               return suites; // e.g. no callback was set - return every supported suites
+
+                       list = (IEnumerable<string>) cb.DynamicInvoke (new object[] { 
+                               (System.Net.SecurityProtocolType) (int) protocol, suites.GetNames () 
+                       });
+#else
+                       // TODO: right now the callback is only available when using System.Net.* types for SSL/TLS
+                       return suites;
+#endif
+                       CipherSuiteCollection allowed = new CipherSuiteCollection (protocol);
+                       if (list != null) {
+                               foreach (var name in list) {
+                                       // add any supported (ignore unknowns) ciphers requested by the callback
+                                       var cipher = suites [name];
+                                       if (cipher != null)
+                                               allowed.Add (cipher);
+                               }
                        }
+                       return allowed;
                }
 
                #region Private Static Methods
@@ -132,6 +188,7 @@ namespace Mono.Security.Protocol.Tls
 
                        // Supported ciphers
                        scs.Add((0x00 << 0x08) | 0x35, "SSL_RSA_WITH_AES_256_CBC_SHA", CipherAlgorithmType.Rijndael, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaKeyX, false, true, 32, 32, 256, 16, 16);
+                       scs.Add((0x00 << 0x08) | 0x2F, "SSL_RSA_WITH_AES_128_CBC_SHA", CipherAlgorithmType.Rijndael, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaKeyX, false, true, 16, 16, 128, 16, 16);
                        scs.Add((0x00 << 0x08) | 0x0A, "SSL_RSA_WITH_3DES_EDE_CBC_SHA", CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaKeyX, false, true, 24, 24, 168, 8, 8);
                        scs.Add((0x00 << 0x08) | 0x05, "SSL_RSA_WITH_RC4_128_SHA", CipherAlgorithmType.Rc4, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaKeyX, false, false, 16, 16, 128, 0, 0);
                        scs.Add((0x00 << 0x08) | 0x04, "SSL_RSA_WITH_RC4_128_MD5", CipherAlgorithmType.Rc4, HashAlgorithmType.Md5, ExchangeAlgorithmType.RsaKeyX, false, false, 16, 16, 128, 0, 0);
index 340913fa04096e0c7af521e3e4a7aa26eb73046a..b4caf28b5c7bb0e27e61e7214dcb81ab45e57151 100644 (file)
@@ -432,9 +432,7 @@ namespace Mono.Security.Protocol.Tls
                                (this.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default)
                        {
                                this.SecurityProtocol = protocolType;
-                               this.SupportedCiphers.Clear();
-                               this.SupportedCiphers = null;
-                               this.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(protocolType);
+                               this.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers ((this is ServerContext), protocolType);
                        }
                        else
                        {
index d0693e89d7884bdba24ece65de4444983f1404e8..787a3924793bd86d6377ef3c10a97df1208c7a4a 100644 (file)
@@ -1,5 +1,6 @@
 // Transport Security Layer (TLS)
 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
+// Copyright (C) 2014 Xamarin Inc. (http://www.xamarin.com)
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
@@ -38,6 +39,10 @@ namespace Mono.Security.Protocol.Tls
                Default = -1073741824,
                Ssl2    = 12,
                Ssl3    = 48,
-               Tls             = 192
+               Tls             = 192,
+#if NET_4_5
+               Tls11   = 768,
+               Tls12   = 3072,
+#endif
        }
 }
\ No newline at end of file
index e615e83e3f66a531ff09fbb82107745837c8eeba..d0d0721a12d106413f1fd9568245c9ebea49605a 100644 (file)
@@ -400,7 +400,7 @@ namespace Mono.Security.Protocol.Tls
                        }
 
                        // Obtain supported cipher suites
-                       this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (this.context.SecurityProtocol);
+                       this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (false, context.SecurityProtocol);
 
                        // Set handshake state
                        this.context.HandshakeState = HandshakeState.Started;
index b0d8bba6ffc99cc8b92675f31d8318bc32d43ef7..6862c694a611a0bdcff9bcce242b3eb90acfc7e8 100644 (file)
@@ -205,7 +205,7 @@ namespace Mono.Security.Protocol.Tls
                        }
 
                        // Obtain supported cipher suites
-                       this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
+                       this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (true, context.SecurityProtocol);
 
                        // Set handshake state
                        this.context.HandshakeState = HandshakeState.Started;