Added new abstract base class for cipher suite implementations
authorCarlos Guzmán Álvarez <carlos@mono-cvs.ximian.com>
Mon, 20 Oct 2003 21:07:53 +0000 (21:07 -0000)
committerCarlos Guzmán Álvarez <carlos@mono-cvs.ximian.com>
Mon, 20 Oct 2003 21:07:53 +0000 (21:07 -0000)
svn path=/trunk/mcs/; revision=19220

mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsAbstractCipherSuite.cs [new file with mode: 0755]

diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsAbstractCipherSuite.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsAbstractCipherSuite.cs
new file mode 100755 (executable)
index 0000000..c7e1bf4
--- /dev/null
@@ -0,0 +1,435 @@
+/* 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