1 /* Transport Security Layer (TLS)
2 * Copyright (c) 2003-2004 Carlos Guzman Alvarez
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;
33 using M = Mono.Security.Cryptography;
35 namespace Mono.Security.Protocol.Tls
37 internal abstract class CipherSuite
43 private CipherAlgorithmType cipherAlgorithmType;
44 private HashAlgorithmType hashAlgorithmType;
45 private ExchangeAlgorithmType exchangeAlgorithmType;
46 private bool isExportable;
47 private CipherMode cipherMode;
48 private byte keyMaterialSize;
49 private int keyBlockSize;
50 private byte expandedKeyMaterialSize;
51 private short effectiveKeyBits;
53 private byte blockSize;
54 private TlsContext context;
55 private SymmetricAlgorithm encryptionAlgorithm;
56 private ICryptoTransform encryptionCipher;
57 private SymmetricAlgorithm decryptionAlgorithm;
58 private ICryptoTransform decryptionCipher;
59 private KeyedHashAlgorithm clientHMAC;
60 private KeyedHashAlgorithm serverHMAC;
64 #region Protected Properties
66 protected ICryptoTransform EncryptionCipher
68 get { return this.encryptionCipher; }
71 protected ICryptoTransform DecryptionCipher
73 get { return this.decryptionCipher; }
76 protected KeyedHashAlgorithm ClientHMAC
78 get { return this.clientHMAC; }
81 protected KeyedHashAlgorithm ServerHMAC
83 get { return this.serverHMAC; }
90 public CipherAlgorithmType CipherAlgorithmType
92 get { return this.cipherAlgorithmType; }
95 public string HashAlgorithmName
99 switch (this.hashAlgorithmType)
101 case HashAlgorithmType.Md5:
104 case HashAlgorithmType.Sha1:
113 public HashAlgorithmType HashAlgorithmType
115 get { return this.hashAlgorithmType; }
122 switch (this.hashAlgorithmType)
124 case HashAlgorithmType.Md5:
127 case HashAlgorithmType.Sha1:
136 public ExchangeAlgorithmType ExchangeAlgorithmType
138 get { return this.exchangeAlgorithmType; }
141 public CipherMode CipherMode
143 get { return this.cipherMode; }
148 get { return this.code; }
153 get { return this.name; }
156 public bool IsExportable
158 get { return this.isExportable; }
161 public byte KeyMaterialSize
163 get { return this.keyMaterialSize; }
166 public int KeyBlockSize
168 get { return this.keyBlockSize; }
171 public byte ExpandedKeyMaterialSize
173 get { return this.expandedKeyMaterialSize; }
176 public byte EffectiveKeyBits
178 get { return this.EffectiveKeyBits; }
183 get { return this.ivSize; }
186 public byte BlockSize
188 get { return this.blockSize; }
191 public TlsContext Context
193 get { return this.context; }
194 set { this.context = value; }
202 short code, string name, CipherAlgorithmType cipherAlgorithmType,
203 HashAlgorithmType hashAlgorithmType, ExchangeAlgorithmType exchangeAlgorithmType,
204 bool exportable, bool blockMode, byte keyMaterialSize,
205 byte expandedKeyMaterialSize, short effectiveKeyBytes,
206 byte ivSize, byte blockSize)
210 this.cipherAlgorithmType = cipherAlgorithmType;
211 this.hashAlgorithmType = hashAlgorithmType;
212 this.exchangeAlgorithmType = exchangeAlgorithmType;
213 this.isExportable = exportable;
216 this.cipherMode = CipherMode.CBC;
218 this.keyMaterialSize = keyMaterialSize;
219 this.expandedKeyMaterialSize= expandedKeyMaterialSize;
220 this.effectiveKeyBits = effectiveKeyBits;
221 this.ivSize = ivSize;
222 this.blockSize = blockSize;
223 this.keyBlockSize = this.keyMaterialSize*2 + this.HashSize*2 + this.ivSize*2;
230 public void InitializeCipher()
232 this.createEncryptionCipher();
233 this.createDecryptionCipher();
236 public RSA CertificateRSA()
238 RSA rsaCert = this.Context.ServerSettings.Certificates[0].RSA;
239 RSA rsa = new RSAManaged(rsaCert.KeySize);
241 rsa.ImportParameters(rsaCert.ExportParameters(false));
246 public void UpdateClientCipherIV(byte[] iv)
248 if (this.cipherMode == CipherMode.CBC)
251 this.encryptionAlgorithm.IV = iv;
253 // Create encryption cipher with the new IV
254 this.encryptionCipher = this.encryptionAlgorithm.CreateEncryptor();
258 public void UpdateServerCipherIV(byte[] iv)
260 if (this.cipherMode == CipherMode.CBC)
263 this.decryptionAlgorithm.IV = iv;
265 // Create encryption cipher with the new IV
266 this.decryptionCipher = this.decryptionAlgorithm.CreateDecryptor();
270 public byte[] EncryptRecord(byte[] fragment, byte[] mac)
272 // Encryption ( fragment + mac [+ padding + padding_length] )
273 MemoryStream ms = new MemoryStream();
274 CryptoStream cs = new CryptoStream(ms, this.EncryptionCipher, CryptoStreamMode.Write);
276 cs.Write(fragment, 0, fragment.Length);
277 cs.Write(mac, 0, mac.Length);
278 if (this.CipherMode == CipherMode.CBC)
280 // Calculate padding_length
281 byte fragmentLength = (byte)(fragment.Length + mac.Length + 1);
282 byte paddingLength = (byte)(this.blockSize - fragmentLength % this.blockSize);
283 if (paddingLength == this.blockSize)
288 // Write padding length byte
289 byte[] padding = new byte[(paddingLength + 1)];
290 for (int i = 0; i < (paddingLength + 1); i++)
292 padding[i] = paddingLength;
295 cs.Write(padding, 0, padding.Length);
297 cs.FlushFinalBlock();
303 public void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC)
305 int fragmentSize = 0;
306 int paddingLength = 0;
308 // Decrypt message fragment ( fragment + mac [+ padding + padding_length] )
309 byte[] buffer = new byte[fragment.Length];
310 this.DecryptionCipher.TransformBlock(fragment, 0, fragment.Length, buffer, 0);
312 // Calculate fragment size
313 if (this.CipherMode == CipherMode.CBC)
315 // Calculate padding_length
316 paddingLength = buffer[buffer.Length - 1];
317 fragmentSize = (buffer.Length - (paddingLength + 1)) - this.HashSize;
321 fragmentSize = buffer.Length - this.HashSize;
324 dcrFragment = new byte[fragmentSize];
325 dcrMAC = new byte[HashSize];
327 Buffer.BlockCopy(buffer, 0, dcrFragment, 0, dcrFragment.Length);
328 Buffer.BlockCopy(buffer, dcrFragment.Length, dcrMAC, 0, dcrMAC.Length);
333 #region Abstract Methods
335 public abstract byte[] ComputeClientRecordMAC(TlsContentType contentType, byte[] fragment);
337 public abstract byte[] ComputeServerRecordMAC(TlsContentType contentType, byte[] fragment);
339 public abstract void ComputeMasterSecret(byte[] preMasterSecret);
341 public abstract void ComputeKeys();
345 #region Key Generation Methods
347 public byte[] CreatePremasterSecret()
349 TlsStream stream = new TlsStream();
351 // Write protocol version
352 stream.Write((short)this.Context.Protocol);
354 // Generate random bytes
355 stream.Write(this.context.GetSecureRandomBytes(46));
357 byte[] preMasterSecret = stream.ToArray();
361 return preMasterSecret;
364 public byte[] PRF(byte[] secret, string label, byte[] data, int length)
366 HashAlgorithm md5 = MD5.Create();
367 HashAlgorithm sha1 = SHA1.Create();
369 int secretLen = secret.Length / 2;
372 TlsStream seedStream = new TlsStream();
373 seedStream.Write(Encoding.ASCII.GetBytes(label));
374 seedStream.Write(data);
375 byte[] seed = seedStream.ToArray();
379 byte[] secret1 = new byte[secretLen];
380 System.Array.Copy(secret, 0, secret1, 0, secretLen);
383 byte[] secret2 = new byte[secretLen];
384 System.Array.Copy(secret, secretLen, secret2, 0, secretLen);
386 // Secret 1 processing
387 byte[] p_md5 = Expand("MD5", secret1, seed, length);
389 // Secret 2 processing
390 byte[] p_sha = Expand("SHA1", secret2, seed, length);
392 // Perfor XOR of both results
393 byte[] masterSecret = new byte[length];
394 for (int i = 0; i < masterSecret.Length; i++)
396 masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);
402 public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)
404 int hashLength = hashName == "MD5" ? 16 : 20;
405 int iterations = (int)(length / hashLength);
406 if ((length % hashLength) > 0)
411 M.HMAC hmac = new M.HMAC(hashName, secret);
412 TlsStream resMacs = new TlsStream();
414 byte[][] hmacs = new byte[iterations + 1][];
416 for (int i = 1; i <= iterations; i++)
418 TlsStream hcseed = new TlsStream();
419 hmac.TransformFinalBlock(hmacs[i-1], 0, hmacs[i-1].Length);
420 hmacs[i] = hmac.Hash;
421 hcseed.Write(hmacs[i]);
423 hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);
424 resMacs.Write(hmac.Hash);
428 byte[] res = new byte[length];
430 System.Array.Copy(resMacs.ToArray(), 0, res, 0, res.Length);
439 #region Private Methods
441 private void createEncryptionCipher()
443 // Create and configure the symmetric algorithm
444 switch (this.cipherAlgorithmType)
446 case CipherAlgorithmType.Des:
447 this.encryptionAlgorithm = DES.Create();
450 case CipherAlgorithmType.Rc2:
451 this.encryptionAlgorithm = RC2.Create();
454 case CipherAlgorithmType.Rc4:
455 this.encryptionAlgorithm = new ARC4Managed();
458 case CipherAlgorithmType.TripleDes:
459 this.encryptionAlgorithm = TripleDES.Create();
462 case CipherAlgorithmType.Rijndael:
463 this.encryptionAlgorithm = Rijndael.Create();
467 // If it's a block cipher
468 if (this.cipherMode == CipherMode.CBC)
470 // Configure encrypt algorithm
471 this.encryptionAlgorithm.Mode = this.cipherMode;
472 this.encryptionAlgorithm.Padding = PaddingMode.None;
473 this.encryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
474 this.encryptionAlgorithm.BlockSize = this.blockSize * 8;
477 // Set the key and IV for the algorithm
478 this.encryptionAlgorithm.Key = this.context.ClientWriteKey;
479 this.encryptionAlgorithm.IV = this.context.ClientWriteIV;
481 // Create encryption cipher
482 this.encryptionCipher = this.encryptionAlgorithm.CreateEncryptor();
484 // Create the HMAC algorithm for the client
485 this.clientHMAC = new M.HMAC(
486 this.HashAlgorithmName,
487 this.context.ClientWriteMAC);
490 private void createDecryptionCipher()
492 // Create and configure the symmetric algorithm
493 switch (this.cipherAlgorithmType)
495 case CipherAlgorithmType.Des:
496 this.decryptionAlgorithm = DES.Create();
499 case CipherAlgorithmType.Rc2:
500 this.decryptionAlgorithm = RC2.Create();
503 case CipherAlgorithmType.Rc4:
504 this.decryptionAlgorithm = new ARC4Managed();
507 case CipherAlgorithmType.TripleDes:
508 this.decryptionAlgorithm = TripleDES.Create();
511 case CipherAlgorithmType.Rijndael:
512 this.decryptionAlgorithm = Rijndael.Create();
516 // If it's a block cipher
517 if (this.cipherMode == CipherMode.CBC)
519 // Configure encrypt algorithm
520 this.decryptionAlgorithm.Mode = this.cipherMode;
521 this.decryptionAlgorithm.Padding = PaddingMode.None;
522 this.decryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
523 this.decryptionAlgorithm.BlockSize = this.blockSize * 8;
526 // Set the key and IV for the algorithm
527 this.decryptionAlgorithm.Key = this.context.ServerWriteKey;
528 this.decryptionAlgorithm.IV = this.context.ServerWriteIV;
530 // Create decryption cipher
531 this.decryptionCipher = this.decryptionAlgorithm.CreateDecryptor();
533 // Create the HMAC algorithm for the server
534 this.serverHMAC = new M.HMAC(
535 this.HashAlgorithmName,
536 this.context.ServerWriteMAC);