1 /* Transport Security Layer (TLS)
2 * Copyright (c) 2003 Carlos Guzmán Álvarez
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
28 using System.Security.Cryptography;
29 using System.Security.Cryptography.X509Certificates;
32 using Mono.Security.Cryptography;
34 namespace Mono.Security.Protocol.Tls
36 internal abstract class TlsAbstractCipherSuite
41 protected string name;
42 protected string algName;
43 protected string hashName;
44 protected bool isExportable;
45 protected CipherMode cipherMode;
46 protected byte keyMaterialSize;
47 protected byte expandedKeyMaterialSize;
48 protected short effectiveKeyBits;
49 protected byte ivSize;
50 protected byte blockSize;
51 protected TlsSessionContext context;
52 protected SymmetricAlgorithm encryptionAlgorithm;
53 protected ICryptoTransform encryptionCipher;
54 protected SymmetricAlgorithm decryptionAlgorithm;
55 protected ICryptoTransform decryptionCipher;
56 protected KeyedHashAlgorithm clientHMAC;
57 protected KeyedHashAlgorithm serverHMAC;
73 public bool IsExportable
75 get { return isExportable; }
78 public CipherMode CipherMode
80 get { return cipherMode; }
85 get { return (int)(hashName == "MD5" ? 16 : 20); }
88 public byte KeyMaterialSize
90 get { return keyMaterialSize; }
93 public int KeyBlockSize
97 return keyMaterialSize*2 + HashSize*2 + ivSize*2;
101 public byte ExpandedKeyMaterialSize
103 get { return expandedKeyMaterialSize; }
106 public byte EffectiveKeyBits
108 get { return EffectiveKeyBits; }
113 get { return ivSize; }
116 public byte BlockSize
118 get { return blockSize; }
121 public string HashName
123 get { return hashName; }
126 public TlsSessionContext Context
128 get { return context; }
129 set { context = value; }
132 public KeyedHashAlgorithm ClientHMAC
134 get { return clientHMAC; }
137 public KeyedHashAlgorithm ServerHMAC
139 get { return serverHMAC; }
146 public TlsAbstractCipherSuite(short code, string name, string algName, string hashName, bool exportable, bool blockMode, byte keyMaterialSize, byte expandedKeyMaterialSize, short effectiveKeyBytes, byte ivSize, byte blockSize)
150 this.algName = algName;
151 this.hashName = hashName;
152 this.isExportable = exportable;
155 this.cipherMode = CipherMode.CBC;
157 this.keyMaterialSize = keyMaterialSize;
158 this.expandedKeyMaterialSize = expandedKeyMaterialSize;
159 this.effectiveKeyBits = effectiveKeyBits;
160 this.ivSize = ivSize;
161 this.blockSize = blockSize;
168 public RSACryptoServiceProvider CreateRSA(X509Certificate certificate)
170 RSAParameters rsaParams = new RSAParameters();
172 // for RSA m_publickey contains 2 ASN.1 integers
173 // the modulus and the public exponent
174 ASN1 pubkey = new ASN1(certificate.GetPublicKey());
175 ASN1 modulus = pubkey [0];
176 if ((modulus == null) || (modulus.Tag != 0x02))
180 ASN1 exponent = pubkey [1];
181 if (exponent.Tag != 0x02)
186 rsaParams.Modulus = getUnsignedBigInteger(modulus.Value);
187 rsaParams.Exponent = exponent.Value;
189 return CreateRSA(rsaParams);
192 public RSACryptoServiceProvider CreateRSA(RSAParameters rsaParams)
194 // BUG: MS BCL 1.0 can't import a key which
195 // isn't the same size as the one present in
197 int keySize = (rsaParams.Modulus.Length << 3);
198 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(keySize);
199 rsa.ImportParameters(rsaParams);
204 public void InitializeCipher()
206 createEncryptionCipher();
207 createDecryptionCipher();
210 public void UpdateClientCipherIV(byte[] iv)
212 if (cipherMode == CipherMode.CBC)
215 encryptionAlgorithm.IV = iv;
217 // Create encryption cipher with the new IV
218 encryptionCipher = encryptionAlgorithm.CreateEncryptor();
222 public void UpdateServerCipherIV(byte[] iv)
224 if (cipherMode == CipherMode.CBC)
227 decryptionAlgorithm.IV = iv;
229 // Create encryption cipher with the new IV
230 decryptionCipher = decryptionAlgorithm.CreateDecryptor();
236 #region ABSTRACT_METHODS
238 public abstract byte[] EncryptRecord(byte[] fragment, byte[] mac);
240 public abstract void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC);
242 public abstract void CreateMasterSecret(byte[] preMasterSecret);
244 public abstract void CreateKeys();
248 #region KEY_GENERATION_METODS
250 public byte[] CreatePremasterSecret()
252 TlsStream stream = new TlsStream();
254 // Write protocol version
255 stream.Write((short)this.context.Protocol);
257 // Generate random bytes
258 stream.Write(this.context.GetSecureRandomBytes(46));
260 byte[] preMasterSecret = stream.ToArray();
264 return preMasterSecret;
267 public byte[] PRF(byte[] secret, string label, byte[] data, int length)
269 MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
270 SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
272 int secretLen = secret.Length / 2;
275 TlsStream seedStream = new TlsStream();
276 seedStream.Write(Encoding.ASCII.GetBytes(label));
277 seedStream.Write(data);
278 byte[] seed = seedStream.ToArray();
282 byte[] secret1 = new byte[secretLen];
283 System.Array.Copy(secret, 0, secret1, 0, secretLen);
286 byte[] secret2 = new byte[secretLen];
287 System.Array.Copy(secret, secretLen, secret2, 0, secretLen);
289 // Secret 1 processing
290 byte[] p_md5 = Expand("MD5", secret1, seed, length);
292 // Secret 2 processing
293 byte[] p_sha = Expand("SHA1", secret2, seed, length);
295 // Perfor XOR of both results
296 byte[] masterSecret = new byte[length];
297 for (int i = 0; i < masterSecret.Length; i++)
299 masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);
305 public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)
307 int hashLength = hashName == "MD5" ? 16 : 20;
308 int iterations = (int)(length / hashLength);
309 if ((length % hashLength) > 0)
314 HMAC hmac = new HMAC(hashName, secret);
315 TlsStream resMacs = new TlsStream();
317 byte[][] hmacs = new byte[iterations + 1][];
319 for (int i = 1; i <= iterations; i++)
321 TlsStream hcseed = new TlsStream();
322 hmac.TransformFinalBlock(hmacs[i-1], 0, hmacs[i-1].Length);
323 hmacs[i] = hmac.Hash;
324 hcseed.Write(hmacs[i]);
326 hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);
327 resMacs.Write(hmac.Hash);
331 byte[] res = new byte[length];
333 System.Array.Copy(resMacs.ToArray(), 0, res, 0, res.Length);
342 #region PRIVATE_METHODS
344 // This code is from Mono.Security.X509Certificate class.
345 private byte[] getUnsignedBigInteger(byte[] integer)
347 if (integer[0] == 0x00)
349 // this first byte is added so we're sure it's an unsigned integer
350 // however we can't feed it into RSAParameters or DSAParameters
351 int length = integer.Length - 1;
352 byte[] uinteger = new byte[length];
353 Array.Copy(integer, 1, uinteger, 0, length);
363 private void createEncryptionCipher()
365 // Create and configure the symmetric algorithm
366 switch (this.algName)
369 encryptionAlgorithm = new ARC4Managed();
373 encryptionAlgorithm = SymmetricAlgorithm.Create(algName);
377 // If it's a block cipher
378 if (cipherMode == CipherMode.CBC)
380 // Configure encrypt algorithm
381 encryptionAlgorithm.Mode = this.cipherMode;
382 encryptionAlgorithm.Padding = PaddingMode.PKCS7;
383 encryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
384 encryptionAlgorithm.BlockSize = this.blockSize * 8;
387 // Set the key and IV for the algorithm
388 encryptionAlgorithm.Key = context.ClientWriteKey;
389 encryptionAlgorithm.IV = context.ClientWriteIV;
391 // Create encryption cipher
392 encryptionCipher = encryptionAlgorithm.CreateEncryptor();
394 // Create the HMAC algorithm for the client
395 clientHMAC = new HMAC(hashName, context.ClientWriteMAC);
398 private void createDecryptionCipher()
400 // Create and configure the symmetric algorithm
401 switch (this.algName)
404 decryptionAlgorithm = new ARC4Managed();
408 decryptionAlgorithm = SymmetricAlgorithm.Create(algName);
412 // If it's a block cipher
413 if (cipherMode == CipherMode.CBC)
415 // Configure encrypt algorithm
416 decryptionAlgorithm.Mode = this.cipherMode;
417 decryptionAlgorithm.Padding = PaddingMode.None;
418 decryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
419 decryptionAlgorithm.BlockSize = this.blockSize * 8;
422 // Set the key and IV for the algorithm
423 decryptionAlgorithm.Key = context.ServerWriteKey;
424 decryptionAlgorithm.IV = context.ServerWriteIV;
426 // Create decryption cipher
427 decryptionCipher = decryptionAlgorithm.CreateDecryptor();
429 // Create the HMAC algorithm for the server
430 serverHMAC = new HMAC(hashName, context.ServerWriteMAC);