Warnings
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / RecordProtocol.cs
index 062b92eb3c905cdefaeeba3aeff24fb175f897b9..1744d00196f2071586bf278f89dbff458be52def 100644 (file)
-/* Transport Security Layer (TLS)\r
- * Copyright (c) 2003-2004 Carlos Guzman Alvarez\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.IO;\r
-using System.Security.Cryptography;\r
-using System.Security.Cryptography.X509Certificates;\r
-\r
-using Mono.Security.Protocol.Tls.Handshake;\r
-\r
-namespace Mono.Security.Protocol.Tls\r
-{\r
-       internal abstract class RecordProtocol\r
-       {\r
-               #region Fields\r
-\r
-               protected Stream        innerStream;\r
-               protected Context       context;\r
-\r
-               #endregion\r
-\r
-               #region Properties\r
-\r
-               public Stream InnerStream\r
-               {\r
-                       get { return this.innerStream; }\r
-                       set { this.innerStream = value; }\r
-               }\r
-\r
-               public Context Context\r
-               {\r
-                       get { return this.context; }\r
-                       set { this.context = value; }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Constructors\r
-\r
-               public RecordProtocol(Stream innerStream, Context context)\r
-               {\r
-                       this.innerStream        = innerStream;\r
-                       this.context            = context;\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Abstract Methods\r
-\r
-               public abstract void SendRecord(HandshakeType type);\r
-               protected abstract void ProcessHandshakeMessage(TlsStream handMsg);\r
-                               \r
-               #endregion\r
-\r
-               #region Reveive Record Methods\r
-\r
-               public 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
-                       // Try to read the Record Content Type\r
-                       int type = this.innerStream.ReadByte();\r
-\r
-                       // There are no more data for read\r
-                       if (type == -1)\r
-                       {\r
-                               return null;\r
-                       }\r
-\r
-                       ContentType     contentType     = (ContentType)type;\r
-                       short           protocol        = 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
-                               this.context.ProtocolNegotiated)\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 == ContentType.Alert && length == 2)\r
-                       {\r
-                       }\r
-                       else\r
-                       {\r
-                               if (this.context.IsActual &&\r
-                                       contentType != ContentType.ChangeCipherSpec)\r
-                               {\r
-                                       message = this.decryptRecordFragment(\r
-                                               contentType, \r
-                                               message.ToArray());\r
-                               }\r
-                       }\r
-\r
-                       // Set last handshake message received to None\r
-                       this.context.LastHandshakeMsg = HandshakeType.None;\r
-                       \r
-                       // Process record\r
-                       byte[] result = message.ToArray();\r
-\r
-                       switch (contentType)\r
-                       {\r
-                               case ContentType.Alert:\r
-                                       this.processAlert(\r
-                                               (AlertLevel)message.ReadByte(),\r
-                                               (AlertDescription)message.ReadByte());\r
-                                       break;\r
-\r
-                               case ContentType.ChangeCipherSpec:\r
-                                       // Reset sequence numbers\r
-                                       this.context.ReadSequenceNumber = 0;\r
-                                       break;\r
-\r
-                               case ContentType.ApplicationData:\r
-                                       break;\r
-\r
-                               case ContentType.Handshake:\r
-                                       while (!message.EOF)\r
-                                       {\r
-                                               this.ProcessHandshakeMessage(message);\r
-                                       }\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
-               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
-               private void processAlert(\r
-                       AlertLevel                      alertLevel, \r
-                       AlertDescription        alertDesc)\r
-               {\r
-                       switch (alertLevel)\r
-                       {\r
-                               case AlertLevel.Fatal:\r
-                                       throw this.context.CreateException(alertLevel, alertDesc);                                      \r
-\r
-                               case AlertLevel.Warning:\r
-                               default:\r
-                               switch (alertDesc)\r
-                               {\r
-                                       case AlertDescription.CloseNotify:\r
-                                               this.context.ConnectionEnd = true;\r
-                                               break;\r
-                               }\r
-                               break;\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Send Alert Methods\r
-\r
-               public void SendAlert(AlertDescription description)\r
-               {\r
-                       this.SendAlert(new Alert(this.Context, description));\r
-               }\r
-\r
-               public void SendAlert(\r
-                       AlertLevel                      level, \r
-                       AlertDescription        description)\r
-               {\r
-                       this.SendAlert(new Alert(this.Context, level, description));\r
-               }\r
-\r
-               public void SendAlert(Alert alert)\r
-               {                       \r
-                       // Write record\r
-                       this.SendRecord(ContentType.Alert, alert.ToArray());\r
-\r
-                       // Update session\r
-                       alert.Update();\r
-\r
-                       // Reset message contents\r
-                       alert.Reset();\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Send Record Methods\r
-\r
-               public void SendChangeCipherSpec()\r
-               {\r
-                       // Send Change Cipher Spec message\r
-                       this.SendRecord(ContentType.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(HandshakeType.Finished);                        \r
-               }\r
-\r
-               public void SendRecord(ContentType 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[] record = this.EncodeRecord(contentType, recordData);\r
-\r
-                       this.innerStream.Write(record, 0, record.Length);\r
-               }\r
-\r
-               public byte[] EncodeRecord(ContentType contentType, byte[] recordData)\r
-               {\r
-                       return this.EncodeRecord(\r
-                               contentType,\r
-                               recordData,\r
-                               0,\r
-                               recordData.Length);\r
-               }\r
-\r
-               public byte[] EncodeRecord(\r
-                       ContentType     contentType, \r
-                       byte[]          recordData,\r
-                       int                     offset,\r
-                       int                     count)\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
-                       TlsStream record = new TlsStream();\r
-\r
-                       int     position = offset;\r
-\r
-                       while (position < ( offset + count ))\r
-                       {\r
-                               short   fragmentLength = 0;\r
-                               byte[]  fragment;\r
-\r
-                               if ((count - position) > Context.MAX_FRAGMENT_SIZE)\r
-                               {\r
-                                       fragmentLength = Context.MAX_FRAGMENT_SIZE;\r
-                               }\r
-                               else\r
-                               {\r
-                                       fragmentLength = (short)(count - position);\r
-                               }\r
-\r
-                               // Fill the fragment data\r
-                               fragment = new byte[fragmentLength];\r
-                               Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);\r
-\r
-                               if (this.context.IsActual)\r
-                               {\r
-                                       // Encrypt fragment\r
-                                       fragment = this.encryptRecordFragment(contentType, fragment);\r
-                               }\r
-\r
-                               // Write tls message\r
-                               record.Write((byte)contentType);\r
-                               record.Write(this.context.Protocol);\r
-                               record.Write((short)fragment.Length);\r
-                               record.Write(fragment);\r
-\r
-                               // Update buffer position\r
-                               position += fragmentLength;\r
-                       }\r
-\r
-                       return record.ToArray();\r
-               }\r
-               \r
-               #endregion\r
-\r
-               #region Cryptography Methods\r
-\r
-               private byte[] encryptRecordFragment(\r
-                       ContentType     contentType, \r
-                       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(\r
-                       ContentType     contentType, \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
-}\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.IO;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+using Mono.Security.Protocol.Tls.Handshake;
+
+namespace Mono.Security.Protocol.Tls
+{
+       internal abstract class RecordProtocol
+       {
+               #region Fields
+
+               protected Stream        innerStream;
+               protected Context       context;
+
+               #endregion
+
+               #region Properties
+
+               public Stream InnerStream
+               {
+                       get { return this.innerStream; }
+                       set { this.innerStream = value; }
+               }
+
+               public Context Context
+               {
+                       get { return this.context; }
+                       set { this.context = value; }
+               }
+
+               #endregion
+
+               #region Constructors
+
+               public RecordProtocol(Stream innerStream, Context context)
+               {
+                       this.innerStream                        = innerStream;
+                       this.context                            = context;
+                       this.context.RecordProtocol = this;
+               }
+
+               #endregion
+
+               #region Abstract Methods
+
+               public abstract void SendRecord(HandshakeType type);
+               protected abstract void ProcessHandshakeMessage(TlsStream handMsg);
+               protected abstract void ProcessChangeCipherSpec();
+                               
+               #endregion
+
+               #region Reveive Record Methods
+
+               public byte[] ReceiveRecord()
+               {
+                       if (this.context.ConnectionEnd)
+                       {
+                               throw new TlsException(
+                                       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);
+
+                       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)
+                       {
+                       }
+                       else
+                       {
+                               if (this.context.IsActual &&
+                                       contentType != ContentType.ChangeCipherSpec)
+                               {
+                                       message = this.decryptRecordFragment(
+                                               contentType, 
+                                               message.ToArray());
+
+                                       DebugHelper.WriteLine("Decrypted record data", message.ToArray());
+                               }
+                       }
+
+                       // Set last handshake message received to None
+                       this.context.LastHandshakeMsg = HandshakeType.None;
+                       
+                       // Process record
+                       byte[] result = message.ToArray();
+
+                       switch (contentType)
+                       {
+                               case ContentType.Alert:
+                                       this.processAlert(
+                                               (AlertLevel)message.ReadByte(),
+                                               (AlertDescription)message.ReadByte());
+                                       break;
+
+                               case ContentType.ChangeCipherSpec:
+                                       this.ProcessChangeCipherSpec();
+                                       break;
+
+                               case ContentType.ApplicationData:
+                                       break;
+
+                               case ContentType.Handshake:
+                                       while (!message.EOF)
+                                       {
+                                               this.ProcessHandshakeMessage(message);
+                                       }
+
+                                       // Update handshakes of current messages
+                                       this.context.HandshakeMessages.Write(message.ToArray());
+                                       break;
+
+                               default:
+                                       throw new TlsException(
+                                               AlertDescription.UnexpectedMessage,
+                                               "Unknown record received from server.");
+                       }
+
+                       return result;
+               }
+
+               private short readShort()
+               {
+                       byte[] b = new byte[2];
+                       this.innerStream.Read(b, 0, b.Length);
+
+                       short val = BitConverter.ToInt16(b, 0);
+
+                       return System.Net.IPAddress.HostToNetworkOrder(val);
+               }
+
+               private void processAlert(
+                       AlertLevel                      alertLevel, 
+                       AlertDescription        alertDesc)
+               {
+                       switch (alertLevel)
+                       {
+                               case AlertLevel.Fatal:
+                                       throw new TlsException(alertLevel, alertDesc);
+
+                               case AlertLevel.Warning:
+                               default:
+                               switch (alertDesc)
+                               {
+                                       case AlertDescription.CloseNotify:
+                                               this.context.ConnectionEnd = true;
+                                               break;
+                               }
+                               break;
+                       }
+               }
+
+               #endregion
+
+               #region Send Alert Methods
+
+               public void SendAlert(AlertDescription description)
+               {
+                       this.SendAlert(new Alert(description));
+               }
+
+               public void SendAlert(
+                       AlertLevel                      level, 
+                       AlertDescription        description)
+               {
+                       this.SendAlert(new Alert(level, description));
+               }
+
+               public void SendAlert(Alert alert)
+               {
+                       DebugHelper.WriteLine(">>>> Write Alert ({0}|{1})", alert.Description, alert.Message);
+
+                       // Write record
+                       this.SendRecord(
+                               ContentType.Alert, 
+                               new byte[]{(byte)alert.Level, (byte)alert.Description});
+
+                       if (alert.IsCloseNotify)
+                       {
+                               this.context.ConnectionEnd = true;
+                       }
+               }
+
+               #endregion
+
+               #region Send Record Methods
+
+               public void SendChangeCipherSpec()
+               {
+                       DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
+
+                       // Send Change Cipher Spec message as a plain message
+                       this.context.IsActual = false;
+
+                       // Send Change Cipher Spec message
+                       this.SendRecord(ContentType.ChangeCipherSpec, new byte[] {1});
+
+                       // Reset sequence numbers
+                       this.context.WriteSequenceNumber = 0;
+
+                       // Make the pending state to be the current state
+                       this.context.IsActual = true;
+
+                       // Send Finished message
+                       this.SendRecord(HandshakeType.Finished);                        
+               }
+
+               public void SendRecord(ContentType contentType, byte[] recordData)
+               {
+                       if (this.context.ConnectionEnd)
+                       {
+                               throw new TlsException(
+                                       AlertDescription.InternalError,
+                                       "The session is finished and it's no longer valid.");
+                       }
+
+                       byte[] record = this.EncodeRecord(contentType, recordData);
+
+                       this.innerStream.Write(record, 0, record.Length);
+               }
+
+               public byte[] EncodeRecord(ContentType contentType, byte[] recordData)
+               {
+                       return this.EncodeRecord(
+                               contentType,
+                               recordData,
+                               0,
+                               recordData.Length);
+               }
+
+               public byte[] EncodeRecord(
+                       ContentType     contentType, 
+                       byte[]          recordData,
+                       int                     offset,
+                       int                     count)
+               {
+                       if (this.context.ConnectionEnd)
+                       {
+                               throw new TlsException(
+                                       AlertDescription.InternalError,
+                                       "The session is finished and it's no longer valid.");
+                       }
+
+                       TlsStream record = new TlsStream();
+
+                       int     position = offset;
+
+                       while (position < ( offset + count ))
+                       {
+                               short   fragmentLength = 0;
+                               byte[]  fragment;
+
+                               if ((count - position) > Context.MAX_FRAGMENT_SIZE)
+                               {
+                                       fragmentLength = Context.MAX_FRAGMENT_SIZE;
+                               }
+                               else
+                               {
+                                       fragmentLength = (short)(count - position);
+                               }
+
+                               // Fill the fragment data
+                               fragment = new byte[fragmentLength];
+                               Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);
+
+                               if (this.context.IsActual)
+                               {
+                                       // Encrypt fragment
+                                       fragment = this.encryptRecordFragment(contentType, fragment);
+                               }
+
+                               // Write tls message
+                               record.Write((byte)contentType);
+                               record.Write(this.context.Protocol);
+                               record.Write((short)fragment.Length);
+                               record.Write(fragment);
+
+                               DebugHelper.WriteLine("Record data", fragment);
+
+                               // Update buffer position
+                               position += fragmentLength;
+                       }
+
+                       return record.ToArray();
+               }
+               
+               #endregion
+
+               #region Cryptography Methods
+
+               private byte[] encryptRecordFragment(
+                       ContentType     contentType, 
+                       byte[]          fragment)
+               {
+                       byte[] mac      = null;
+
+                       // Calculate message MAC
+                       if (this.Context is ClientContext)
+                       {
+                               mac     = this.context.Cipher.ComputeClientRecordMAC(contentType, fragment);
+                       }       
+                       else
+                       {
+                               mac     = this.context.Cipher.ComputeServerRecordMAC(contentType, fragment);
+                       }
+
+                       DebugHelper.WriteLine(">>>> Record MAC", mac);
+
+                       // Encrypt the message
+                       byte[] ecr = this.context.Cipher.EncryptRecord(fragment, mac);
+
+                       // Set new Client Cipher IV
+                       if (this.context.Cipher.CipherMode == CipherMode.CBC)
+                       {
+                               byte[] iv = new byte[this.context.Cipher.IvSize];
+                               Buffer.BlockCopy(ecr, ecr.Length - iv.Length, iv, 0, iv.Length);
+
+                               this.context.Cipher.UpdateClientCipherIV(iv);
+                       }
+
+                       // Update sequence number
+                       this.context.WriteSequenceNumber++;
+
+                       return ecr;
+               }
+
+               private TlsStream decryptRecordFragment(
+                       ContentType     contentType, 
+                       byte[]          fragment)
+               {
+                       byte[]  dcrFragment             = null;
+                       byte[]  dcrMAC                  = null;
+                       bool    badRecordMac    = false;
+
+                       try
+                       {
+                               this.context.Cipher.DecryptRecord(fragment, ref dcrFragment, ref dcrMAC);
+                       }
+                       catch
+                       {
+                               if (this.context is ServerContext)
+                               {
+                                       this.Context.RecordProtocol.SendAlert(AlertDescription.DecryptionFailed);
+                               }
+
+                               throw;
+                       }
+                       
+                       // Generate record MAC
+                       byte[] mac = null;
+
+                       if (this.Context is ClientContext)
+                       {
+                               mac = this.context.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);
+                       }
+                       else
+                       {
+                               mac = this.context.Cipher.ComputeClientRecordMAC(contentType, dcrFragment);
+                       }
+
+                       DebugHelper.WriteLine(">>>> Record MAC", mac);
+
+                       // Check record MAC
+                       if (mac.Length != dcrMAC.Length)
+                       {
+                               badRecordMac = true;
+                       }
+                       else
+                       {
+                               for (int i = 0; i < mac.Length; i++)
+                               {
+                                       if (mac[i] != dcrMAC[i])
+                                       {
+                                               badRecordMac = true;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (badRecordMac)
+                       {
+                               throw new TlsException(AlertDescription.BadRecordMAC, "Bad record MAC");
+                       }
+
+                       // Update sequence number
+                       this.context.ReadSequenceNumber++;
+
+                       return new TlsStream(dcrFragment);
+               }
+
+               #endregion
+       }
+}