Updated TLS/SSL implementation files with unix-like line endings
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / CipherSuite.cs
1 /* Transport Security Layer (TLS)
2  * Copyright (c) 2003 Carlos Guzmán Álvarez
3  * 
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:
11  * 
12  * The above copyright notice and this permission notice shall be included 
13  * in all copies or substantial portions of the Software.
14  * 
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.
23  */
24
25 using System;
26 using System.IO;
27 using System.Text;
28 using System.Security.Cryptography;
29
30 using Mono.Security;
31 using Mono.Security.Cryptography;
32 using Mono.Security.X509;
33
34 namespace Mono.Security.Protocol.Tls
35 {
36         internal abstract class CipherSuite
37         {
38                 #region FIELDS
39
40                 private short                           code;
41                 private string                          name;
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;
49                 private byte                            ivSize;
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;
58                         
59                 #endregion
60
61                 #region PROTECTED_PROPERTIES
62
63                 protected ICryptoTransform EncryptionCipher
64                 {
65                         get { return encryptionCipher; }
66                 }
67
68                 protected ICryptoTransform DecryptionCipher
69                 {
70                         get { return decryptionCipher; }
71                 }
72
73                 protected KeyedHashAlgorithm ClientHMAC
74                 {
75                         get { return clientHMAC; }
76                 }
77                 
78                 protected KeyedHashAlgorithm ServerHMAC
79                 {
80                         get { return serverHMAC; }
81                 }
82
83                 #endregion
84
85                 #region PROPERTIES
86
87                 public short Code
88                 {
89                         get { return code; }
90                 }
91
92                 public string Name
93                 {
94                         get { return name; }
95                 }
96
97                 public bool IsExportable
98                 {
99                         get { return isExportable; }
100                 }
101
102                 public CipherMode CipherMode
103                 {
104                         get { return cipherMode; }
105                 }
106
107                 public int HashSize
108                 {
109                         get { return (int)(hashName == "MD5" ? 16 : 20); }
110                 }
111
112                 public byte     KeyMaterialSize
113                 {
114                         get { return keyMaterialSize; }
115                 }
116
117                 public int KeyBlockSize
118                 {
119                         get 
120                         {
121                                 return keyMaterialSize*2 + HashSize*2 + ivSize*2;
122                         }
123                 }
124
125                 public byte     ExpandedKeyMaterialSize
126                 {
127                         get { return expandedKeyMaterialSize; }
128                 }
129
130                 public byte     EffectiveKeyBits
131                 {
132                         get { return EffectiveKeyBits; }
133                 }
134                 
135                 public byte IvSize
136                 {
137                         get { return ivSize; }
138                 }
139
140                 public byte     BlockSize
141                 {
142                         get { return blockSize; }
143                 }
144
145                 public string HashName
146                 {
147                         get { return hashName; }
148                 }
149
150                 public TlsSessionContext Context
151                 {
152                         get { return context; }
153                         set { context = value; }
154                 }
155
156                 #endregion
157
158                 #region CONSTRUCTORS
159                 
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)
161                 {
162                         this.code                                               = code;
163                         this.name                                               = name;
164                         this.algName                                    = algName;
165                         this.hashName                                   = hashName;
166                         this.isExportable                               = exportable;
167                         if (blockMode)
168                         {
169                                 this.cipherMode                         = CipherMode.CBC;
170                         }
171                         this.keyMaterialSize                    = keyMaterialSize;
172                         this.expandedKeyMaterialSize    = expandedKeyMaterialSize;
173                         this.effectiveKeyBits                   = effectiveKeyBits;
174                         this.ivSize                                             = ivSize;
175                         this.blockSize                                  = blockSize;
176                 }
177
178                 #endregion
179
180                 #region METHODS
181
182                 public void InitializeCipher()
183                 {
184                         createEncryptionCipher();
185                         createDecryptionCipher();
186                 }
187
188                 public RSA CreateRSA()
189                 {
190                         RSA rsa;
191                         if (this.Context.ServerSettings.ServerKeyExchange)
192                         {
193                                 rsa = new RSACryptoServiceProvider();
194                                 rsa.ImportParameters(this.Context.ServerSettings.RsaParameters);
195                         }
196                         else
197                         {
198                                 rsa = this.Context.ServerSettings.ServerCertificates[0].RSA;
199                         }
200         
201                         return rsa;
202                 }
203
204                 public RSACryptoServiceProvider CreateRSA(RSAParameters rsaParams)
205                 {                       
206                         // BUG: MS BCL 1.0 can't import a key which 
207                         // isn't the same size as the one present in
208                         // the container.
209                         int keySize = (rsaParams.Modulus.Length << 3);
210                         RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(keySize);
211                         rsa.ImportParameters(rsaParams);
212
213                         return rsa;
214                 }
215
216                 public void UpdateClientCipherIV(byte[] iv)
217                 {
218                         if (cipherMode == CipherMode.CBC)
219                         {
220                                 // Set the new IV
221                                 encryptionAlgorithm.IV  = iv;
222                         
223                                 // Create encryption cipher with the new IV
224                                 encryptionCipher = encryptionAlgorithm.CreateEncryptor();
225                         }
226                 }
227
228                 public void UpdateServerCipherIV(byte[] iv)
229                 {
230                         if (cipherMode == CipherMode.CBC)
231                         {
232                                 // Set the new IV
233                                 decryptionAlgorithm.IV  = iv;
234                         
235                                 // Create encryption cipher with the new IV
236                                 decryptionCipher = decryptionAlgorithm.CreateDecryptor();
237                         }
238                 }
239
240                 public byte[] EncryptRecord(byte[] fragment, byte[] mac)
241                 {
242                         // Encryption ( fragment + mac [+ padding + padding_length] )
243                         MemoryStream ms = new MemoryStream();
244                         CryptoStream cs = new CryptoStream(ms, this.EncryptionCipher, CryptoStreamMode.Write);
245
246                         cs.Write(fragment, 0, fragment.Length);
247                         cs.Write(mac, 0, mac.Length);
248                         if (this.CipherMode == CipherMode.CBC)
249                         {
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)
254                                 {
255                                         paddingLength = 0;
256                                 }
257
258                                 // Write padding length byte
259                                 for (int i = 0; i < (paddingLength + 1); i++)
260                                 {
261                                         cs.WriteByte((byte)paddingLength);
262                                 }
263                         }
264                         // cs.FlushFinalBlock();
265                         cs.Close();                     
266
267                         return ms.ToArray();
268                 }
269
270                 public void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC)
271                 {
272                         int     fragmentSize    = 0;
273                         int paddingLength       = 0;
274
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);
278
279                         // Calculate fragment size
280                         if (this.CipherMode == CipherMode.CBC)
281                         {
282                                 // Calculate padding_length
283                                 paddingLength = buffer[buffer.Length - 1];
284
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--)
287                                 {
288                                         if (buffer[i] != paddingLength)
289                                         {
290                                                 paddingLength = 0;
291                                                 break;
292                                         }
293                                 }
294                                 */
295
296                                 fragmentSize = (buffer.Length - (paddingLength + 1)) - HashSize;
297                         }
298                         else
299                         {
300                                 fragmentSize = buffer.Length - HashSize;
301                         }
302
303                         dcrFragment = new byte[fragmentSize];
304                         dcrMAC          = new byte[HashSize];
305
306                         Buffer.BlockCopy(buffer, 0, dcrFragment, 0, dcrFragment.Length);
307                         Buffer.BlockCopy(buffer, dcrFragment.Length, dcrMAC, 0, dcrMAC.Length);
308                 }
309
310                 #endregion
311
312                 #region ABSTRACT_METHODS
313
314                 public abstract byte[] ComputeClientRecordMAC(TlsContentType contentType, byte[] fragment);
315
316                 public abstract byte[] ComputeServerRecordMAC(TlsContentType contentType, byte[] fragment);
317
318                 public abstract void ComputeMasterSecret(byte[] preMasterSecret);
319
320                 public abstract void ComputeKeys();
321
322                 #endregion
323
324                 #region KEY_GENERATION_METODS
325
326                 public byte[] CreatePremasterSecret()
327                 {
328                         TlsStream stream = new TlsStream();
329
330                         // Write protocol version
331                         stream.Write((short)this.Context.Protocol);
332
333                         // Generate random bytes
334                         stream.Write(this.context.GetSecureRandomBytes(46));
335
336                         byte[] preMasterSecret = stream.ToArray();
337
338                         stream.Reset();
339
340                         return preMasterSecret;
341                 }
342
343                 public byte[] PRF(byte[] secret, string label, byte[] data, int length)
344                 {
345                         MD5CryptoServiceProvider        md5     = new MD5CryptoServiceProvider();
346                         SHA1CryptoServiceProvider       sha1 = new SHA1CryptoServiceProvider();
347
348                         int secretLen = secret.Length / 2;
349
350                         // Seed
351                         TlsStream seedStream = new TlsStream();
352                         seedStream.Write(Encoding.ASCII.GetBytes(label));
353                         seedStream.Write(data);
354                         byte[] seed = seedStream.ToArray();
355                         seedStream.Reset();
356
357                         // Secret 1
358                         byte[] secret1 = new byte[secretLen];
359                         System.Array.Copy(secret, 0, secret1, 0, secretLen);
360
361                         // Secret2
362                         byte[] secret2 = new byte[secretLen];
363                         System.Array.Copy(secret, secretLen, secret2, 0, secretLen);
364
365                         // Secret 1 processing
366                         byte[] p_md5 = Expand("MD5", secret1, seed, length);
367
368                         // Secret 2 processing
369                         byte[] p_sha = Expand("SHA1", secret2, seed, length);
370
371                         // Perfor XOR of both results
372                         byte[] masterSecret = new byte[length];
373                         for (int i = 0; i < masterSecret.Length; i++)
374                         {
375                                 masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);
376                         }
377
378                         return masterSecret;
379                 }
380                 
381                 public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)
382                 {
383                         int hashLength  = hashName == "MD5" ? 16 : 20;
384                         int     iterations      = (int)(length / hashLength);
385                         if ((length % hashLength) > 0)
386                         {
387                                 iterations++;
388                         }
389                         
390                         HMAC            hmac    = new HMAC(hashName, secret);
391                         TlsStream       resMacs = new TlsStream();
392                         
393                         byte[][] hmacs = new byte[iterations + 1][];
394                         hmacs[0] = seed;
395                         for (int i = 1; i <= iterations; i++)
396                         {                               
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]);
401                                 hcseed.Write(seed);
402                                 hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);
403                                 resMacs.Write(hmac.Hash);
404                                 hcseed.Reset();
405                         }
406
407                         byte[] res = new byte[length];
408                         
409                         System.Array.Copy(resMacs.ToArray(), 0, res, 0, res.Length);
410
411                         resMacs.Reset();
412
413                         return res;
414                 }
415
416                 #endregion
417
418                 #region PRIVATE_METHODS
419
420                 // This code is from Mono.Security.X509Certificate class.
421                 private byte[] getUnsignedBigInteger(byte[] integer) 
422                 {
423                         if (integer[0] == 0x00) 
424                         {
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);
430
431                                 return uinteger;
432                         }
433                         else
434                         {
435                                 return integer;
436                         }
437                 }
438
439                 private void createEncryptionCipher()
440                 {
441                         // Create and configure the symmetric algorithm
442                         switch (this.algName)
443                         {
444                                 case "RC4":
445                                         encryptionAlgorithm = new ARC4Managed();
446                                         break;
447
448                                 default:
449                                         encryptionAlgorithm = SymmetricAlgorithm.Create(algName);
450                                         break;
451                         }
452
453                         // If it's a block cipher
454                         if (cipherMode == CipherMode.CBC)
455                         {
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;
461                         }
462
463                         // Set the key and IV for the algorithm
464                         encryptionAlgorithm.Key = context.ClientWriteKey;
465                         encryptionAlgorithm.IV  = context.ClientWriteIV;
466                         
467                         // Create encryption cipher
468                         encryptionCipher = encryptionAlgorithm.CreateEncryptor();
469
470                         // Create the HMAC algorithm for the client
471                         clientHMAC = new HMAC(hashName, context.ClientWriteMAC);
472                 }
473
474                 private void createDecryptionCipher()
475                 {
476                         // Create and configure the symmetric algorithm
477                         switch (this.algName)
478                         {
479                                 case "RC4":
480                                         decryptionAlgorithm = new ARC4Managed();
481                                         break;
482
483                                 default:
484                                         decryptionAlgorithm = SymmetricAlgorithm.Create(algName);
485                                         break;
486                         }
487
488                         // If it's a block cipher
489                         if (cipherMode == CipherMode.CBC)
490                         {
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;
496                         }
497
498                         // Set the key and IV for the algorithm
499                         decryptionAlgorithm.Key = context.ServerWriteKey;
500                         decryptionAlgorithm.IV  = context.ServerWriteIV;
501
502                         // Create decryption cipher                     
503                         decryptionCipher = decryptionAlgorithm.CreateDecryptor();
504
505                         // Create the HMAC algorithm for the server
506                         serverHMAC = new HMAC(hashName, context.ServerWriteMAC);
507                 }
508
509                 #endregion
510         }
511 }