comment as X509Certificate2 is in System.dll, which cause a circular dependency issue :(
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / TlsCipherSuite.cs
index be433654584f44ff114525c36f20bfabe69632df..3eaaee01df1c487405ae152f04a546d1f0be3efb 100644 (file)
-/* Transport Security Layer (TLS)
- * Copyright (c) 2003 Carlos Guzmán Álvarez
- * 
- * 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.
- */
+// Transport Security Layer (TLS)
+// Copyright (c) 2003-2004 Carlos Guzman Alvarez
+// Copyright (C) 2006 Novell, Inc (http://www.novell.com)
+//
+// 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.Text;
 using System.Security.Cryptography;
-using System.Security.Cryptography.X509Certificates;
-
-using Mono.Security;
-using Mono.Security.Cryptography;
 
 namespace Mono.Security.Protocol.Tls
 {
-       internal class TlsCipherSuite : TlsAbstractCipherSuite
+       internal class TlsCipherSuite : CipherSuite
        {
-               #region CONSTRUCTORS
+               private const int MacHeaderLength = 13;
+               private byte[] header;
+
+               #region Constructors
                
-               public TlsCipherSuite(short code, string name, string algName, 
-                       string hashName, bool exportable, bool blockMode, 
-                       byte keyMaterialSize, byte expandedKeyMaterialSize, 
-                       short effectiveKeyBytes, byte ivSize, byte blockSize) 
-                       : base (code, name, algName, hashName, exportable, blockMode,
-                       keyMaterialSize, expandedKeyMaterialSize, effectiveKeyBytes,
-                       ivSize, blockSize)
+               public TlsCipherSuite(
+                       short code, string name, CipherAlgorithmType cipherAlgorithmType, 
+                       HashAlgorithmType hashAlgorithmType, ExchangeAlgorithmType exchangeAlgorithmType,
+                       bool exportable, bool blockMode, byte keyMaterialSize, 
+                       byte expandedKeyMaterialSize, short effectiveKeyBytes, 
+                       byte ivSize, byte blockSize) 
+                       :base(code, name, cipherAlgorithmType, hashAlgorithmType, 
+                       exchangeAlgorithmType, exportable, blockMode, keyMaterialSize, 
+                       expandedKeyMaterialSize, effectiveKeyBytes, ivSize, blockSize)
                {
                }
 
                #endregion
 
-               #region METHODS
+               #region MAC Generation Methods
 
-               public override byte[] EncryptRecord(byte[] fragment, byte[] mac)
+               public override byte[] ComputeServerRecordMAC(ContentType contentType, byte[] fragment)
                {
-                       // Encryption ( fragment + mac [+ padding + padding_length] )
-                       MemoryStream ms = new MemoryStream();
-                       CryptoStream cs = new CryptoStream(ms, encryptionCipher, CryptoStreamMode.Write);
-
-                       cs.Write(fragment, 0, fragment.Length);
-                       cs.Write(mac, 0, mac.Length);
-                       if (cipherMode == CipherMode.CBC)
-                       {
-                               // Calculate padding_length
-                               int fragmentLength      = fragment.Length + mac.Length + 1;
-                               int paddingLength       = (((fragmentLength/blockSize)*blockSize) + blockSize) - fragmentLength;
-
-                               // Write padding length byte
-                               cs.WriteByte((byte)paddingLength);
-                       }
-                       //cs.FlushFinalBlock();
-                       cs.Close();                     
-
-                       return ms.ToArray();
+                       if (header == null)
+                               header = new byte [MacHeaderLength];
+
+                       ulong seqnum = (Context is ClientContext) ? Context.ReadSequenceNumber : Context.WriteSequenceNumber;
+                       Write (header, 0, seqnum);
+                       header [8] = (byte) contentType;
+                       Write (header, 9, this.Context.Protocol);
+                       Write (header, 11, (short)fragment.Length);
+
+                       HashAlgorithm mac = this.ServerHMAC;
+                       mac.TransformBlock (header, 0, header.Length, header, 0);
+                       mac.TransformBlock (fragment, 0, fragment.Length, fragment, 0);
+                       // hack, else the method will allocate a new buffer of the same length (negative half the optimization)
+                       mac.TransformFinalBlock (CipherSuite.EmptyArray, 0, 0);
+                       return mac.Hash;
                }
 
-               public override void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC)
+               public override byte[] ComputeClientRecordMAC(ContentType contentType, byte[] fragment)
                {
-                       int     fragmentSize    = 0;
-                       int paddingLength       = 0;
-
-                       // Decrypt message fragment ( fragment + mac [+ padding + padding_length] )
-                       byte[] buffer = new byte[fragment.Length];
-                       decryptionCipher.TransformBlock(fragment, 0, fragment.Length, buffer, 0);
-
-                       // Calculate fragment size
-                       if (cipherMode == CipherMode.CBC)
-                       {
-                               // Calculate padding_length
-                               paddingLength = buffer[buffer.Length - 1];
-                               for (int i = (buffer.Length - 1); i > (buffer.Length - (paddingLength + 1)); i--)
-                               {
-                                       if (buffer[i] != paddingLength)
-                                       {
-                                               paddingLength = 0;
-                                               break;
-                                       }
-                               }
-
-                               fragmentSize = (buffer.Length - (paddingLength + 1)) - HashSize;
-                       }
-                       else
-                       {
-                               fragmentSize = buffer.Length - HashSize;
-                       }
-
-                       dcrFragment = new byte[fragmentSize];
-                       dcrMAC          = new byte[HashSize];
-
-                       Buffer.BlockCopy(buffer, 0, dcrFragment, 0, dcrFragment.Length);
-                       Buffer.BlockCopy(buffer, dcrFragment.Length, dcrMAC, 0, dcrMAC.Length);
+                       if (header == null)
+                               header = new byte [MacHeaderLength];
+
+                       ulong seqnum = (Context is ClientContext) ? Context.WriteSequenceNumber : Context.ReadSequenceNumber;
+                       Write (header, 0, seqnum);
+                       header [8] = (byte) contentType;
+                       Write (header, 9, this.Context.Protocol);
+                       Write (header, 11, (short)fragment.Length);
+
+                       HashAlgorithm mac = this.ClientHMAC;
+                       mac.TransformBlock (header, 0, header.Length, header, 0);
+                       mac.TransformBlock (fragment, 0, fragment.Length, fragment, 0);
+                       // hack, else the method will allocate a new buffer of the same length (negative half the optimization)
+                       mac.TransformFinalBlock (CipherSuite.EmptyArray, 0, 0);
+                       return mac.Hash;
                }
 
                #endregion
 
-               #region KEY_GENERATION_METODS
+               #region Key Generation Methods
 
-               public override void CreateMasterSecret(byte[] preMasterSecret)
+               public override void ComputeMasterSecret(byte[] preMasterSecret)
                {
-                       TlsStream seed = new TlsStream();
-
-                       // Seed
-                       seed.Write(context.ClientRandom);
-                       seed.Write(context.ServerRandom);
-
                        // Create master secret
-                       context.MasterSecret = new byte[preMasterSecret.Length];
-                       context.MasterSecret = PRF(preMasterSecret, "master secret", seed.ToArray(), 48);
+                       this.Context.MasterSecret = new byte[preMasterSecret.Length];
+                       this.Context.MasterSecret = this.PRF(
+                               preMasterSecret, "master secret", this.Context.RandomCS, 48);
 
-                       seed.Reset();
+                       DebugHelper.WriteLine(">>>> MasterSecret", this.Context.MasterSecret);
                }
 
-               public override void CreateKeys()
+               public override void ComputeKeys()
                {
-                       TlsStream seed = new TlsStream();
-
-                       // Seed
-                       seed.Write(context.ServerRandom);
-                       seed.Write(context.ClientRandom);
-
                        // Create keyblock
                        TlsStream keyBlock = new TlsStream(
-                               PRF(this.Context.MasterSecret, 
+                               this.PRF(
+                               this.Context.MasterSecret, 
                                "key expansion",
-                               seed.ToArray(),
+                               this.Context.RandomSC,
                                this.KeyBlockSize));
 
-                       this.Context.ClientWriteMAC = keyBlock.ReadBytes(this.HashSize);
-                       this.Context.ServerWriteMAC = keyBlock.ReadBytes(this.HashSize);
+                       this.Context.Negotiating.ClientWriteMAC = keyBlock.ReadBytes(this.HashSize);
+                       this.Context.Negotiating.ServerWriteMAC = keyBlock.ReadBytes(this.HashSize);
                        this.Context.ClientWriteKey = keyBlock.ReadBytes(this.KeyMaterialSize);
                        this.Context.ServerWriteKey = keyBlock.ReadBytes(this.KeyMaterialSize);
 
@@ -159,39 +127,51 @@ namespace Mono.Security.Protocol.Tls
                                }
                                else
                                {
-                                       this.Context.ClientWriteIV = new byte[0];
-                                       this.Context.ServerWriteIV = new byte[0];
+                                       this.Context.ClientWriteIV = CipherSuite.EmptyArray;
+                                       this.Context.ServerWriteIV = CipherSuite.EmptyArray;
                                }
                        }
                        else
                        {
-                               // Seed
-                               seed.Reset();
-                               seed.Write(this.Context.ClientRandom);
-                               seed.Write(this.Context.ServerRandom);
-
                                // Generate final write keys
-                               byte[] finalClientWriteKey      = PRF(this.Context.ClientWriteKey, "client write key", seed.ToArray(), this.KeyMaterialSize);
-                               byte[] finalServerWriteKey      = PRF(this.Context.ServerWriteKey, "server write key", seed.ToArray(), this.KeyMaterialSize);
+                               byte[] finalClientWriteKey      = PRF(this.Context.ClientWriteKey, "client write key", this.Context.RandomCS, this.ExpandedKeyMaterialSize);
+                               byte[] finalServerWriteKey      = PRF(this.Context.ServerWriteKey, "server write key", this.Context.RandomCS, this.ExpandedKeyMaterialSize);
                                
                                this.Context.ClientWriteKey     = finalClientWriteKey;
                                this.Context.ServerWriteKey     = finalServerWriteKey;
 
-                               // Generate IV block
-                               byte[] ivBlock = PRF(new byte[]{}, "IV block", seed.ToArray(), this.IvSize*2);
+                               if (this.IvSize > 0) 
+                               {
+                                       // Generate IV block
+                                       byte[] ivBlock = PRF(CipherSuite.EmptyArray, "IV block", this.Context.RandomCS, this.IvSize*2);
+
+                                       // Generate IV keys
+                                       this.Context.ClientWriteIV = new byte[this.IvSize];                             
+                                       Buffer.BlockCopy(ivBlock, 0, this.Context.ClientWriteIV, 0, this.Context.ClientWriteIV.Length);
 
-                               // Generate IV keys
-                               this.Context.ClientWriteIV = new byte[this.IvSize];                             
-                               System.Array.Copy(ivBlock, 0, this.Context.ClientWriteIV, 0, this.Context.ClientWriteIV.Length);
-                               this.Context.ServerWriteIV = new byte[this.IvSize];
-                               System.Array.Copy(ivBlock, this.IvSize, this.Context.ServerWriteIV, 0, this.Context.ServerWriteIV.Length);
+                                       this.Context.ServerWriteIV = new byte[this.IvSize];
+                                       Buffer.BlockCopy(ivBlock, this.IvSize, this.Context.ServerWriteIV, 0, this.Context.ServerWriteIV.Length);
+                               }
+                               else 
+                               {
+                                       this.Context.ClientWriteIV = CipherSuite.EmptyArray;
+                                       this.Context.ServerWriteIV = CipherSuite.EmptyArray;
+                               }
                        }
 
+                       DebugHelper.WriteLine(">>>> KeyBlock", keyBlock.ToArray());
+                       DebugHelper.WriteLine(">>>> ClientWriteKey", this.Context.ClientWriteKey);
+                       DebugHelper.WriteLine(">>>> ClientWriteIV", this.Context.ClientWriteIV);
+                       DebugHelper.WriteLine(">>>> ClientWriteMAC", this.Context.Negotiating.ClientWriteMAC);
+                       DebugHelper.WriteLine(">>>> ServerWriteKey", this.Context.ServerWriteKey);
+                       DebugHelper.WriteLine(">>>> ServerWriteIV", this.Context.ServerWriteIV);
+                       DebugHelper.WriteLine(">>>> ServerWriteMAC", this.Context.Negotiating.ServerWriteMAC);
+
+                       ClientSessionCache.SetContextInCache (this.Context);
                        // Clear no more needed data
-                       seed.Reset();
                        keyBlock.Reset();
                }
 
                #endregion
        }
-}
\ No newline at end of file
+}