TlsCipherSuite now inhers from TlsAbstractCipherSuite class, fixed problem with paddi...
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / TlsAbstractCipherSuite.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 using System.Security.Cryptography.X509Certificates;
30
31 using Mono.Security;
32 using Mono.Security.Cryptography;
33
34 namespace Mono.Security.Protocol.Tls
35 {
36         internal abstract class TlsAbstractCipherSuite
37         {
38                 #region FIELDS
39
40                 protected short                                 code;
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;
58                         
59                 #endregion
60
61                 #region PROPERTIES
62
63                 public short Code
64                 {
65                         get { return code; }
66                 }
67
68                 public string Name
69                 {
70                         get { return name; }
71                 }
72
73                 public bool IsExportable
74                 {
75                         get { return isExportable; }
76                 }
77
78                 public CipherMode CipherMode
79                 {
80                         get { return cipherMode; }
81                 }
82
83                 public int HashSize
84                 {
85                         get { return (int)(hashName == "MD5" ? 16 : 20); }
86                 }
87
88                 public byte     KeyMaterialSize
89                 {
90                         get { return keyMaterialSize; }
91                 }
92
93                 public int KeyBlockSize
94                 {
95                         get 
96                         {
97                                 return keyMaterialSize*2 + HashSize*2 + ivSize*2;
98                         }
99                 }
100
101                 public byte     ExpandedKeyMaterialSize
102                 {
103                         get { return expandedKeyMaterialSize; }
104                 }
105
106                 public byte     EffectiveKeyBits
107                 {
108                         get { return EffectiveKeyBits; }
109                 }
110                 
111                 public byte IvSize
112                 {
113                         get { return ivSize; }
114                 }
115
116                 public byte     BlockSize
117                 {
118                         get { return blockSize; }
119                 }
120
121                 public string HashName
122                 {
123                         get { return hashName; }
124                 }
125
126                 public TlsSessionContext Context
127                 {
128                         get { return context; }
129                         set { context = value; }
130                 }
131
132                 public KeyedHashAlgorithm ClientHMAC
133                 {
134                         get { return clientHMAC; }
135                 }
136
137                 public KeyedHashAlgorithm ServerHMAC
138                 {
139                         get { return serverHMAC; }
140                 }
141
142                 #endregion
143
144                 #region CONSTRUCTORS
145                 
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)
147                 {
148                         this.code                                               = code;
149                         this.name                                               = name;
150                         this.algName                                    = algName;
151                         this.hashName                                   = hashName;
152                         this.isExportable                               = exportable;
153                         if (blockMode)
154                         {
155                                 this.cipherMode                         = CipherMode.CBC;
156                         }
157                         this.keyMaterialSize                    = keyMaterialSize;
158                         this.expandedKeyMaterialSize    = expandedKeyMaterialSize;
159                         this.effectiveKeyBits                   = effectiveKeyBits;
160                         this.ivSize                                             = ivSize;
161                         this.blockSize                                  = blockSize;
162                 }
163
164                 #endregion
165
166                 #region METHODS
167
168                 public RSACryptoServiceProvider CreateRSA(X509Certificate certificate)
169                 {
170                         RSAParameters rsaParams = new RSAParameters();
171
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))
177                         {
178                                 return null;
179                         }
180                         ASN1 exponent = pubkey [1];
181                         if (exponent.Tag != 0x02)
182                         {
183                                 return null;
184                         }
185
186                         rsaParams.Modulus       = getUnsignedBigInteger(modulus.Value);
187                         rsaParams.Exponent      = exponent.Value;
188                         
189                         return CreateRSA(rsaParams);
190                 }
191
192                 public RSACryptoServiceProvider CreateRSA(RSAParameters rsaParams)
193                 {                       
194                         // BUG: MS BCL 1.0 can't import a key which 
195                         // isn't the same size as the one present in
196                         // the container.
197                         int keySize = (rsaParams.Modulus.Length << 3);
198                         RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(keySize);
199                         rsa.ImportParameters(rsaParams);
200
201                         return rsa;
202                 }
203
204                 public void InitializeCipher()
205                 {
206                         createEncryptionCipher();
207                         createDecryptionCipher();
208                 }
209
210                 public void UpdateClientCipherIV(byte[] iv)
211                 {
212                         if (cipherMode == CipherMode.CBC)
213                         {
214                                 // Set the new IV
215                                 encryptionAlgorithm.IV  = iv;
216                         
217                                 // Create encryption cipher with the new IV
218                                 encryptionCipher = encryptionAlgorithm.CreateEncryptor();
219                         }
220                 }
221
222                 public void UpdateServerCipherIV(byte[] iv)
223                 {
224                         if (cipherMode == CipherMode.CBC)
225                         {
226                                 // Set the new IV
227                                 decryptionAlgorithm.IV  = iv;
228                         
229                                 // Create encryption cipher with the new IV
230                                 decryptionCipher = decryptionAlgorithm.CreateDecryptor();
231                         }
232                 }
233
234                 #endregion
235
236                 #region ABSTRACT_METHODS
237
238                 public abstract byte[] EncryptRecord(byte[] fragment, byte[] mac);
239
240                 public abstract void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC);
241
242                 public abstract void CreateMasterSecret(byte[] preMasterSecret);
243
244                 public abstract void CreateKeys();
245
246                 #endregion
247
248                 #region KEY_GENERATION_METODS
249
250                 public byte[] CreatePremasterSecret()
251                 {
252                         TlsStream stream = new TlsStream();
253
254                         // Write protocol version
255                         stream.Write((short)this.context.Protocol);
256
257                         // Generate random bytes
258                         stream.Write(this.context.GetSecureRandomBytes(46));
259
260                         byte[] preMasterSecret = stream.ToArray();
261
262                         stream.Reset();
263
264                         return preMasterSecret;
265                 }
266
267                 public byte[] PRF(byte[] secret, string label, byte[] data, int length)
268                 {
269                         MD5CryptoServiceProvider        md5     = new MD5CryptoServiceProvider();
270                         SHA1CryptoServiceProvider       sha1 = new SHA1CryptoServiceProvider();
271
272                         int secretLen = secret.Length / 2;
273
274                         // Seed
275                         TlsStream seedStream = new TlsStream();
276                         seedStream.Write(Encoding.ASCII.GetBytes(label));
277                         seedStream.Write(data);
278                         byte[] seed = seedStream.ToArray();
279                         seedStream.Reset();
280
281                         // Secret 1
282                         byte[] secret1 = new byte[secretLen];
283                         System.Array.Copy(secret, 0, secret1, 0, secretLen);
284
285                         // Secret2
286                         byte[] secret2 = new byte[secretLen];
287                         System.Array.Copy(secret, secretLen, secret2, 0, secretLen);
288
289                         // Secret 1 processing
290                         byte[] p_md5 = Expand("MD5", secret1, seed, length);
291
292                         // Secret 2 processing
293                         byte[] p_sha = Expand("SHA1", secret2, seed, length);
294
295                         // Perfor XOR of both results
296                         byte[] masterSecret = new byte[length];
297                         for (int i = 0; i < masterSecret.Length; i++)
298                         {
299                                 masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);
300                         }
301
302                         return masterSecret;
303                 }
304                 
305                 public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)
306                 {
307                         int hashLength  = hashName == "MD5" ? 16 : 20;
308                         int     iterations      = (int)(length / hashLength);
309                         if ((length % hashLength) > 0)
310                         {
311                                 iterations++;
312                         }
313                         
314                         HMAC            hmac    = new HMAC(hashName, secret);
315                         TlsStream       resMacs = new TlsStream();
316                         
317                         byte[][] hmacs = new byte[iterations + 1][];
318                         hmacs[0] = seed;
319                         for (int i = 1; i <= iterations; i++)
320                         {                               
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]);
325                                 hcseed.Write(seed);
326                                 hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);
327                                 resMacs.Write(hmac.Hash);
328                                 hcseed.Reset();
329                         }
330
331                         byte[] res = new byte[length];
332                         
333                         System.Array.Copy(resMacs.ToArray(), 0, res, 0, res.Length);
334
335                         resMacs.Reset();
336
337                         return res;
338                 }
339
340                 #endregion
341
342                 #region PRIVATE_METHODS
343
344                 // This code is from Mono.Security.X509Certificate class.
345                 private byte[] getUnsignedBigInteger(byte[] integer) 
346                 {
347                         if (integer[0] == 0x00) 
348                         {
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);
354
355                                 return uinteger;
356                         }
357                         else
358                         {
359                                 return integer;
360                         }
361                 }
362
363                 private void createEncryptionCipher()
364                 {
365                         // Create and configure the symmetric algorithm
366                         switch (this.algName)
367                         {
368                                 case "RC4":
369                                         encryptionAlgorithm = new ARC4Managed();
370                                         break;
371
372                                 default:
373                                         encryptionAlgorithm = SymmetricAlgorithm.Create(algName);
374                                         break;
375                         }
376
377                         // If it's a block cipher
378                         if (cipherMode == CipherMode.CBC)
379                         {
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;
385                         }
386
387                         // Set the key and IV for the algorithm
388                         encryptionAlgorithm.Key = context.ClientWriteKey;
389                         encryptionAlgorithm.IV  = context.ClientWriteIV;
390                         
391                         // Create encryption cipher
392                         encryptionCipher = encryptionAlgorithm.CreateEncryptor();
393
394                         // Create the HMAC algorithm for the client
395                         clientHMAC = new HMAC(hashName, context.ClientWriteMAC);
396                 }
397
398                 private void createDecryptionCipher()
399                 {
400                         // Create and configure the symmetric algorithm
401                         switch (this.algName)
402                         {
403                                 case "RC4":
404                                         decryptionAlgorithm = new ARC4Managed();
405                                         break;
406
407                                 default:
408                                         decryptionAlgorithm = SymmetricAlgorithm.Create(algName);
409                                         break;
410                         }
411
412                         // If it's a block cipher
413                         if (cipherMode == CipherMode.CBC)
414                         {
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;
420                         }
421
422                         // Set the key and IV for the algorithm
423                         decryptionAlgorithm.Key = context.ServerWriteKey;
424                         decryptionAlgorithm.IV  = context.ServerWriteIV;
425
426                         // Create decryption cipher                     
427                         decryptionCipher = decryptionAlgorithm.CreateDecryptor();
428
429                         // Create the HMAC algorithm for the server
430                         serverHMAC = new HMAC(hashName, context.ServerWriteMAC);
431                 }
432
433                 #endregion
434         }
435 }