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;
31 using Mono.Security.Cryptography;
32 using Mono.Security.X509;
34 namespace Mono.Security.Protocol.Tls
36 internal abstract class CipherSuite
42 private string algName;
43 private string hashName;
44 private bool isExportable;
45 private CipherMode cipherMode;
46 private byte keyMaterialSize;
47 private byte expandedKeyMaterialSize;
48 private short effectiveKeyBits;
50 private byte blockSize;
51 private TlsSessionContext context;
52 private SymmetricAlgorithm encryptionAlgorithm;
53 private ICryptoTransform encryptionCipher;
54 private SymmetricAlgorithm decryptionAlgorithm;
55 private ICryptoTransform decryptionCipher;
56 private KeyedHashAlgorithm clientHMAC;
57 private KeyedHashAlgorithm serverHMAC;
61 #region PROTECTED_PROPERTIES
63 protected ICryptoTransform EncryptionCipher
65 get { return encryptionCipher; }
68 protected ICryptoTransform DecryptionCipher
70 get { return decryptionCipher; }
73 protected KeyedHashAlgorithm ClientHMAC
75 get { return clientHMAC; }
78 protected KeyedHashAlgorithm ServerHMAC
80 get { return serverHMAC; }
97 public bool IsExportable
99 get { return isExportable; }
102 public CipherMode CipherMode
104 get { return cipherMode; }
109 get { return (int)(hashName == "MD5" ? 16 : 20); }
112 public byte KeyMaterialSize
114 get { return keyMaterialSize; }
117 public int KeyBlockSize
121 return keyMaterialSize*2 + HashSize*2 + ivSize*2;
125 public byte ExpandedKeyMaterialSize
127 get { return expandedKeyMaterialSize; }
130 public byte EffectiveKeyBits
132 get { return EffectiveKeyBits; }
137 get { return ivSize; }
140 public byte BlockSize
142 get { return blockSize; }
145 public string HashName
147 get { return hashName; }
150 public TlsSessionContext Context
152 get { return context; }
153 set { context = value; }
160 public CipherSuite(short code, string name, string algName, string hashName, bool exportable, bool blockMode, byte keyMaterialSize, byte expandedKeyMaterialSize, short effectiveKeyBytes, byte ivSize, byte blockSize)
164 this.algName = algName;
165 this.hashName = hashName;
166 this.isExportable = exportable;
169 this.cipherMode = CipherMode.CBC;
171 this.keyMaterialSize = keyMaterialSize;
172 this.expandedKeyMaterialSize = expandedKeyMaterialSize;
173 this.effectiveKeyBits = effectiveKeyBits;
174 this.ivSize = ivSize;
175 this.blockSize = blockSize;
182 public void InitializeCipher()
184 createEncryptionCipher();
185 createDecryptionCipher();
188 public RSA CreateRSA()
191 if (this.Context.ServerSettings.ServerKeyExchange)
193 rsa = new RSACryptoServiceProvider();
194 rsa.ImportParameters(this.Context.ServerSettings.RsaParameters);
198 rsa = this.Context.ServerSettings.ServerCertificates[0].RSA;
204 public RSACryptoServiceProvider CreateRSA(RSAParameters rsaParams)
206 // BUG: MS BCL 1.0 can't import a key which
207 // isn't the same size as the one present in
209 int keySize = (rsaParams.Modulus.Length << 3);
210 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(keySize);
211 rsa.ImportParameters(rsaParams);
216 public void UpdateClientCipherIV(byte[] iv)
218 if (cipherMode == CipherMode.CBC)
221 encryptionAlgorithm.IV = iv;
223 // Create encryption cipher with the new IV
224 encryptionCipher = encryptionAlgorithm.CreateEncryptor();
228 public void UpdateServerCipherIV(byte[] iv)
230 if (cipherMode == CipherMode.CBC)
233 decryptionAlgorithm.IV = iv;
235 // Create encryption cipher with the new IV
236 decryptionCipher = decryptionAlgorithm.CreateDecryptor();
240 public byte[] EncryptRecord(byte[] fragment, byte[] mac)
242 // Encryption ( fragment + mac [+ padding + padding_length] )
243 MemoryStream ms = new MemoryStream();
244 CryptoStream cs = new CryptoStream(ms, this.EncryptionCipher, CryptoStreamMode.Write);
246 cs.Write(fragment, 0, fragment.Length);
247 cs.Write(mac, 0, mac.Length);
248 if (this.CipherMode == CipherMode.CBC)
250 // Calculate padding_length
251 int fragmentLength = fragment.Length + mac.Length + 1;
252 int paddingLength = this.blockSize - fragmentLength % this.blockSize;
253 if (paddingLength == this.blockSize)
258 // Write padding length byte
259 for (int i = 0; i < (paddingLength + 1); i++)
261 cs.WriteByte((byte)paddingLength);
264 // cs.FlushFinalBlock();
270 public void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC)
272 int fragmentSize = 0;
273 int paddingLength = 0;
275 // Decrypt message fragment ( fragment + mac [+ padding + padding_length] )
276 byte[] buffer = new byte[fragment.Length];
277 this.DecryptionCipher.TransformBlock(fragment, 0, fragment.Length, buffer, 0);
279 // Calculate fragment size
280 if (this.CipherMode == CipherMode.CBC)
282 // Calculate padding_length
283 paddingLength = buffer[buffer.Length - 1];
285 /* Review this that is valid way for TLS1 but not for SSL3
286 for (int i = (buffer.Length - 1); i > (buffer.Length - (paddingLength + 1)); i--)
288 if (buffer[i] != paddingLength)
296 fragmentSize = (buffer.Length - (paddingLength + 1)) - HashSize;
300 fragmentSize = buffer.Length - HashSize;
303 dcrFragment = new byte[fragmentSize];
304 dcrMAC = new byte[HashSize];
306 Buffer.BlockCopy(buffer, 0, dcrFragment, 0, dcrFragment.Length);
307 Buffer.BlockCopy(buffer, dcrFragment.Length, dcrMAC, 0, dcrMAC.Length);
312 #region ABSTRACT_METHODS
314 public abstract byte[] ComputeClientRecordMAC(TlsContentType contentType, byte[] fragment);
316 public abstract byte[] ComputeServerRecordMAC(TlsContentType contentType, byte[] fragment);
318 public abstract void ComputeMasterSecret(byte[] preMasterSecret);
320 public abstract void ComputeKeys();
324 #region KEY_GENERATION_METODS
326 public byte[] CreatePremasterSecret()
328 TlsStream stream = new TlsStream();
330 // Write protocol version
331 stream.Write((short)this.Context.Protocol);
333 // Generate random bytes
334 stream.Write(this.context.GetSecureRandomBytes(46));
336 byte[] preMasterSecret = stream.ToArray();
340 return preMasterSecret;
343 public byte[] PRF(byte[] secret, string label, byte[] data, int length)
345 MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
346 SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
348 int secretLen = secret.Length / 2;
351 TlsStream seedStream = new TlsStream();
352 seedStream.Write(Encoding.ASCII.GetBytes(label));
353 seedStream.Write(data);
354 byte[] seed = seedStream.ToArray();
358 byte[] secret1 = new byte[secretLen];
359 System.Array.Copy(secret, 0, secret1, 0, secretLen);
362 byte[] secret2 = new byte[secretLen];
363 System.Array.Copy(secret, secretLen, secret2, 0, secretLen);
365 // Secret 1 processing
366 byte[] p_md5 = Expand("MD5", secret1, seed, length);
368 // Secret 2 processing
369 byte[] p_sha = Expand("SHA1", secret2, seed, length);
371 // Perfor XOR of both results
372 byte[] masterSecret = new byte[length];
373 for (int i = 0; i < masterSecret.Length; i++)
375 masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);
381 public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)
383 int hashLength = hashName == "MD5" ? 16 : 20;
384 int iterations = (int)(length / hashLength);
385 if ((length % hashLength) > 0)
390 HMAC hmac = new HMAC(hashName, secret);
391 TlsStream resMacs = new TlsStream();
393 byte[][] hmacs = new byte[iterations + 1][];
395 for (int i = 1; i <= iterations; i++)
397 TlsStream hcseed = new TlsStream();
398 hmac.TransformFinalBlock(hmacs[i-1], 0, hmacs[i-1].Length);
399 hmacs[i] = hmac.Hash;
400 hcseed.Write(hmacs[i]);
402 hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);
403 resMacs.Write(hmac.Hash);
407 byte[] res = new byte[length];
409 System.Array.Copy(resMacs.ToArray(), 0, res, 0, res.Length);
418 #region PRIVATE_METHODS
420 // This code is from Mono.Security.X509Certificate class.
421 private byte[] getUnsignedBigInteger(byte[] integer)
423 if (integer[0] == 0x00)
425 // this first byte is added so we're sure it's an unsigned integer
426 // however we can't feed it into RSAParameters or DSAParameters
427 int length = integer.Length - 1;
428 byte[] uinteger = new byte[length];
429 Array.Copy(integer, 1, uinteger, 0, length);
439 private void createEncryptionCipher()
441 // Create and configure the symmetric algorithm
442 switch (this.algName)
445 encryptionAlgorithm = new ARC4Managed();
449 encryptionAlgorithm = SymmetricAlgorithm.Create(algName);
453 // If it's a block cipher
454 if (cipherMode == CipherMode.CBC)
456 // Configure encrypt algorithm
457 encryptionAlgorithm.Mode = this.cipherMode;
458 encryptionAlgorithm.Padding = PaddingMode.None;
459 encryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
460 encryptionAlgorithm.BlockSize = this.blockSize * 8;
463 // Set the key and IV for the algorithm
464 encryptionAlgorithm.Key = context.ClientWriteKey;
465 encryptionAlgorithm.IV = context.ClientWriteIV;
467 // Create encryption cipher
468 encryptionCipher = encryptionAlgorithm.CreateEncryptor();
470 // Create the HMAC algorithm for the client
471 clientHMAC = new HMAC(hashName, context.ClientWriteMAC);
474 private void createDecryptionCipher()
476 // Create and configure the symmetric algorithm
477 switch (this.algName)
480 decryptionAlgorithm = new ARC4Managed();
484 decryptionAlgorithm = SymmetricAlgorithm.Create(algName);
488 // If it's a block cipher
489 if (cipherMode == CipherMode.CBC)
491 // Configure encrypt algorithm
492 decryptionAlgorithm.Mode = this.cipherMode;
493 decryptionAlgorithm.Padding = PaddingMode.None;
494 decryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
495 decryptionAlgorithm.BlockSize = this.blockSize * 8;
498 // Set the key and IV for the algorithm
499 decryptionAlgorithm.Key = context.ServerWriteKey;
500 decryptionAlgorithm.IV = context.ServerWriteIV;
502 // Create decryption cipher
503 decryptionCipher = decryptionAlgorithm.CreateDecryptor();
505 // Create the HMAC algorithm for the server
506 serverHMAC = new HMAC(hashName, context.ServerWriteMAC);