-/* 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.IO;\r
-using System.Text;\r
-using System.Security.Cryptography;\r
-\r
-using Mono.Security;\r
-using Mono.Security.Cryptography;\r
-using Mono.Security.X509;\r
-\r
-namespace Mono.Security.Protocol.Tls\r
-{\r
- internal abstract class CipherSuite\r
- {\r
- #region FIELDS\r
-\r
- private short code;\r
- private string name;\r
- private string algName;\r
- private string hashName;\r
- private bool isExportable;\r
- private CipherMode cipherMode;\r
- private byte keyMaterialSize;\r
- private byte expandedKeyMaterialSize;\r
- private short effectiveKeyBits;\r
- private byte ivSize;\r
- private byte blockSize;\r
- private TlsSessionContext context;\r
- private SymmetricAlgorithm encryptionAlgorithm;\r
- private ICryptoTransform encryptionCipher;\r
- private SymmetricAlgorithm decryptionAlgorithm;\r
- private ICryptoTransform decryptionCipher;\r
- private KeyedHashAlgorithm clientHMAC;\r
- private KeyedHashAlgorithm serverHMAC;\r
- \r
- #endregion\r
-\r
- #region PROTECTED_PROPERTIES\r
-\r
- protected ICryptoTransform EncryptionCipher\r
- {\r
- get { return encryptionCipher; }\r
- }\r
-\r
- protected ICryptoTransform DecryptionCipher\r
- {\r
- get { return decryptionCipher; }\r
- }\r
-\r
- protected KeyedHashAlgorithm ClientHMAC\r
- {\r
- get { return clientHMAC; }\r
- }\r
- \r
- protected KeyedHashAlgorithm ServerHMAC\r
- {\r
- get { return serverHMAC; }\r
- }\r
-\r
- #endregion\r
-\r
- #region PROPERTIES\r
-\r
- public short Code\r
- {\r
- get { return code; }\r
- }\r
-\r
- public string Name\r
- {\r
- get { return name; }\r
- }\r
-\r
- public bool IsExportable\r
- {\r
- get { return isExportable; }\r
- }\r
-\r
- public CipherMode CipherMode\r
- {\r
- get { return cipherMode; }\r
- }\r
-\r
- public int HashSize\r
- {\r
- get { return (int)(hashName == "MD5" ? 16 : 20); }\r
- }\r
-\r
- public byte KeyMaterialSize\r
- {\r
- get { return keyMaterialSize; }\r
- }\r
-\r
- public int KeyBlockSize\r
- {\r
- get \r
- {\r
- return keyMaterialSize*2 + HashSize*2 + ivSize*2;\r
- }\r
- }\r
-\r
- public byte ExpandedKeyMaterialSize\r
- {\r
- get { return expandedKeyMaterialSize; }\r
- }\r
-\r
- public byte EffectiveKeyBits\r
- {\r
- get { return EffectiveKeyBits; }\r
- }\r
- \r
- public byte IvSize\r
- {\r
- get { return ivSize; }\r
- }\r
-\r
- public byte BlockSize\r
- {\r
- get { return blockSize; }\r
- }\r
-\r
- public string HashName\r
- {\r
- get { return hashName; }\r
- }\r
-\r
- public TlsSessionContext Context\r
- {\r
- get { return context; }\r
- set { context = value; }\r
- }\r
-\r
- #endregion\r
-\r
- #region CONSTRUCTORS\r
- \r
- public CipherSuite(short code, string name, string algName, string hashName, bool exportable, bool blockMode, byte keyMaterialSize, byte expandedKeyMaterialSize, short effectiveKeyBytes, byte ivSize, byte blockSize)\r
- {\r
- this.code = code;\r
- this.name = name;\r
- this.algName = algName;\r
- this.hashName = hashName;\r
- this.isExportable = exportable;\r
- if (blockMode)\r
- {\r
- this.cipherMode = CipherMode.CBC;\r
- }\r
- this.keyMaterialSize = keyMaterialSize;\r
- this.expandedKeyMaterialSize = expandedKeyMaterialSize;\r
- this.effectiveKeyBits = effectiveKeyBits;\r
- this.ivSize = ivSize;\r
- this.blockSize = blockSize;\r
- }\r
-\r
- #endregion\r
-\r
- #region METHODS\r
-\r
- public void InitializeCipher()\r
- {\r
- createEncryptionCipher();\r
- createDecryptionCipher();\r
- }\r
-\r
- public RSA CreateRSA()\r
- {\r
- RSA rsa;\r
- if (this.Context.ServerSettings.ServerKeyExchange)\r
- {\r
- rsa = new RSACryptoServiceProvider();\r
- rsa.ImportParameters(this.Context.ServerSettings.RsaParameters);\r
- }\r
- else\r
- {\r
- rsa = this.Context.ServerSettings.ServerCertificates[0].RSA;\r
- }\r
- \r
- return rsa;\r
- }\r
-\r
- public RSACryptoServiceProvider CreateRSA(RSAParameters rsaParams)\r
- { \r
- // BUG: MS BCL 1.0 can't import a key which \r
- // isn't the same size as the one present in\r
- // the container.\r
- int keySize = (rsaParams.Modulus.Length << 3);\r
- RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(keySize);\r
- rsa.ImportParameters(rsaParams);\r
-\r
- return rsa;\r
- }\r
-\r
- public void UpdateClientCipherIV(byte[] iv)\r
- {\r
- if (cipherMode == CipherMode.CBC)\r
- {\r
- // Set the new IV\r
- encryptionAlgorithm.IV = iv;\r
- \r
- // Create encryption cipher with the new IV\r
- encryptionCipher = encryptionAlgorithm.CreateEncryptor();\r
- }\r
- }\r
-\r
- public void UpdateServerCipherIV(byte[] iv)\r
- {\r
- if (cipherMode == CipherMode.CBC)\r
- {\r
- // Set the new IV\r
- decryptionAlgorithm.IV = iv;\r
- \r
- // Create encryption cipher with the new IV\r
- decryptionCipher = decryptionAlgorithm.CreateDecryptor();\r
- }\r
- }\r
-\r
- public byte[] EncryptRecord(byte[] fragment, byte[] mac)\r
- {\r
- // Encryption ( fragment + mac [+ padding + padding_length] )\r
- MemoryStream ms = new MemoryStream();\r
- CryptoStream cs = new CryptoStream(ms, this.EncryptionCipher, CryptoStreamMode.Write);\r
-\r
- cs.Write(fragment, 0, fragment.Length);\r
- cs.Write(mac, 0, mac.Length);\r
- if (this.CipherMode == CipherMode.CBC)\r
- {\r
- // Calculate padding_length\r
- int fragmentLength = fragment.Length + mac.Length + 1;\r
- int paddingLength = this.blockSize - fragmentLength % this.blockSize;\r
- if (paddingLength == this.blockSize)\r
- {\r
- paddingLength = 0;\r
- }\r
-\r
- // Write padding length byte\r
- for (int i = 0; i < (paddingLength + 1); i++)\r
- {\r
- cs.WriteByte((byte)paddingLength);\r
- }\r
- }\r
- // cs.FlushFinalBlock();\r
- cs.Close(); \r
-\r
- return ms.ToArray();\r
- }\r
-\r
- public void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC)\r
- {\r
- int fragmentSize = 0;\r
- int paddingLength = 0;\r
-\r
- // Decrypt message fragment ( fragment + mac [+ padding + padding_length] )\r
- byte[] buffer = new byte[fragment.Length];\r
- this.DecryptionCipher.TransformBlock(fragment, 0, fragment.Length, buffer, 0);\r
-\r
- // Calculate fragment size\r
- if (this.CipherMode == CipherMode.CBC)\r
- {\r
- // Calculate padding_length\r
- paddingLength = buffer[buffer.Length - 1];\r
-\r
- /* Review this that is valid way for TLS1 but not for SSL3\r
- for (int i = (buffer.Length - 1); i > (buffer.Length - (paddingLength + 1)); i--)\r
- {\r
- if (buffer[i] != paddingLength)\r
- {\r
- paddingLength = 0;\r
- break;\r
- }\r
- }\r
- */\r
-\r
- fragmentSize = (buffer.Length - (paddingLength + 1)) - HashSize;\r
- }\r
- else\r
- {\r
- fragmentSize = buffer.Length - HashSize;\r
- }\r
-\r
- dcrFragment = new byte[fragmentSize];\r
- dcrMAC = new byte[HashSize];\r
-\r
- Buffer.BlockCopy(buffer, 0, dcrFragment, 0, dcrFragment.Length);\r
- Buffer.BlockCopy(buffer, dcrFragment.Length, dcrMAC, 0, dcrMAC.Length);\r
- }\r
-\r
- #endregion\r
-\r
- #region ABSTRACT_METHODS\r
-\r
- public abstract byte[] ComputeClientRecordMAC(TlsContentType contentType, byte[] fragment);\r
-\r
- public abstract byte[] ComputeServerRecordMAC(TlsContentType contentType, byte[] fragment);\r
-\r
- public abstract void ComputeMasterSecret(byte[] preMasterSecret);\r
-\r
- public abstract void ComputeKeys();\r
-\r
- #endregion\r
-\r
- #region KEY_GENERATION_METODS\r
-\r
- public byte[] CreatePremasterSecret()\r
- {\r
- TlsStream stream = new TlsStream();\r
-\r
- // Write protocol version\r
- stream.Write((short)this.Context.Protocol);\r
-\r
- // Generate random bytes\r
- stream.Write(this.context.GetSecureRandomBytes(46));\r
-\r
- byte[] preMasterSecret = stream.ToArray();\r
-\r
- stream.Reset();\r
-\r
- return preMasterSecret;\r
- }\r
-\r
- public byte[] PRF(byte[] secret, string label, byte[] data, int length)\r
- {\r
- MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();\r
- SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();\r
-\r
- int secretLen = secret.Length / 2;\r
-\r
- // Seed\r
- TlsStream seedStream = new TlsStream();\r
- seedStream.Write(Encoding.ASCII.GetBytes(label));\r
- seedStream.Write(data);\r
- byte[] seed = seedStream.ToArray();\r
- seedStream.Reset();\r
-\r
- // Secret 1\r
- byte[] secret1 = new byte[secretLen];\r
- System.Array.Copy(secret, 0, secret1, 0, secretLen);\r
-\r
- // Secret2\r
- byte[] secret2 = new byte[secretLen];\r
- System.Array.Copy(secret, secretLen, secret2, 0, secretLen);\r
-\r
- // Secret 1 processing\r
- byte[] p_md5 = Expand("MD5", secret1, seed, length);\r
-\r
- // Secret 2 processing\r
- byte[] p_sha = Expand("SHA1", secret2, seed, length);\r
-\r
- // Perfor XOR of both results\r
- byte[] masterSecret = new byte[length];\r
- for (int i = 0; i < masterSecret.Length; i++)\r
- {\r
- masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);\r
- }\r
-\r
- return masterSecret;\r
- }\r
- \r
- public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)\r
- {\r
- int hashLength = hashName == "MD5" ? 16 : 20;\r
- int iterations = (int)(length / hashLength);\r
- if ((length % hashLength) > 0)\r
- {\r
- iterations++;\r
- }\r
- \r
- HMAC hmac = new HMAC(hashName, secret);\r
- TlsStream resMacs = new TlsStream();\r
- \r
- byte[][] hmacs = new byte[iterations + 1][];\r
- hmacs[0] = seed;\r
- for (int i = 1; i <= iterations; i++)\r
- { \r
- TlsStream hcseed = new TlsStream();\r
- hmac.TransformFinalBlock(hmacs[i-1], 0, hmacs[i-1].Length);\r
- hmacs[i] = hmac.Hash;\r
- hcseed.Write(hmacs[i]);\r
- hcseed.Write(seed);\r
- hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);\r
- resMacs.Write(hmac.Hash);\r
- hcseed.Reset();\r
- }\r
-\r
- byte[] res = new byte[length];\r
- \r
- System.Array.Copy(resMacs.ToArray(), 0, res, 0, res.Length);\r
-\r
- resMacs.Reset();\r
-\r
- return res;\r
- }\r
-\r
- #endregion\r
-\r
- #region PRIVATE_METHODS\r
-\r
- // This code is from Mono.Security.X509Certificate class.\r
- private byte[] getUnsignedBigInteger(byte[] integer) \r
- {\r
- if (integer[0] == 0x00) \r
- {\r
- // this first byte is added so we're sure it's an unsigned integer\r
- // however we can't feed it into RSAParameters or DSAParameters\r
- int length = integer.Length - 1;\r
- byte[] uinteger = new byte[length]; \r
- Array.Copy(integer, 1, uinteger, 0, length);\r
-\r
- return uinteger;\r
- }\r
- else\r
- {\r
- return integer;\r
- }\r
- }\r
-\r
- private void createEncryptionCipher()\r
- {\r
- // Create and configure the symmetric algorithm\r
- switch (this.algName)\r
- {\r
- case "RC4":\r
- encryptionAlgorithm = new ARC4Managed();\r
- break;\r
-\r
- default:\r
- encryptionAlgorithm = SymmetricAlgorithm.Create(algName);\r
- break;\r
- }\r
-\r
- // If it's a block cipher\r
- if (cipherMode == CipherMode.CBC)\r
- {\r
- // Configure encrypt algorithm\r
- encryptionAlgorithm.Mode = this.cipherMode;\r
- encryptionAlgorithm.Padding = PaddingMode.None;\r
- encryptionAlgorithm.KeySize = this.keyMaterialSize * 8;\r
- encryptionAlgorithm.BlockSize = this.blockSize * 8;\r
- }\r
-\r
- // Set the key and IV for the algorithm\r
- encryptionAlgorithm.Key = context.ClientWriteKey;\r
- encryptionAlgorithm.IV = context.ClientWriteIV;\r
- \r
- // Create encryption cipher\r
- encryptionCipher = encryptionAlgorithm.CreateEncryptor();\r
-\r
- // Create the HMAC algorithm for the client\r
- clientHMAC = new HMAC(hashName, context.ClientWriteMAC);\r
- }\r
-\r
- private void createDecryptionCipher()\r
- {\r
- // Create and configure the symmetric algorithm\r
- switch (this.algName)\r
- {\r
- case "RC4":\r
- decryptionAlgorithm = new ARC4Managed();\r
- break;\r
-\r
- default:\r
- decryptionAlgorithm = SymmetricAlgorithm.Create(algName);\r
- break;\r
- }\r
-\r
- // If it's a block cipher\r
- if (cipherMode == CipherMode.CBC)\r
- {\r
- // Configure encrypt algorithm\r
- decryptionAlgorithm.Mode = this.cipherMode;\r
- decryptionAlgorithm.Padding = PaddingMode.None;\r
- decryptionAlgorithm.KeySize = this.keyMaterialSize * 8;\r
- decryptionAlgorithm.BlockSize = this.blockSize * 8;\r
- }\r
-\r
- // Set the key and IV for the algorithm\r
- decryptionAlgorithm.Key = context.ServerWriteKey;\r
- decryptionAlgorithm.IV = context.ServerWriteIV;\r
-\r
- // Create decryption cipher \r
- decryptionCipher = decryptionAlgorithm.CreateDecryptor();\r
-\r
- // Create the HMAC algorithm for the server\r
- serverHMAC = new HMAC(hashName, context.ServerWriteMAC);\r
- }\r
-\r
- #endregion\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.Text;
+using System.Security.Cryptography;
+
+using Mono.Security;
+using Mono.Security.Cryptography;
+using Mono.Security.X509;
+using M = Mono.Security.Cryptography;
+
+namespace Mono.Security.Protocol.Tls
+{
+ internal abstract class CipherSuite
+ {
+ #region Fields
+
+ private short code;
+ private string name;
+ private CipherAlgorithmType cipherAlgorithmType;
+ private HashAlgorithmType hashAlgorithmType;
+ private ExchangeAlgorithmType exchangeAlgorithmType;
+ private bool isExportable;
+ private CipherMode cipherMode;
+ private byte keyMaterialSize;
+ private int keyBlockSize;
+ private byte expandedKeyMaterialSize;
+ private short effectiveKeyBits;
+ private byte ivSize;
+ private byte blockSize;
+ private TlsContext context;
+ private SymmetricAlgorithm encryptionAlgorithm;
+ private ICryptoTransform encryptionCipher;
+ private SymmetricAlgorithm decryptionAlgorithm;
+ private ICryptoTransform decryptionCipher;
+ private KeyedHashAlgorithm clientHMAC;
+ private KeyedHashAlgorithm serverHMAC;
+
+ #endregion
+
+ #region Protected Properties
+
+ protected ICryptoTransform EncryptionCipher
+ {
+ get { return this.encryptionCipher; }
+ }
+
+ protected ICryptoTransform DecryptionCipher
+ {
+ get { return this.decryptionCipher; }
+ }
+
+ protected KeyedHashAlgorithm ClientHMAC
+ {
+ get { return this.clientHMAC; }
+ }
+
+ protected KeyedHashAlgorithm ServerHMAC
+ {
+ get { return this.serverHMAC; }
+ }
+
+ #endregion
+
+ #region Properties
+
+ public CipherAlgorithmType CipherAlgorithmType
+ {
+ get { return this.cipherAlgorithmType; }
+ }
+
+ public string HashAlgorithmName
+ {
+ get
+ {
+ switch (this.hashAlgorithmType)
+ {
+ case HashAlgorithmType.Md5:
+ return "MD5";
+
+ case HashAlgorithmType.Sha1:
+ return "SHA1";
+
+ default:
+ return "None";
+ }
+ }
+ }
+
+ public HashAlgorithmType HashAlgorithmType
+ {
+ get { return this.hashAlgorithmType; }
+ }
+
+ public int HashSize
+ {
+ get
+ {
+ switch (this.hashAlgorithmType)
+ {
+ case HashAlgorithmType.Md5:
+ return 16;
+
+ case HashAlgorithmType.Sha1:
+ return 20;
+
+ default:
+ return 0;
+ }
+ }
+ }
+
+ public ExchangeAlgorithmType ExchangeAlgorithmType
+ {
+ get { return this.exchangeAlgorithmType; }
+ }
+
+ public CipherMode CipherMode
+ {
+ get { return this.cipherMode; }
+ }
+
+ public short Code
+ {
+ get { return this.code; }
+ }
+
+ public string Name
+ {
+ get { return this.name; }
+ }
+
+ public bool IsExportable
+ {
+ get { return this.isExportable; }
+ }
+
+ public byte KeyMaterialSize
+ {
+ get { return this.keyMaterialSize; }
+ }
+
+ public int KeyBlockSize
+ {
+ get { return this.keyBlockSize; }
+ }
+
+ public byte ExpandedKeyMaterialSize
+ {
+ get { return this.expandedKeyMaterialSize; }
+ }
+
+ public byte EffectiveKeyBits
+ {
+ get { return this.EffectiveKeyBits; }
+ }
+
+ public byte IvSize
+ {
+ get { return this.ivSize; }
+ }
+
+ public byte BlockSize
+ {
+ get { return this.blockSize; }
+ }
+
+ public TlsContext Context
+ {
+ get { return this.context; }
+ set { this.context = value; }
+ }
+
+ #endregion
+
+ #region Constructors
+
+ public CipherSuite(
+ short code, string name, CipherAlgorithmType cipherAlgorithmType,
+ HashAlgorithmType hashAlgorithmType, ExchangeAlgorithmType exchangeAlgorithmType,
+ bool exportable, bool blockMode, byte keyMaterialSize,
+ byte expandedKeyMaterialSize, short effectiveKeyBytes,
+ byte ivSize, byte blockSize)
+ {
+ this.code = code;
+ this.name = name;
+ this.cipherAlgorithmType = cipherAlgorithmType;
+ this.hashAlgorithmType = hashAlgorithmType;
+ this.exchangeAlgorithmType = exchangeAlgorithmType;
+ this.isExportable = exportable;
+ if (blockMode)
+ {
+ this.cipherMode = CipherMode.CBC;
+ }
+ this.keyMaterialSize = keyMaterialSize;
+ this.expandedKeyMaterialSize= expandedKeyMaterialSize;
+ this.effectiveKeyBits = effectiveKeyBits;
+ this.ivSize = ivSize;
+ this.blockSize = blockSize;
+ this.keyBlockSize = this.keyMaterialSize*2 + this.HashSize*2 + this.ivSize*2;
+ }
+
+ #endregion
+
+ #region Methods
+
+ public void InitializeCipher()
+ {
+ this.createEncryptionCipher();
+ this.createDecryptionCipher();
+ }
+
+ public RSA CertificateRSA()
+ {
+ RSA rsaCert = this.Context.ServerSettings.Certificates[0].RSA;
+ RSA rsa = new RSAManaged(rsaCert.KeySize);
+
+ rsa.ImportParameters(rsaCert.ExportParameters(false));
+
+ return rsa;
+ }
+
+ public void UpdateClientCipherIV(byte[] iv)
+ {
+ if (this.cipherMode == CipherMode.CBC)
+ {
+ // Set the new IV
+ this.encryptionAlgorithm.IV = iv;
+
+ // Create encryption cipher with the new IV
+ this.encryptionCipher = this.encryptionAlgorithm.CreateEncryptor();
+ }
+ }
+
+ public void UpdateServerCipherIV(byte[] iv)
+ {
+ if (this.cipherMode == CipherMode.CBC)
+ {
+ // Set the new IV
+ this.decryptionAlgorithm.IV = iv;
+
+ // Create encryption cipher with the new IV
+ this.decryptionCipher = this.decryptionAlgorithm.CreateDecryptor();
+ }
+ }
+
+ public byte[] EncryptRecord(byte[] fragment, byte[] mac)
+ {
+ // Encryption ( fragment + mac [+ padding + padding_length] )
+ MemoryStream ms = new MemoryStream();
+ CryptoStream cs = new CryptoStream(ms, this.EncryptionCipher, CryptoStreamMode.Write);
+
+ cs.Write(fragment, 0, fragment.Length);
+ cs.Write(mac, 0, mac.Length);
+ if (this.CipherMode == CipherMode.CBC)
+ {
+ // Calculate padding_length
+ byte fragmentLength = (byte)(fragment.Length + mac.Length + 1);
+ byte paddingLength = (byte)(this.blockSize - fragmentLength % this.blockSize);
+ if (paddingLength == this.blockSize)
+ {
+ paddingLength = 0;
+ }
+
+ // Write padding length byte
+ byte[] padding = new byte[(paddingLength + 1)];
+ for (int i = 0; i < (paddingLength + 1); i++)
+ {
+ padding[i] = paddingLength;
+ }
+
+ cs.Write(padding, 0, padding.Length);
+ }
+ cs.FlushFinalBlock();
+ cs.Close();
+
+ return ms.ToArray();
+ }
+
+ public void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC)
+ {
+ int fragmentSize = 0;
+ int paddingLength = 0;
+
+ // Decrypt message fragment ( fragment + mac [+ padding + padding_length] )
+ byte[] buffer = new byte[fragment.Length];
+ this.DecryptionCipher.TransformBlock(fragment, 0, fragment.Length, buffer, 0);
+
+ // Calculate fragment size
+ if (this.CipherMode == CipherMode.CBC)
+ {
+ // Calculate padding_length
+ paddingLength = buffer[buffer.Length - 1];
+ fragmentSize = (buffer.Length - (paddingLength + 1)) - this.HashSize;
+ }
+ else
+ {
+ fragmentSize = buffer.Length - this.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);
+ }
+
+ #endregion
+
+ #region Abstract Methods
+
+ public abstract byte[] ComputeClientRecordMAC(TlsContentType contentType, byte[] fragment);
+
+ public abstract byte[] ComputeServerRecordMAC(TlsContentType contentType, byte[] fragment);
+
+ public abstract void ComputeMasterSecret(byte[] preMasterSecret);
+
+ public abstract void ComputeKeys();
+
+ #endregion
+
+ #region Key Generation Methods
+
+ public byte[] CreatePremasterSecret()
+ {
+ TlsStream stream = new TlsStream();
+
+ // Write protocol version
+ stream.Write((short)this.Context.Protocol);
+
+ // Generate random bytes
+ stream.Write(this.context.GetSecureRandomBytes(46));
+
+ byte[] preMasterSecret = stream.ToArray();
+
+ stream.Reset();
+
+ return preMasterSecret;
+ }
+
+ public byte[] PRF(byte[] secret, string label, byte[] data, int length)
+ {
+ HashAlgorithm md5 = MD5.Create();
+ HashAlgorithm sha1 = SHA1.Create();
+
+ int secretLen = secret.Length / 2;
+
+ // Seed
+ TlsStream seedStream = new TlsStream();
+ seedStream.Write(Encoding.ASCII.GetBytes(label));
+ seedStream.Write(data);
+ byte[] seed = seedStream.ToArray();
+ seedStream.Reset();
+
+ // Secret 1
+ byte[] secret1 = new byte[secretLen];
+ System.Array.Copy(secret, 0, secret1, 0, secretLen);
+
+ // Secret2
+ byte[] secret2 = new byte[secretLen];
+ System.Array.Copy(secret, secretLen, secret2, 0, secretLen);
+
+ // Secret 1 processing
+ byte[] p_md5 = Expand("MD5", secret1, seed, length);
+
+ // Secret 2 processing
+ byte[] p_sha = Expand("SHA1", secret2, seed, length);
+
+ // Perfor XOR of both results
+ byte[] masterSecret = new byte[length];
+ for (int i = 0; i < masterSecret.Length; i++)
+ {
+ masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);
+ }
+
+ return masterSecret;
+ }
+
+ public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)
+ {
+ int hashLength = hashName == "MD5" ? 16 : 20;
+ int iterations = (int)(length / hashLength);
+ if ((length % hashLength) > 0)
+ {
+ iterations++;
+ }
+
+ M.HMAC hmac = new M.HMAC(hashName, secret);
+ TlsStream resMacs = new TlsStream();
+
+ byte[][] hmacs = new byte[iterations + 1][];
+ hmacs[0] = seed;
+ for (int i = 1; i <= iterations; i++)
+ {
+ TlsStream hcseed = new TlsStream();
+ hmac.TransformFinalBlock(hmacs[i-1], 0, hmacs[i-1].Length);
+ hmacs[i] = hmac.Hash;
+ hcseed.Write(hmacs[i]);
+ hcseed.Write(seed);
+ hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);
+ resMacs.Write(hmac.Hash);
+ hcseed.Reset();
+ }
+
+ byte[] res = new byte[length];
+
+ System.Array.Copy(resMacs.ToArray(), 0, res, 0, res.Length);
+
+ resMacs.Reset();
+
+ return res;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private void createEncryptionCipher()
+ {
+ // Create and configure the symmetric algorithm
+ switch (this.cipherAlgorithmType)
+ {
+ case CipherAlgorithmType.Des:
+ this.encryptionAlgorithm = DES.Create();
+ break;
+
+ case CipherAlgorithmType.Rc2:
+ this.encryptionAlgorithm = RC2.Create();
+ break;
+
+ case CipherAlgorithmType.Rc4:
+ this.encryptionAlgorithm = new ARC4Managed();
+ break;
+
+ case CipherAlgorithmType.TripleDes:
+ this.encryptionAlgorithm = TripleDES.Create();
+ break;
+
+ case CipherAlgorithmType.Rijndael:
+ this.encryptionAlgorithm = Rijndael.Create();
+ break;
+ }
+
+ // If it's a block cipher
+ if (this.cipherMode == CipherMode.CBC)
+ {
+ // Configure encrypt algorithm
+ this.encryptionAlgorithm.Mode = this.cipherMode;
+ this.encryptionAlgorithm.Padding = PaddingMode.None;
+ this.encryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
+ this.encryptionAlgorithm.BlockSize = this.blockSize * 8;
+ }
+
+ // Set the key and IV for the algorithm
+ this.encryptionAlgorithm.Key = this.context.ClientWriteKey;
+ this.encryptionAlgorithm.IV = this.context.ClientWriteIV;
+
+ // Create encryption cipher
+ this.encryptionCipher = this.encryptionAlgorithm.CreateEncryptor();
+
+ // Create the HMAC algorithm for the client
+ this.clientHMAC = new M.HMAC(
+ this.HashAlgorithmName,
+ this.context.ClientWriteMAC);
+ }
+
+ private void createDecryptionCipher()
+ {
+ // Create and configure the symmetric algorithm
+ switch (this.cipherAlgorithmType)
+ {
+ case CipherAlgorithmType.Des:
+ this.decryptionAlgorithm = DES.Create();
+ break;
+
+ case CipherAlgorithmType.Rc2:
+ this.decryptionAlgorithm = RC2.Create();
+ break;
+
+ case CipherAlgorithmType.Rc4:
+ this.decryptionAlgorithm = new ARC4Managed();
+ break;
+
+ case CipherAlgorithmType.TripleDes:
+ this.decryptionAlgorithm = TripleDES.Create();
+ break;
+
+ case CipherAlgorithmType.Rijndael:
+ this.decryptionAlgorithm = Rijndael.Create();
+ break;
+ }
+
+ // If it's a block cipher
+ if (this.cipherMode == CipherMode.CBC)
+ {
+ // Configure encrypt algorithm
+ this.decryptionAlgorithm.Mode = this.cipherMode;
+ this.decryptionAlgorithm.Padding = PaddingMode.None;
+ this.decryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
+ this.decryptionAlgorithm.BlockSize = this.blockSize * 8;
+ }
+
+ // Set the key and IV for the algorithm
+ this.decryptionAlgorithm.Key = this.context.ServerWriteKey;
+ this.decryptionAlgorithm.IV = this.context.ServerWriteIV;
+
+ // Create decryption cipher
+ this.decryptionCipher = this.decryptionAlgorithm.CreateDecryptor();
+
+ // Create the HMAC algorithm for the server
+ this.serverHMAC = new M.HMAC(
+ this.HashAlgorithmName,
+ this.context.ServerWriteMAC);
+ }
+
+ #endregion
+ }
}
\ No newline at end of file