* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / SslClientStream.cs
index ea95850e6e8d6f1a5e59dc873b80c5e59b9b4690..c4407e05b010c6692982834883e2b7a179304d0f 100644 (file)
-/* Transport Security Layer (TLS)\r
- * Copyright (c) 2003 Carlos Guzmán Álvarez\r
- * \r
- * Permission is hereby granted, free of charge, to any person \r
- * obtaining a copy of this software and associated documentation \r
- * files (the "Software"), to deal in the Software without restriction, \r
- * including without limitation the rights to use, copy, modify, merge, \r
- * publish, distribute, sublicense, and/or sell copies of the Software, \r
- * and to permit persons to whom the Software is furnished to do so, \r
- * subject to the following conditions:\r
- * \r
- * The above copyright notice and this permission notice shall be included \r
- * in all copies or substantial portions of the Software.\r
- * \r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, \r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES \r
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \r
- * DEALINGS IN THE SOFTWARE.\r
- */\r
-\r
-using System;\r
-using System.Collections;\r
-using System.IO;\r
-using System.Net;\r
-using System.Net.Sockets;\r
-using System.Security.Cryptography;\r
-using System.Security.Cryptography.X509Certificates;\r
-\r
-using Mono.Security.Protocol.Tls.Alerts;\r
-using Mono.Security.Protocol.Tls.Handshake;\r
-using Mono.Security.Protocol.Tls.Handshake.Client;\r
-\r
-namespace Mono.Security.Protocol.Tls\r
-{\r
-       public delegate bool CertificateValidationCallback(\r
-       X509Certificate certificate, int[] certificateErrors);\r
-       \r
-       public delegate X509Certificate CertificateSelectionCallback(\r
-       X509CertificateCollection clientCertificates, \r
-       X509Certificate serverCertificate, \r
-       string targetHost, \r
-       X509CertificateCollection serverRequestedCertificates);\r
-\r
-       public class SslClientStream : Stream, IDisposable\r
-       {\r
-               #region EVENTS\r
-\r
-               public event TlsWarningAlertEventHandler WarningAlert;\r
-\r
-               #endregion\r
-\r
-               #region INTERNAL_EVENTS\r
-               \r
-               internal event CertificateValidationCallback ServerCertValidation;\r
-               internal event CertificateSelectionCallback     ClientCertSelection;\r
-               \r
-               #endregion\r
-\r
-               #region FIELDS\r
-\r
-               private CertificateValidationCallback   serverCertValidationDelegate;\r
-               private CertificateSelectionCallback    clientCertSelectionDelegate;\r
-               private Stream                                                  innerStream;\r
-               private BufferedStream                                  inputBuffer;\r
-               private TlsContext                                              context;\r
-               private bool                                                    ownsStream;\r
-               private bool                                                    disposed;\r
-\r
-               #endregion\r
-\r
-               #region PROPERTIES\r
-\r
-               public override bool CanRead\r
-               {\r
-                       get { return this.innerStream.CanRead; }\r
-               }\r
-\r
-               public override bool CanSeek\r
-               {\r
-                       get { return false; }\r
-               }\r
-\r
-               public override bool CanWrite\r
-               {\r
-                       get { return this.innerStream.CanWrite; }\r
-               }\r
-\r
-               public override long Length\r
-               {\r
-                       get { throw new NotSupportedException(); }\r
-               }\r
-\r
-               public override long Position\r
-               {\r
-                       get { throw new NotSupportedException(); }\r
-                       set { throw new NotSupportedException(); }\r
-\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region SECURITY_PROPERTIES\r
-\r
-               public bool CheckCertRevocationStatus \r
-               {\r
-                       get { throw new NotImplementedException(); }\r
-                       set { throw new NotImplementedException(); }\r
-               }\r
-\r
-               public CertificateValidationCallback ServerCertValidationDelegate \r
-               {\r
-                       get { return this.serverCertValidationDelegate; }\r
-                       set \r
-                       { \r
-                               if (this.ServerCertValidation != null)\r
-                               {\r
-                                       this.ServerCertValidation -= this.serverCertValidationDelegate;\r
-                               }\r
-                               this.serverCertValidationDelegate       = value;\r
-                               this.ServerCertValidation                       += this.serverCertValidationDelegate;\r
-                       }\r
-               }\r
-\r
-               public CertificateSelectionCallback ClientCertSelectionDelegate \r
-               {\r
-                       get { return this.clientCertSelectionDelegate; }\r
-                       set \r
-                       { \r
-                               if (this.ClientCertSelection != null)\r
-                               {\r
-                                       this.ClientCertSelection -= this.clientCertSelectionDelegate;\r
-                               }\r
-                               this.clientCertSelectionDelegate        = value;\r
-                               this.ClientCertSelection                        += this.clientCertSelectionDelegate;\r
-                       }\r
-               }\r
-\r
-               public CipherAlgorithmType CipherAlgorithm \r
-               {\r
-                       get { return this.context.Cipher.CipherAlgorithmType;}\r
-               }\r
-               \r
-               public int CipherStrength \r
-               {\r
-                       get { return this.context.Cipher.EffectiveKeyBits;}\r
-               }\r
-               \r
-               public X509CertificateCollection ClientCertificates \r
-               {\r
-                       get { return this.context.ClientSettings.Certificates;}\r
-               }\r
-               \r
-               public HashAlgorithmType HashAlgorithm \r
-               {\r
-                       get { return this.context.Cipher.HashAlgorithmType; }\r
-               }\r
-               \r
-               public int HashStrength\r
-               {\r
-                       get { return this.context.Cipher.HashSize * 8; }\r
-               }\r
-               \r
-               public int KeyExchangeStrength \r
-               {\r
-                       get \r
-                       { \r
-                               return this.context.ServerSettings.Certificates[0].RSA.KeySize;\r
-                       }\r
-               }\r
-               \r
-               public ExchangeAlgorithmType KeyExchangeAlgorithm \r
-               {\r
-                       get { return this.context.Cipher.ExchangeAlgorithmType; }\r
-               }\r
-               \r
-               public SecurityProtocolType SecurityProtocol \r
-               {\r
-                       get { return this.context.Protocol; }\r
-               }\r
-               \r
-               public X509Certificate SelectedClientCertificate \r
-               {\r
-                       get { throw new NotImplementedException(); }\r
-               }\r
-\r
-               public X509Certificate ServerCertificate \r
-               {\r
-                       get { throw new NotImplementedException(); }\r
-               } \r
-\r
-               #endregion\r
-\r
-               #region DESTRUCTOR\r
-\r
-               ~SslClientStream()\r
-               {\r
-                       this.Dispose(false);\r
-               }\r
-\r
-               #endregion\r
-\r
-        #region IDISPOSABLE\r
-\r
-               void IDisposable.Dispose()\r
-               {\r
-                       this.Dispose(true);\r
-                       GC.SuppressFinalize(this);\r
-               }\r
-\r
-               protected virtual void Dispose(bool disposing)\r
-               {\r
-                       if (!disposed)\r
-                       {\r
-                               if (disposing)\r
-                               {\r
-                                       if (this.innerStream != null)\r
-                                       {\r
-                                               // Write close notify\r
-                                               TlsCloseNotifyAlert alert = new TlsCloseNotifyAlert(this.context);\r
-                                               this.SendAlert(alert);\r
-\r
-                                               if (this.ownsStream)\r
-                                               {\r
-                                                       // Close inner stream\r
-                                                       this.innerStream.Close();\r
-                                               }\r
-                                       }\r
-                                       this.ownsStream                                         = false;\r
-                                       this.innerStream                                        = null;\r
-                                       if (this.ClientCertSelection != null)\r
-                                       {\r
-                                               this.ClientCertSelection -= this.clientCertSelectionDelegate;\r
-                                       }\r
-                                       if (this.ServerCertValidation != null)\r
-                                       {\r
-                                               this.ServerCertValidation -= this.serverCertValidationDelegate;\r
-                                       }\r
-                                       this.serverCertValidationDelegate       = null;\r
-                                       this.clientCertSelectionDelegate        = null;\r
-                               }\r
-\r
-                               disposed = true;\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region CONSTRUCTORS\r
-               \r
-               public SslClientStream(Stream stream, string targetHost, bool ownsStream) : \r
-                       this(stream, targetHost, \r
-                       ownsStream, SecurityProtocolType.Default, null)\r
-               {\r
-               }\r
-               \r
-               public SslClientStream(\r
-                       Stream stream, string targetHost, X509Certificate clientCertificate) : \r
-                       this(\r
-                       stream, targetHost, \r
-                       false, SecurityProtocolType.Default, \r
-                       new X509CertificateCollection(new X509Certificate[]{clientCertificate}))\r
-               {\r
-               }\r
-\r
-               public SslClientStream(\r
-                       Stream stream,\r
-                       string targetHost, X509CertificateCollection clientCertificates) : \r
-                       this(stream, targetHost, false, \r
-                       SecurityProtocolType.Default, clientCertificates)\r
-               {\r
-               }\r
-\r
-               public SslClientStream(\r
-                       Stream stream,\r
-                       string targetHost,\r
-                       bool ownsStream,\r
-                       SecurityProtocolType securityProtocolType) : \r
-                       this(stream, targetHost, ownsStream, securityProtocolType,\r
-                       new X509CertificateCollection())\r
-               {\r
-               }\r
-\r
-               public SslClientStream(\r
-                       Stream stream,\r
-                       string targetHost,\r
-                       bool ownsStream,\r
-                       SecurityProtocolType securityProtocolType,\r
-                       X509CertificateCollection clientCertificates)\r
-               {\r
-                       this.context            = new TlsContext(\r
-                               this,\r
-                               securityProtocolType, \r
-                               targetHost, \r
-                               clientCertificates);\r
-                       this.inputBuffer        = new BufferedStream(new MemoryStream());\r
-                       this.innerStream        = stream;\r
-                       this.ownsStream         = ownsStream;\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region METHODS\r
-\r
-               public override IAsyncResult BeginRead(\r
-                       byte[] buffer,\r
-                       int offset,\r
-                       int count,\r
-                       AsyncCallback callback,\r
-                       object state)\r
-               {\r
-                       throw new NotSupportedException();\r
-               }\r
-\r
-               public override IAsyncResult BeginWrite(\r
-                       byte[] buffer,\r
-                       int offset,\r
-                       int count,\r
-                       AsyncCallback callback,\r
-                       object state)\r
-               {\r
-                       throw new NotSupportedException();\r
-               }\r
-\r
-               public override int EndRead(IAsyncResult asyncResult)\r
-               {\r
-                       throw new NotSupportedException();\r
-               }\r
-\r
-               public override void EndWrite(IAsyncResult asyncResult)\r
-               {\r
-                       throw new NotSupportedException();\r
-               }\r
-\r
-               public override void Close()\r
-               {\r
-                       ((IDisposable)this).Dispose();\r
-               }\r
-\r
-               public override void Flush()\r
-               {\r
-                       if (this.disposed)\r
-                       {\r
-                               throw new ObjectDisposedException("The NetworkStream is closed.");\r
-                       }\r
-\r
-                       this.innerStream.Flush();\r
-               }\r
-\r
-               public int Read(byte[] buffer)\r
-               {\r
-                       return this.Read(buffer, 0, buffer.Length);\r
-               }\r
-\r
-               public override int Read(byte[] buffer, int offset, int size)\r
-               {\r
-                       if (!this.context.HandshakeFinished)\r
-                       {\r
-                               // Start handshake negotiation\r
-                               this.doHandshake();\r
-                       }\r
-\r
-                       if (buffer == null)\r
-                       {\r
-                               throw new ArgumentNullException("buffer is a null reference.");\r
-                       }\r
-                       if (offset < 0)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("offset is less than 0.");\r
-                       }\r
-                       if (offset > buffer.Length)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");\r
-                       }\r
-                       if (size < 0)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("size is less than 0.");\r
-                       }\r
-                       if (size > (buffer.Length - offset))\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("size is less than the length of buffer minus the value of the offset parameter.");\r
-                       }\r
-                       if (this.disposed)\r
-                       {\r
-                               throw new ObjectDisposedException("The NetworkStream is closed.");\r
-                       }\r
-       \r
-                       try\r
-                       {\r
-                               // If actual buffer is full readed reset it\r
-                               if (this.inputBuffer.Position == this.inputBuffer.Length &&\r
-                                       this.inputBuffer.Length > 0)\r
-                               {\r
-                                       this.resetBuffer();\r
-                               }\r
-\r
-                               // Check if we have space in the middle buffer\r
-                               // if not Read next TLS record and update the inputBuffer\r
-                               while ((this.inputBuffer.Length - this.inputBuffer.Position) < size)\r
-                               {\r
-                                       // Read next record and write it into the inputBuffer\r
-                                       long    position        = this.inputBuffer.Position;                                    \r
-                                       byte[]  record          = this.receiveRecord();\r
-\r
-                                       if (record.Length > 0)\r
-                                       {\r
-                                               // Write new data to the inputBuffer\r
-                                               this.inputBuffer.Seek(0, SeekOrigin.End);\r
-                                               this.inputBuffer.Write(record, 0, record.Length);\r
-\r
-                                               // Restore buffer position\r
-                                               this.inputBuffer.Seek(position, SeekOrigin.Begin);\r
-                                       }\r
-\r
-                                       #warning "Think on how to solve this"\r
-                                       /*\r
-                                       if (base.Available == 0)\r
-                                       {\r
-                                               break;\r
-                                       }\r
-                                       */\r
-                               }\r
-\r
-                               return this.inputBuffer.Read(buffer, offset, size);\r
-                       }\r
-                       catch (TlsException ex)\r
-                       {\r
-                               throw ex;\r
-                       }\r
-                       catch (Exception ex)\r
-                       {\r
-                               throw new IOException("IO exception during read.", ex);\r
-                       }\r
-               }\r
-\r
-               public override long Seek(long offset, SeekOrigin origin)\r
-               {\r
-                       throw new NotSupportedException();\r
-               }\r
-               \r
-               public override void SetLength(long value)\r
-               {\r
-                       throw new NotSupportedException();\r
-               }\r
-\r
-               public void Write(byte[] buffer)\r
-               {\r
-                       this.Write(buffer, 0, buffer.Length);\r
-               }\r
-\r
-               public override void Write(byte[] buffer, int offset, int size)\r
-               {\r
-                       if (!this.context.HandshakeFinished)\r
-                       {\r
-                               // Start handshake negotiation\r
-                               this.doHandshake();\r
-                       }\r
-\r
-                       if (buffer == null)\r
-                       {\r
-                               throw new ArgumentNullException("buffer is a null reference.");\r
-                       }\r
-                       if (offset < 0)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("offset is less than 0.");\r
-                       }\r
-                       if (offset > buffer.Length)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");\r
-                       }\r
-                       if (size < 0)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("size is less than 0.");\r
-                       }\r
-                       if (size > (buffer.Length - offset))\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("size is less than the length of buffer minus the value of the offset parameter.");\r
-                       }\r
-                       if (disposed)\r
-                       {\r
-                               throw new ObjectDisposedException("The NetworkStream is closed.");\r
-                       }\r
-\r
-                       try\r
-                       {\r
-                               // Send the buffer as a TLS record\r
-                               byte[] recordData = new byte[size];\r
-                               System.Array.Copy(buffer, offset, recordData, 0, size);\r
-\r
-                               this.sendRecord(TlsContentType.ApplicationData, recordData);\r
-                       }\r
-                       catch (TlsException ex)\r
-                       {\r
-                               throw ex;\r
-                       }\r
-                       catch (Exception ex)\r
-                       {\r
-                               throw new IOException("IO exception during Write.", ex);\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region TLS_RECORD_METHODS\r
-\r
-               private byte[] receiveRecord()\r
-               {\r
-                       if (this.context.ConnectionEnd)\r
-                       {\r
-                               throw this.context.CreateException("The session is finished and it's no longer valid.");\r
-                       }\r
-                       \r
-                       TlsContentType                  contentType     = (TlsContentType)innerStream.ReadByte();\r
-                       SecurityProtocolType    protocol        = (SecurityProtocolType)this.ReadShort();\r
-                       short                                   length          = this.ReadShort();\r
-                       \r
-                       // Read Record data\r
-                       int             received        = 0;\r
-                       byte[]  buffer          = new byte[length];\r
-                       while (received != length)\r
-                       {\r
-                               received += this.innerStream.Read(\r
-                                       buffer, received, buffer.Length - received);\r
-                       }\r
-\r
-                       TlsStream message = new TlsStream(buffer);\r
-               \r
-                       // Check that the message has a valid protocol version\r
-                       if (protocol != this.context.Protocol)\r
-                       {\r
-                               throw this.context.CreateException("Invalid protocol version on message received from server");\r
-                       }\r
-\r
-                       // Decrypt message contents if needed\r
-                       if (contentType == TlsContentType.Alert && length == 2)\r
-                       {\r
-                       }\r
-                       else\r
-                       {\r
-                               if (this.context.IsActual &&\r
-                                       contentType != TlsContentType.ChangeCipherSpec)\r
-                               {\r
-                                       message = this.decryptRecordFragment(\r
-                                               contentType, \r
-                                               protocol,\r
-                                               message.ToArray());\r
-                               }\r
-                       }\r
-\r
-                       byte[] result = message.ToArray();\r
-\r
-                       // Process record\r
-                       switch (contentType)\r
-                       {\r
-                               case TlsContentType.Alert:\r
-                                       this.processAlert((TlsAlertLevel)message.ReadByte(),\r
-                                               (TlsAlertDescription)message.ReadByte());\r
-                                       break;\r
-\r
-                               case TlsContentType.ChangeCipherSpec:\r
-                                       // Reset sequence numbers\r
-                                       this.context.ReadSequenceNumber = 0;\r
-                                       break;\r
-\r
-                               case TlsContentType.ApplicationData:\r
-                                       break;\r
-\r
-                               case TlsContentType.Handshake:\r
-                                       while (!message.EOF)\r
-                                       {\r
-                                               this.processHandshakeMessage(message);\r
-                                       }\r
-                                       // Update handshakes of current messages\r
-                                       this.context.HandshakeMessages.Write(message.ToArray());\r
-                                       break;\r
-\r
-                               default:\r
-                                       throw this.context.CreateException("Unknown record received from server.");\r
-                       }\r
-\r
-                       return result;\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region TLS_SEND_METHODS\r
-\r
-               internal void SendAlert(TlsAlert alert)\r
-               {                       \r
-                       // Write record\r
-                       this.sendRecord(TlsContentType.Alert, alert.ToArray());\r
-\r
-                       // Update session\r
-                       alert.UpdateSession();\r
-\r
-                       // Reset message contents\r
-                       alert.Reset();\r
-               }\r
-\r
-               private void sendRecord(TlsHandshakeType type)\r
-               {\r
-                       TlsHandshakeMessage msg = createClientHandshakeMessage(type);\r
-                       \r
-                       // Write record\r
-                       this.sendRecord(msg.ContentType, msg.EncodeMessage());\r
-\r
-                       // Update session\r
-                       msg.UpdateSession();\r
-\r
-                       // Reset message contents\r
-                       msg.Reset();\r
-               }\r
-\r
-               private void sendChangeCipherSpec()\r
-               {\r
-                       // Send Change Cipher Spec message\r
-                       this.sendRecord(TlsContentType.ChangeCipherSpec, new byte[] {1});\r
-\r
-                       // Reset sequence numbers\r
-                       this.context.WriteSequenceNumber = 0;\r
-\r
-                       // Make the pending state to be the current state\r
-                       this.context.IsActual = true;\r
-\r
-                       // Send Finished message\r
-                       this.sendRecord(TlsHandshakeType.Finished);                     \r
-               }\r
-               \r
-               private void sendRecord(TlsContentType contentType, byte[] recordData)\r
-               {\r
-                       if (this.context.ConnectionEnd)\r
-                       {\r
-                               throw this.context.CreateException("The session is finished and it's no longer valid.");\r
-                       }\r
-\r
-                       byte[][] fragments = this.fragmentData(recordData);\r
-                       for (int i = 0; i < fragments.Length; i++)\r
-                       {\r
-                               byte[] fragment = fragments[i];\r
-\r
-                               if (this.context.IsActual)\r
-                               {\r
-                                       // Encrypt fragment\r
-                                       fragment = this.encryptRecordFragment(contentType, fragment);\r
-                               }\r
-\r
-                               // Write tls message\r
-                               TlsStream record = new TlsStream();\r
-                               record.Write((byte)contentType);\r
-                               record.Write((short)this.context.Protocol);\r
-                               record.Write((short)fragment.Length);\r
-                               record.Write(fragment);\r
-\r
-                               // Write record\r
-                               this.innerStream.Write(record.ToArray(), 0, (int)record.Length);\r
-\r
-                               // Reset record data\r
-                               record.Reset();\r
-                       }\r
-               }\r
-\r
-               private byte[][] fragmentData(byte[] messageData)\r
-               {\r
-                       ArrayList d = new ArrayList();\r
-                       \r
-                       int     position = 0;\r
-\r
-                       while (position < messageData.Length)\r
-                       {\r
-                               short   fragmentLength = 0;\r
-                               byte[]  fragmentData;\r
-                               if ((messageData.Length - position) > TlsContext.MAX_FRAGMENT_SIZE)\r
-                               {\r
-                                       fragmentLength = TlsContext.MAX_FRAGMENT_SIZE;\r
-                               }\r
-                               else\r
-                               {\r
-                                       fragmentLength = (short)(messageData.Length - position);\r
-                               }\r
-                               fragmentData = new byte[fragmentLength];\r
-\r
-                               System.Array.Copy(messageData, position, fragmentData, 0, fragmentLength);\r
-\r
-                               d.Add(fragmentData);\r
-\r
-                               position += fragmentLength;\r
-                       }\r
-\r
-                       byte[][] result = new byte[d.Count][];\r
-                       for (int i = 0; i < d.Count; i++)\r
-                       {\r
-                               result[i] = (byte[])d[i];\r
-                       }\r
-\r
-                       return result;\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region TLS_CRYPTO_METHODS\r
-\r
-               private byte[] encryptRecordFragment(TlsContentType contentType, byte[] fragment)\r
-               {\r
-                       // Calculate message MAC\r
-                       byte[] mac      = this.context.Cipher.ComputeClientRecordMAC(contentType, fragment);\r
-\r
-                       // Encrypt the message\r
-                       byte[] ecr = this.context.Cipher.EncryptRecord(fragment, mac);\r
-\r
-                       // Set new IV\r
-                       if (this.context.Cipher.CipherMode == CipherMode.CBC)\r
-                       {\r
-                               byte[] iv = new byte[this.context.Cipher.IvSize];\r
-                               System.Array.Copy(ecr, ecr.Length - iv.Length, iv, 0, iv.Length);\r
-                               this.context.Cipher.UpdateClientCipherIV(iv);\r
-                       }\r
-\r
-                       // Update sequence number\r
-                       this.context.WriteSequenceNumber++;\r
-\r
-                       return ecr;\r
-               }\r
-\r
-               private TlsStream decryptRecordFragment(TlsContentType contentType, \r
-                       SecurityProtocolType protocol,\r
-                       byte[] fragment)\r
-               {\r
-                       byte[]  dcrFragment     = null;\r
-                       byte[]  dcrMAC          = null;\r
-\r
-                       // Decrypt message\r
-                       this.context.Cipher.DecryptRecord(fragment, ref dcrFragment, ref dcrMAC);\r
-\r
-                       // Set new IV\r
-                       if (this.context.Cipher.CipherMode == CipherMode.CBC)\r
-                       {\r
-                               byte[] iv = new byte[this.context.Cipher.IvSize];\r
-                               System.Array.Copy(fragment, fragment.Length - iv.Length, iv, 0, iv.Length);\r
-                               this.context.Cipher.UpdateServerCipherIV(iv);\r
-                       }\r
-                       \r
-                       // Check MAC code\r
-                       byte[] mac = this.context.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);\r
-\r
-                       // Check that the mac is correct\r
-                       if (mac.Length != dcrMAC.Length)\r
-                       {\r
-                               throw new TlsException("Invalid MAC received from server.");\r
-                       }\r
-                       for (int i = 0; i < mac.Length; i++)\r
-                       {\r
-                               if (mac[i] != dcrMAC[i])\r
-                               {\r
-                                       throw new TlsException("Invalid MAC received from server.");\r
-                               }\r
-                       }\r
-\r
-                       // Update sequence number\r
-                       this.context.ReadSequenceNumber++;\r
-\r
-                       return new TlsStream(dcrFragment);\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region MESSAGE_PROCESSING\r
-\r
-               private void processHandshakeMessage(TlsStream handMsg)\r
-               {\r
-                       TlsHandshakeType        handshakeType   = (TlsHandshakeType)handMsg.ReadByte();\r
-                       TlsHandshakeMessage     message                 = null;\r
-\r
-                       // Read message length\r
-                       int length = handMsg.ReadInt24();\r
-\r
-                       // Read message data\r
-                       byte[] data = new byte[length];\r
-                       handMsg.Read(data, 0, length);\r
-\r
-                       // Create and process the server message\r
-                       message = this.createServerHandshakeMessage(handshakeType, data);\r
-\r
-                       // Update session\r
-                       if (message != null)\r
-                       {\r
-                               message.UpdateSession();\r
-                       }\r
-               }\r
-\r
-               private void processAlert(TlsAlertLevel alertLevel, TlsAlertDescription alertDesc)\r
-               {\r
-                       switch (alertLevel)\r
-                       {\r
-                               case TlsAlertLevel.Fatal:\r
-                                       throw this.context.CreateException(alertLevel, alertDesc);                                      \r
-\r
-                               case TlsAlertLevel.Warning:\r
-                               default:\r
-                               switch (alertDesc)\r
-                               {\r
-                                       case TlsAlertDescription.CloseNotify:\r
-                                               this.context.ConnectionEnd = true;\r
-                                               break;\r
-\r
-                                       default:\r
-                                               this.RaiseWarningAlert(alertLevel, alertDesc);\r
-                                               break;\r
-                               }\r
-                                       break;\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region MISC_METHODS\r
-\r
-               private void resetBuffer()\r
-               {\r
-                       this.inputBuffer.SetLength(0);\r
-                       this.inputBuffer.Position = 0;\r
-               }\r
-\r
-               private short ReadShort()\r
-               {\r
-                       byte[] b = new byte[2];\r
-                       this.innerStream.Read(b, 0, b.Length);\r
-\r
-                       short val = BitConverter.ToInt16(b, 0);\r
-\r
-                       return System.Net.IPAddress.HostToNetworkOrder(val);\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region HANDSHAKE_METHODS\r
-\r
-               /*\r
-                       Client                                                                                  Server\r
-\r
-                       ClientHello                 -------->\r
-                                                                                                                       ServerHello\r
-                                                                                                                       Certificate*\r
-                                                                                                                       ServerKeyExchange*\r
-                                                                                                                       CertificateRequest*\r
-                                                                               <--------                       ServerHelloDone\r
-                       Certificate*\r
-                       ClientKeyExchange\r
-                       CertificateVerify*\r
-                       [ChangeCipherSpec]\r
-                       Finished                    -------->\r
-                                                                                                                       [ChangeCipherSpec]\r
-                                                                               <--------           Finished\r
-                       Application Data            <------->                   Application Data\r
-\r
-                                       Fig. 1 - Message flow for a full handshake              \r
-               */\r
-\r
-               private void doHandshake()\r
-               {\r
-                       // Obtain supported cipher suite collection\r
-                       this.context.SupportedCiphers = TlsCipherSuiteFactory.GetSupportedCiphers(context.Protocol);\r
-\r
-                       // Send client hello\r
-                       this.sendRecord(TlsHandshakeType.ClientHello);\r
-\r
-                       // Read server response\r
-                       while (!this.context.HelloDone)\r
-                       {\r
-                               // Read next record\r
-                               this.receiveRecord();\r
-                       }\r
-                       \r
-                       // Send client certificate if requested\r
-                       if (this.context.ServerSettings.CertificateRequest)\r
-                       {\r
-                               this.sendRecord(TlsHandshakeType.Certificate);\r
-                       }\r
-\r
-                       // Send Client Key Exchange\r
-                       this.sendRecord(TlsHandshakeType.ClientKeyExchange);\r
-\r
-                       // Now initialize session cipher with the generated keys\r
-                       this.context.Cipher.InitializeCipher();\r
-\r
-                       // Send certificate verify if requested\r
-                       if (this.context.ServerSettings.CertificateRequest)\r
-                       {\r
-                               this.sendRecord(TlsHandshakeType.CertificateVerify);\r
-                       }\r
-\r
-                       // Send Cipher Spec protocol\r
-                       this.sendChangeCipherSpec();                    \r
-                       \r
-                       // Read record until server finished is received\r
-                       while (!this.context.HandshakeFinished)\r
-                       {\r
-                               // If all goes well this will process messages:\r
-                               //              Change Cipher Spec\r
-                               //              Server finished\r
-                               this.receiveRecord();\r
-                       }\r
-\r
-                       // Clear Key Info\r
-                       this.context.ClearKeyInfo();\r
-               }\r
-               \r
-               private TlsHandshakeMessage createClientHandshakeMessage(TlsHandshakeType type)\r
-               {\r
-                       switch (type)\r
-                       {\r
-                               case TlsHandshakeType.ClientHello:\r
-                                       return new TlsClientHello(this.context);\r
-\r
-                               case TlsHandshakeType.Certificate:\r
-                                       return new TlsClientCertificate(this.context);\r
-\r
-                               case TlsHandshakeType.ClientKeyExchange:\r
-                                       return new TlsClientKeyExchange(this.context);\r
-\r
-                               case TlsHandshakeType.CertificateVerify:\r
-                                       return new TlsClientCertificateVerify(this.context);\r
-\r
-                               case TlsHandshakeType.Finished:\r
-                                       return new TlsClientFinished(this.context);\r
-\r
-                               default:\r
-                                       throw new InvalidOperationException("Unknown client handshake message type: " + type.ToString() );\r
-                       }\r
-               }\r
-\r
-               private TlsHandshakeMessage createServerHandshakeMessage(TlsHandshakeType type, byte[] buffer)\r
-               {\r
-                       switch (type)\r
-                       {\r
-                               case TlsHandshakeType.HelloRequest:\r
-                                       this.sendRecord(TlsHandshakeType.ClientHello);\r
-                                       return null;\r
-\r
-                               case TlsHandshakeType.ServerHello:\r
-                                       return new TlsServerHello(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.Certificate:\r
-                                       return new TlsServerCertificate(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.ServerKeyExchange:\r
-                                       return new TlsServerKeyExchange(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.CertificateRequest:\r
-                                       return new TlsServerCertificateRequest(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.ServerHelloDone:\r
-                                       return new TlsServerHelloDone(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.Finished:\r
-                                       return new TlsServerFinished(this.context, buffer);\r
-\r
-                               default:\r
-                                       throw this.context.CreateException("Unknown server handshake message received ({0})", type.ToString());\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region EVENT_METHODS\r
-\r
-               internal void RaiseWarningAlert(TlsAlertLevel level, TlsAlertDescription description)\r
-               {\r
-                       if (WarningAlert != null)\r
-                       {\r
-                               WarningAlert(this, new TlsWarningAlertEventArgs(level, description));\r
-                       }\r
-               }\r
-\r
-               internal bool RaiseServerCertificateValidation(\r
-                       X509Certificate certificate, int[] certificateErrors)\r
-               {\r
-                       if (this.ServerCertValidation != null)\r
-                       {\r
-                               return this.ServerCertValidation(certificate, certificateErrors);\r
-                       }\r
-\r
-                       return false;\r
-               }\r
-\r
-               internal bool RaiseClientCertificateSelection(\r
-                       X509CertificateCollection clientCertificates, \r
-                       X509Certificate serverCertificate, \r
-                       string targetHost, \r
-                       X509CertificateCollection serverRequestedCertificates)\r
-               {\r
-                       #warning "Add implementation"\r
-\r
-                       return true;\r
-               }\r
-\r
-\r
-               #endregion\r
-       }\r
-}\r
+// 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.Net;
+using System.Net.Sockets;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+
+using Mono.Security.Protocol.Tls.Handshake;
+
+namespace Mono.Security.Protocol.Tls
+{
+       #region Delegates
+
+       public delegate bool CertificateValidationCallback(
+               X509Certificate certificate, 
+               int[]                   certificateErrors);
+
+       public delegate X509Certificate CertificateSelectionCallback(
+               X509CertificateCollection       clientCertificates, 
+               X509Certificate                         serverCertificate, 
+               string                                          targetHost, 
+               X509CertificateCollection       serverRequestedCertificates);
+
+       public delegate AsymmetricAlgorithm PrivateKeySelectionCallback(
+               X509Certificate certificate, 
+               string                  targetHost);
+
+       #endregion
+
+       public class SslClientStream : SslStreamBase
+       {
+               #region Internal Events
+               
+               internal event CertificateValidationCallback    ServerCertValidation;
+               internal event CertificateSelectionCallback             ClientCertSelection;
+               internal event PrivateKeySelectionCallback              PrivateKeySelection;
+               
+               #endregion
+
+               #region Properties
+
+               // required by HttpsClientStream for proxy support
+               internal Stream InputBuffer 
+               {
+                       get { return base.inputBuffer; }
+               }
+
+               public X509CertificateCollection ClientCertificates
+               {
+                       get { return this.context.ClientSettings.Certificates; }
+               }
+
+               public X509Certificate SelectedClientCertificate
+               {
+                       get { return this.context.ClientSettings.ClientCertificate; }
+               }
+
+               #endregion
+
+               #region Callback Properties
+
+               public CertificateValidationCallback ServerCertValidationDelegate
+               {
+                       get { return this.ServerCertValidation; }
+                       set { this.ServerCertValidation = value; }                      
+               }
+
+               public CertificateSelectionCallback ClientCertSelectionDelegate 
+               {
+                       get { return this.ClientCertSelection; }
+                       set { this.ClientCertSelection = value; }
+               }
+
+               public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
+               {
+                       get { return this.PrivateKeySelection; }
+                       set { this.PrivateKeySelection = value; }
+               }
+               
+               #endregion
+
+               #region Constructors
+               
+               public SslClientStream(
+                       Stream  stream, 
+                       string  targetHost, 
+                       bool    ownsStream) 
+                       : this(
+                               stream, targetHost, ownsStream, 
+                               SecurityProtocolType.Default, null)
+               {
+               }
+               
+               public SslClientStream(
+                       Stream                          stream, 
+                       string                          targetHost, 
+                       X509Certificate         clientCertificate) 
+                       : this(
+                               stream, targetHost, false, SecurityProtocolType.Default, 
+                               new X509CertificateCollection(new X509Certificate[]{clientCertificate}))
+               {
+               }
+
+               public SslClientStream(
+                       Stream                                          stream,
+                       string                                          targetHost, 
+                       X509CertificateCollection clientCertificates) : 
+                       this(
+                               stream, targetHost, false, SecurityProtocolType.Default, 
+                               clientCertificates)
+               {
+               }
+
+               public SslClientStream(
+                       Stream                                  stream,
+                       string                                  targetHost,
+                       bool                                    ownsStream,
+                       SecurityProtocolType    securityProtocolType) 
+                       : this(
+                               stream, targetHost, ownsStream, securityProtocolType,
+                               new X509CertificateCollection())
+               {
+               }
+
+               public SslClientStream(
+                       Stream                                          stream,
+                       string                                          targetHost,
+                       bool                                            ownsStream,
+                       SecurityProtocolType            securityProtocolType,
+                       X509CertificateCollection       clientCertificates):
+                       base(stream, ownsStream)
+               {
+                       if (targetHost == null || targetHost.Length == 0)
+                       {
+                               throw new ArgumentNullException("targetHost is null or an empty string.");
+                       }
+
+                       this.context = new ClientContext(
+                               this,
+                               securityProtocolType, 
+                               targetHost, 
+                               clientCertificates);
+
+                       this.protocol = new ClientRecordProtocol(innerStream, (ClientContext)this.context);
+               }
+
+               #endregion
+
+               #region Finalizer
+
+               ~SslClientStream()
+               {
+                       base.Dispose(false);
+               }
+
+               #endregion
+
+               #region IDisposable Methods
+
+               protected override void Dispose(bool disposing)
+               {
+                       base.Dispose(disposing);
+
+                       if (disposing)
+                       {
+                               this.ServerCertValidation = null;
+                               this.ClientCertSelection = null;
+                               this.PrivateKeySelection = null;
+                       }
+               }
+
+               #endregion
+
+               #region Handshake Methods
+
+               /*
+                       Client                                                                                  Server
+
+                       ClientHello                 -------->
+                                                                                                                       ServerHello
+                                                                                                                       Certificate*
+                                                                                                                       ServerKeyExchange*
+                                                                                                                       CertificateRequest*
+                                                                               <--------                       ServerHelloDone
+                       Certificate*
+                       ClientKeyExchange
+                       CertificateVerify*
+                       [ChangeCipherSpec]
+                       Finished                    -------->
+                                                                                                                       [ChangeCipherSpec]
+                                                                               <--------           Finished
+                       Application Data            <------->                   Application Data
+
+                                       Fig. 1 - Message flow for a full handshake              
+               */
+
+               internal override IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state)
+               {
+                       try
+                       {
+                               if (this.context.HandshakeState != HandshakeState.None)
+                               {
+                                       this.context.Clear();
+                               }
+
+                               // Obtain supported cipher suites
+                               this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
+
+                               // Set handshake state
+                               this.context.HandshakeState = HandshakeState.Started;
+
+                               // Send client hello
+                               return this.protocol.BeginSendRecord(HandshakeType.ClientHello, callback, state);
+                       }
+                       catch (TlsException ex)
+                       {
+                               this.protocol.SendAlert(ex.Alert);
+
+                               throw new IOException("The authentication or decryption has failed.", ex);
+                       }
+                       catch (Exception ex)
+                       {
+                               this.protocol.SendAlert(AlertDescription.InternalError);
+
+                               throw new IOException("The authentication or decryption has failed.", ex);
+                       }
+               }
+
+               private void SafeReceiveRecord (Stream s)
+               {
+                       byte[] record = this.protocol.ReceiveRecord (s);
+                       if ((record == null) || (record.Length == 0)) {
+                               throw new TlsException (
+                                       AlertDescription.HandshakeFailiure,
+                                       "The server stopped the handshake.");
+                       }
+               }
+
+               internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult)
+               {
+                       this.protocol.EndSendRecord(asyncResult);
+
+                       // Read server response
+                       while (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone) 
+                       {
+                               // Read next record
+                               SafeReceiveRecord (this.innerStream);
+
+                               // special case for abbreviated handshake where no ServerHelloDone is sent from the server
+                               if (this.context.AbbreviatedHandshake && (this.context.LastHandshakeMsg == HandshakeType.ServerHello))
+                                       break;
+                       }
+
+                       // the handshake is much easier if we can reuse a preivous session settings
+                       if (this.context.AbbreviatedHandshake) 
+                       {
+                               ClientSessionCache.SetContextFromCache (this.context);
+                               this.context.Cipher.ComputeKeys ();
+                               this.context.Cipher.InitializeCipher ();
+
+                               // Send Cipher Spec protocol
+                               this.protocol.SendChangeCipherSpec ();
+
+                               // Read record until server finished is received
+                               while (this.context.HandshakeState != HandshakeState.Finished) 
+                               {
+                                       // If all goes well this will process messages:
+                                       //              Change Cipher Spec
+                                       //              Server finished
+                                       SafeReceiveRecord (this.innerStream);
+                               }
+
+                               // Send Finished message
+                               this.protocol.SendRecord (HandshakeType.Finished);
+                       }
+                       else
+                       {
+                               // Send client certificate if requested
+                               // even if the server ask for it it _may_ still be optional
+                               bool clientCertificate = this.context.ServerSettings.CertificateRequest;
+
+                               // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
+                               // the current design doesn't allow a very cute way to handle 
+                               // SSL3 alert warning for NoCertificate (41).
+                               if (this.context.SecurityProtocol == SecurityProtocolType.Ssl3)
+                               {
+                                       clientCertificate = ((this.context.ClientSettings.Certificates != null) &&
+                                               (this.context.ClientSettings.Certificates.Count > 0));
+                                       // this works well with OpenSSL (but only for SSL3)
+                               }
+
+                               if (clientCertificate)
+                               {
+                                       this.protocol.SendRecord(HandshakeType.Certificate);
+                               }
+
+                               // Send Client Key Exchange
+                               this.protocol.SendRecord(HandshakeType.ClientKeyExchange);
+
+                               // Now initialize session cipher with the generated keys
+                               this.context.Cipher.InitializeCipher();
+
+                               // Send certificate verify if requested (optional)
+                               if (clientCertificate && (this.context.ClientSettings.ClientCertificate != null))
+                               {
+                                       this.protocol.SendRecord(HandshakeType.CertificateVerify);
+                               }
+
+                               // Send Cipher Spec protocol
+                               this.protocol.SendChangeCipherSpec ();
+                               // Send Finished message
+                               this.protocol.SendRecord (HandshakeType.Finished);                      
+
+                               // Read record until server finished is received
+                               while (this.context.HandshakeState != HandshakeState.Finished) {
+                                       // If all goes well this will process messages:
+                                       //              Change Cipher Spec
+                                       //              Server finished
+                                       SafeReceiveRecord (this.innerStream);
+                               }
+                       }
+
+                       // Reset Handshake messages information
+                       this.context.HandshakeMessages.Reset ();
+
+                       // Clear Key Info
+                       this.context.ClearKeyInfo();
+
+               }
+
+               #endregion
+
+               #region Event Methods
+
+               internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
+               {
+                       if (this.ClientCertSelection != null)
+                       {
+                               return this.ClientCertSelection(
+                                       clientCertificates,
+                                       serverCertificate,
+                                       targetHost,
+                                       serverRequestedCertificates);
+                       }
+
+                       return null;
+               }
+               
+               internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
+               {
+                       if (this.ServerCertValidation != null)
+                       {
+                               return this.ServerCertValidation(certificate, errors);
+                       }
+
+                       return (errors != null && errors.Length == 0);
+               }
+
+               internal virtual bool RaiseServerCertificateValidation(
+                       X509Certificate certificate, 
+                       int[]                   certificateErrors)
+               {
+                       return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
+               }
+
+               internal X509Certificate RaiseClientCertificateSelection(
+                       X509CertificateCollection       clientCertificates, 
+                       X509Certificate                         serverCertificate, 
+                       string                                          targetHost, 
+                       X509CertificateCollection       serverRequestedCertificates)
+               {
+                       return base.RaiseLocalCertificateSelection(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
+               }
+
+               internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
+               {
+                       if (this.PrivateKeySelection != null)
+                       {
+                               return this.PrivateKeySelection(certificate, targetHost);
+                       }
+
+                       return null;
+               }
+
+               internal AsymmetricAlgorithm RaisePrivateKeySelection(
+                       X509Certificate certificate,
+                       string targetHost)
+               {
+                       return base.RaiseLocalPrivateKeySelection(certificate, targetHost);
+               }
+
+               #endregion
+       }
+}