--- /dev/null
+/* 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.
+ */
+
+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 abstract class TlsAbstractCipherSuite
+ {
+ #region FIELDS
+
+ protected short code;
+ protected string name;
+ protected string algName;
+ protected string hashName;
+ protected bool isExportable;
+ protected CipherMode cipherMode;
+ protected byte keyMaterialSize;
+ protected byte expandedKeyMaterialSize;
+ protected short effectiveKeyBits;
+ protected byte ivSize;
+ protected byte blockSize;
+ protected TlsSessionContext context;
+ protected SymmetricAlgorithm encryptionAlgorithm;
+ protected ICryptoTransform encryptionCipher;
+ protected SymmetricAlgorithm decryptionAlgorithm;
+ protected ICryptoTransform decryptionCipher;
+ protected KeyedHashAlgorithm clientHMAC;
+ protected KeyedHashAlgorithm serverHMAC;
+
+ #endregion
+
+ #region PROPERTIES
+
+ public short Code
+ {
+ get { return code; }
+ }
+
+ public string Name
+ {
+ get { return name; }
+ }
+
+ public bool IsExportable
+ {
+ get { return isExportable; }
+ }
+
+ public CipherMode CipherMode
+ {
+ get { return cipherMode; }
+ }
+
+ public int HashSize
+ {
+ get { return (int)(hashName == "MD5" ? 16 : 20); }
+ }
+
+ public byte KeyMaterialSize
+ {
+ get { return keyMaterialSize; }
+ }
+
+ public int KeyBlockSize
+ {
+ get
+ {
+ return keyMaterialSize*2 + HashSize*2 + ivSize*2;
+ }
+ }
+
+ public byte ExpandedKeyMaterialSize
+ {
+ get { return expandedKeyMaterialSize; }
+ }
+
+ public byte EffectiveKeyBits
+ {
+ get { return EffectiveKeyBits; }
+ }
+
+ public byte IvSize
+ {
+ get { return ivSize; }
+ }
+
+ public byte BlockSize
+ {
+ get { return blockSize; }
+ }
+
+ public string HashName
+ {
+ get { return hashName; }
+ }
+
+ public TlsSessionContext Context
+ {
+ get { return context; }
+ set { context = value; }
+ }
+
+ public KeyedHashAlgorithm ClientHMAC
+ {
+ get { return clientHMAC; }
+ }
+
+ public KeyedHashAlgorithm ServerHMAC
+ {
+ get { return serverHMAC; }
+ }
+
+ #endregion
+
+ #region CONSTRUCTORS
+
+ public TlsAbstractCipherSuite(short code, string name, string algName, string hashName, bool exportable, bool blockMode, byte keyMaterialSize, byte expandedKeyMaterialSize, short effectiveKeyBytes, byte ivSize, byte blockSize)
+ {
+ this.code = code;
+ this.name = name;
+ this.algName = algName;
+ this.hashName = hashName;
+ this.isExportable = exportable;
+ if (blockMode)
+ {
+ this.cipherMode = CipherMode.CBC;
+ }
+ this.keyMaterialSize = keyMaterialSize;
+ this.expandedKeyMaterialSize = expandedKeyMaterialSize;
+ this.effectiveKeyBits = effectiveKeyBits;
+ this.ivSize = ivSize;
+ this.blockSize = blockSize;
+ }
+
+ #endregion
+
+ #region METHODS
+
+ public RSACryptoServiceProvider CreateRSA(X509Certificate certificate)
+ {
+ RSAParameters rsaParams = new RSAParameters();
+
+ // for RSA m_publickey contains 2 ASN.1 integers
+ // the modulus and the public exponent
+ ASN1 pubkey = new ASN1(certificate.GetPublicKey());
+ ASN1 modulus = pubkey [0];
+ if ((modulus == null) || (modulus.Tag != 0x02))
+ {
+ return null;
+ }
+ ASN1 exponent = pubkey [1];
+ if (exponent.Tag != 0x02)
+ {
+ return null;
+ }
+
+ rsaParams.Modulus = getUnsignedBigInteger(modulus.Value);
+ rsaParams.Exponent = exponent.Value;
+
+ return CreateRSA(rsaParams);
+ }
+
+ public RSACryptoServiceProvider CreateRSA(RSAParameters rsaParams)
+ {
+ // BUG: MS BCL 1.0 can't import a key which
+ // isn't the same size as the one present in
+ // the container.
+ int keySize = (rsaParams.Modulus.Length << 3);
+ RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(keySize);
+ rsa.ImportParameters(rsaParams);
+
+ return rsa;
+ }
+
+ public void InitializeCipher()
+ {
+ createEncryptionCipher();
+ createDecryptionCipher();
+ }
+
+ public void UpdateClientCipherIV(byte[] iv)
+ {
+ if (cipherMode == CipherMode.CBC)
+ {
+ // Set the new IV
+ encryptionAlgorithm.IV = iv;
+
+ // Create encryption cipher with the new IV
+ encryptionCipher = encryptionAlgorithm.CreateEncryptor();
+ }
+ }
+
+ public void UpdateServerCipherIV(byte[] iv)
+ {
+ if (cipherMode == CipherMode.CBC)
+ {
+ // Set the new IV
+ decryptionAlgorithm.IV = iv;
+
+ // Create encryption cipher with the new IV
+ decryptionCipher = decryptionAlgorithm.CreateDecryptor();
+ }
+ }
+
+ #endregion
+
+ #region ABSTRACT_METHODS
+
+ public abstract byte[] EncryptRecord(byte[] fragment, byte[] mac);
+
+ public abstract void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC);
+
+ public abstract void CreateMasterSecret(byte[] preMasterSecret);
+
+ public abstract void CreateKeys();
+
+ #endregion
+
+ #region KEY_GENERATION_METODS
+
+ 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)
+ {
+ MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
+ SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
+
+ 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++;
+ }
+
+ HMAC hmac = new 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
+
+ // This code is from Mono.Security.X509Certificate class.
+ private byte[] getUnsignedBigInteger(byte[] integer)
+ {
+ if (integer[0] == 0x00)
+ {
+ // this first byte is added so we're sure it's an unsigned integer
+ // however we can't feed it into RSAParameters or DSAParameters
+ int length = integer.Length - 1;
+ byte[] uinteger = new byte[length];
+ Array.Copy(integer, 1, uinteger, 0, length);
+
+ return uinteger;
+ }
+ else
+ {
+ return integer;
+ }
+ }
+
+ private void createEncryptionCipher()
+ {
+ // Create and configure the symmetric algorithm
+ switch (this.algName)
+ {
+ case "RC4":
+ encryptionAlgorithm = new ARC4Managed();
+ break;
+
+ default:
+ encryptionAlgorithm = SymmetricAlgorithm.Create(algName);
+ break;
+ }
+
+ // If it's a block cipher
+ if (cipherMode == CipherMode.CBC)
+ {
+ // Configure encrypt algorithm
+ encryptionAlgorithm.Mode = this.cipherMode;
+ encryptionAlgorithm.Padding = PaddingMode.PKCS7;
+ encryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
+ encryptionAlgorithm.BlockSize = this.blockSize * 8;
+ }
+
+ // Set the key and IV for the algorithm
+ encryptionAlgorithm.Key = context.ClientWriteKey;
+ encryptionAlgorithm.IV = context.ClientWriteIV;
+
+ // Create encryption cipher
+ encryptionCipher = encryptionAlgorithm.CreateEncryptor();
+
+ // Create the HMAC algorithm for the client
+ clientHMAC = new HMAC(hashName, context.ClientWriteMAC);
+ }
+
+ private void createDecryptionCipher()
+ {
+ // Create and configure the symmetric algorithm
+ switch (this.algName)
+ {
+ case "RC4":
+ decryptionAlgorithm = new ARC4Managed();
+ break;
+
+ default:
+ decryptionAlgorithm = SymmetricAlgorithm.Create(algName);
+ break;
+ }
+
+ // If it's a block cipher
+ if (cipherMode == CipherMode.CBC)
+ {
+ // Configure encrypt algorithm
+ decryptionAlgorithm.Mode = this.cipherMode;
+ decryptionAlgorithm.Padding = PaddingMode.None;
+ decryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
+ decryptionAlgorithm.BlockSize = this.blockSize * 8;
+ }
+
+ // Set the key and IV for the algorithm
+ decryptionAlgorithm.Key = context.ServerWriteKey;
+ decryptionAlgorithm.IV = context.ServerWriteIV;
+
+ // Create decryption cipher
+ decryptionCipher = decryptionAlgorithm.CreateDecryptor();
+
+ // Create the HMAC algorithm for the server
+ serverHMAC = new HMAC(hashName, context.ServerWriteMAC);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file