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 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 RSACryptoServiceProvider CreateRSA(X509Certificate certificate)
190 RSAParameters rsaParams = new RSAParameters();
192 // for RSA m_publickey contains 2 ASN.1 integers
193 // the modulus and the public exponent
194 ASN1 pubkey = new ASN1(certificate.GetPublicKey());
195 ASN1 modulus = pubkey [0];
196 if ((modulus == null) || (modulus.Tag != 0x02))
200 ASN1 exponent = pubkey [1];
201 if (exponent.Tag != 0x02)
206 rsaParams.Modulus = getUnsignedBigInteger(modulus.Value);
207 rsaParams.Exponent = exponent.Value;
209 return CreateRSA(rsaParams);
212 public RSACryptoServiceProvider CreateRSA(RSAParameters rsaParams)
214 // BUG: MS BCL 1.0 can't import a key which
215 // isn't the same size as the one present in
217 int keySize = (rsaParams.Modulus.Length << 3);
218 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(keySize);
219 rsa.ImportParameters(rsaParams);
224 public void UpdateClientCipherIV(byte[] iv)
226 if (cipherMode == CipherMode.CBC)
229 encryptionAlgorithm.IV = iv;
231 // Create encryption cipher with the new IV
232 encryptionCipher = encryptionAlgorithm.CreateEncryptor();
236 public void UpdateServerCipherIV(byte[] iv)
238 if (cipherMode == CipherMode.CBC)
241 decryptionAlgorithm.IV = iv;
243 // Create encryption cipher with the new IV
244 decryptionCipher = decryptionAlgorithm.CreateDecryptor();
248 public byte[] EncryptRecord(byte[] fragment, byte[] mac)
250 // Encryption ( fragment + mac [+ padding + padding_length] )
251 MemoryStream ms = new MemoryStream();
252 CryptoStream cs = new CryptoStream(ms, this.EncryptionCipher, CryptoStreamMode.Write);
254 cs.Write(fragment, 0, fragment.Length);
255 cs.Write(mac, 0, mac.Length);
256 if (this.CipherMode == CipherMode.CBC)
258 // Calculate padding_length
259 int fragmentLength = fragment.Length + mac.Length + 1;
260 int paddingLength = (((fragmentLength/this.BlockSize)*this.BlockSize) + this.BlockSize) - fragmentLength;
262 // Write padding length byte
263 cs.WriteByte((byte)paddingLength);
265 //cs.FlushFinalBlock();
271 public void DecryptRecord(byte[] fragment, ref byte[] dcrFragment, ref byte[] dcrMAC)
273 int fragmentSize = 0;
274 int paddingLength = 0;
276 // Decrypt message fragment ( fragment + mac [+ padding + padding_length] )
277 byte[] buffer = new byte[fragment.Length];
278 this.DecryptionCipher.TransformBlock(fragment, 0, fragment.Length, buffer, 0);
280 // Calculate fragment size
281 if (this.CipherMode == CipherMode.CBC)
283 // Calculate padding_length
284 paddingLength = buffer[buffer.Length - 1];
285 for (int i = (buffer.Length - 1); i > (buffer.Length - (paddingLength + 1)); i--)
287 if (buffer[i] != paddingLength)
294 fragmentSize = (buffer.Length - (paddingLength + 1)) - HashSize;
298 fragmentSize = buffer.Length - HashSize;
301 dcrFragment = new byte[fragmentSize];
302 dcrMAC = new byte[HashSize];
304 Buffer.BlockCopy(buffer, 0, dcrFragment, 0, dcrFragment.Length);
305 Buffer.BlockCopy(buffer, dcrFragment.Length, dcrMAC, 0, dcrMAC.Length);
310 #region ABSTRACT_METHODS
312 public abstract byte[] ComputeClientRecordMAC(TlsContentType contentType, byte[] fragment);
314 public abstract byte[] ComputeServerRecordMAC(TlsContentType contentType, byte[] fragment);
316 public abstract void ComputeMasterSecret(byte[] preMasterSecret);
318 public abstract void ComputeKeys();
322 #region KEY_GENERATION_METODS
324 public byte[] CreatePremasterSecret()
326 TlsStream stream = new TlsStream();
328 // Write protocol version
329 stream.Write((short)this.Context.Protocol);
331 // Generate random bytes
332 stream.Write(this.context.GetSecureRandomBytes(46));
334 byte[] preMasterSecret = stream.ToArray();
338 return preMasterSecret;
341 public byte[] PRF(byte[] secret, string label, byte[] data, int length)
343 MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
344 SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
346 int secretLen = secret.Length / 2;
349 TlsStream seedStream = new TlsStream();
350 seedStream.Write(Encoding.ASCII.GetBytes(label));
351 seedStream.Write(data);
352 byte[] seed = seedStream.ToArray();
356 byte[] secret1 = new byte[secretLen];
357 System.Array.Copy(secret, 0, secret1, 0, secretLen);
360 byte[] secret2 = new byte[secretLen];
361 System.Array.Copy(secret, secretLen, secret2, 0, secretLen);
363 // Secret 1 processing
364 byte[] p_md5 = Expand("MD5", secret1, seed, length);
366 // Secret 2 processing
367 byte[] p_sha = Expand("SHA1", secret2, seed, length);
369 // Perfor XOR of both results
370 byte[] masterSecret = new byte[length];
371 for (int i = 0; i < masterSecret.Length; i++)
373 masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);
379 public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)
381 int hashLength = hashName == "MD5" ? 16 : 20;
382 int iterations = (int)(length / hashLength);
383 if ((length % hashLength) > 0)
388 HMAC hmac = new HMAC(hashName, secret);
389 TlsStream resMacs = new TlsStream();
391 byte[][] hmacs = new byte[iterations + 1][];
393 for (int i = 1; i <= iterations; i++)
395 TlsStream hcseed = new TlsStream();
396 hmac.TransformFinalBlock(hmacs[i-1], 0, hmacs[i-1].Length);
397 hmacs[i] = hmac.Hash;
398 hcseed.Write(hmacs[i]);
400 hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);
401 resMacs.Write(hmac.Hash);
405 byte[] res = new byte[length];
407 System.Array.Copy(resMacs.ToArray(), 0, res, 0, res.Length);
416 #region PRIVATE_METHODS
418 // This code is from Mono.Security.X509Certificate class.
419 private byte[] getUnsignedBigInteger(byte[] integer)
421 if (integer[0] == 0x00)
423 // this first byte is added so we're sure it's an unsigned integer
424 // however we can't feed it into RSAParameters or DSAParameters
425 int length = integer.Length - 1;
426 byte[] uinteger = new byte[length];
427 Array.Copy(integer, 1, uinteger, 0, length);
437 private void createEncryptionCipher()
439 // Create and configure the symmetric algorithm
440 switch (this.algName)
443 encryptionAlgorithm = new ARC4Managed();
447 encryptionAlgorithm = SymmetricAlgorithm.Create(algName);
451 // If it's a block cipher
452 if (cipherMode == CipherMode.CBC)
454 // Configure encrypt algorithm
455 encryptionAlgorithm.Mode = this.cipherMode;
456 encryptionAlgorithm.Padding = PaddingMode.PKCS7;
457 encryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
458 encryptionAlgorithm.BlockSize = this.blockSize * 8;
461 // Set the key and IV for the algorithm
462 encryptionAlgorithm.Key = context.ClientWriteKey;
463 encryptionAlgorithm.IV = context.ClientWriteIV;
465 // Create encryption cipher
466 encryptionCipher = encryptionAlgorithm.CreateEncryptor();
468 // Create the HMAC algorithm for the client
469 clientHMAC = new HMAC(hashName, context.ClientWriteMAC);
472 private void createDecryptionCipher()
474 // Create and configure the symmetric algorithm
475 switch (this.algName)
478 decryptionAlgorithm = new ARC4Managed();
482 decryptionAlgorithm = SymmetricAlgorithm.Create(algName);
486 // If it's a block cipher
487 if (cipherMode == CipherMode.CBC)
489 // Configure encrypt algorithm
490 decryptionAlgorithm.Mode = this.cipherMode;
491 decryptionAlgorithm.Padding = PaddingMode.None;
492 decryptionAlgorithm.KeySize = this.keyMaterialSize * 8;
493 decryptionAlgorithm.BlockSize = this.blockSize * 8;
496 // Set the key and IV for the algorithm
497 decryptionAlgorithm.Key = context.ServerWriteKey;
498 decryptionAlgorithm.IV = context.ServerWriteIV;
500 // Create decryption cipher
501 decryptionCipher = decryptionAlgorithm.CreateDecryptor();
503 // Create the HMAC algorithm for the server
504 serverHMAC = new HMAC(hashName, context.ServerWriteMAC);