1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System
.Security
.Cryptography
;
31 using Mono
.Security
.Cryptography
;
32 using M
= Mono
.Security
.Cryptography
;
34 namespace Mono
.Security
.Protocol
.Tls
36 internal abstract class CipherSuite
40 public static byte[] EmptyArray
= new byte[0];
48 private CipherAlgorithmType cipherAlgorithmType
;
49 private HashAlgorithmType hashAlgorithmType
;
50 private ExchangeAlgorithmType exchangeAlgorithmType
;
51 private bool isExportable
;
52 private CipherMode cipherMode
;
53 private byte keyMaterialSize
;
54 private int keyBlockSize
;
55 private byte expandedKeyMaterialSize
;
56 private short effectiveKeyBits
;
58 private byte blockSize
;
59 private Context context
;
60 private SymmetricAlgorithm encryptionAlgorithm
;
61 private ICryptoTransform encryptionCipher
;
62 private SymmetricAlgorithm decryptionAlgorithm
;
63 private ICryptoTransform decryptionCipher
;
64 private KeyedHashAlgorithm clientHMAC
;
65 private KeyedHashAlgorithm serverHMAC
;
69 #region Protected Properties
71 protected ICryptoTransform EncryptionCipher
73 get { return this.encryptionCipher; }
76 protected ICryptoTransform DecryptionCipher
78 get { return this.decryptionCipher; }
81 protected KeyedHashAlgorithm ClientHMAC
83 get { return this.clientHMAC; }
86 protected KeyedHashAlgorithm ServerHMAC
88 get { return this.serverHMAC; }
95 public CipherAlgorithmType CipherAlgorithmType
97 get { return this.cipherAlgorithmType; }
100 public string HashAlgorithmName
104 switch (this.hashAlgorithmType
)
106 case HashAlgorithmType
.Md5
:
109 case HashAlgorithmType
.Sha1
:
118 public HashAlgorithmType HashAlgorithmType
120 get { return this.hashAlgorithmType; }
127 switch (this.hashAlgorithmType
)
129 case HashAlgorithmType
.Md5
:
132 case HashAlgorithmType
.Sha1
:
141 public ExchangeAlgorithmType ExchangeAlgorithmType
143 get { return this.exchangeAlgorithmType; }
146 public CipherMode CipherMode
148 get { return this.cipherMode; }
153 get { return this.code; }
158 get { return this.name; }
161 public bool IsExportable
163 get { return this.isExportable; }
166 public byte KeyMaterialSize
168 get { return this.keyMaterialSize; }
171 public int KeyBlockSize
173 get { return this.keyBlockSize; }
176 public byte ExpandedKeyMaterialSize
178 get { return this.expandedKeyMaterialSize; }
181 public short EffectiveKeyBits
183 get { return this.effectiveKeyBits; }
188 get { return this.ivSize; }
192 public byte BlockSize
194 get { return this.blockSize; }
198 public Context Context
200 get { return this.context; }
203 this.context
= value;
212 short code
, string name
, CipherAlgorithmType cipherAlgorithmType
,
213 HashAlgorithmType hashAlgorithmType
, ExchangeAlgorithmType exchangeAlgorithmType
,
214 bool exportable
, bool blockMode
, byte keyMaterialSize
,
215 byte expandedKeyMaterialSize
, short effectiveKeyBits
,
216 byte ivSize
, byte blockSize
)
220 this.cipherAlgorithmType
= cipherAlgorithmType
;
221 this.hashAlgorithmType
= hashAlgorithmType
;
222 this.exchangeAlgorithmType
= exchangeAlgorithmType
;
223 this.isExportable
= exportable
;
226 this.cipherMode
= CipherMode
.CBC
;
228 this.keyMaterialSize
= keyMaterialSize
;
229 this.expandedKeyMaterialSize
= expandedKeyMaterialSize
;
230 this.effectiveKeyBits
= effectiveKeyBits
;
231 this.ivSize
= ivSize
;
232 this.blockSize
= blockSize
;
233 this.keyBlockSize
= (this.keyMaterialSize
+ this.HashSize
+ this.ivSize
) << 1;
240 internal void Write (byte[] array
, int offset
, short value)
242 if (offset
> array
.Length
- 2)
243 throw new ArgumentException ("offset");
245 array
[offset
] = (byte) (value >> 8);
246 array
[offset
+ 1] = (byte) value;
249 internal void Write (byte[] array
, int offset
, ulong value)
251 if (offset
> array
.Length
- 8)
252 throw new ArgumentException ("offset");
254 array
[offset
] = (byte) (value >> 56);
255 array
[offset
+ 1] = (byte) (value >> 48);
256 array
[offset
+ 2] = (byte) (value >> 40);
257 array
[offset
+ 3] = (byte) (value >> 32);
258 array
[offset
+ 4] = (byte) (value >> 24);
259 array
[offset
+ 5] = (byte) (value >> 16);
260 array
[offset
+ 6] = (byte) (value >> 8);
261 array
[offset
+ 7] = (byte) value;
264 public void InitializeCipher()
266 this.createEncryptionCipher();
267 this.createDecryptionCipher();
270 public byte[] EncryptRecord(byte[] fragment
, byte[] mac
)
272 // Encryption ( fragment + mac [+ padding + padding_length] )
273 int length
= fragment
.Length
+ mac
.Length
;
275 if (this.CipherMode
== CipherMode
.CBC
) {
276 // Calculate padding_length
277 length
++; // keep an extra byte
278 padlen
= (this.blockSize
- length
% this.blockSize
);
279 if (padlen
== this.blockSize
) {
285 byte[] plain
= new byte [length
];
286 Buffer
.BlockCopy (fragment
, 0, plain
, 0, fragment
.Length
);
287 Buffer
.BlockCopy (mac
, 0, plain
, fragment
.Length
, mac
.Length
);
289 int start
= fragment
.Length
+ mac
.Length
;
290 for (int i
= start
; i
< (start
+ padlen
+ 1); i
++) {
291 plain
[i
] = (byte)padlen
;
295 this.EncryptionCipher
.TransformBlock (plain
, 0, plain
.Length
, plain
, 0);
299 public void DecryptRecord(byte[] fragment
, out byte[] dcrFragment
, out byte[] dcrMAC
)
301 int fragmentSize
= 0;
302 int paddingLength
= 0;
304 // Decrypt message fragment ( fragment + mac [+ padding + padding_length] )
305 this.DecryptionCipher
.TransformBlock(fragment
, 0, fragment
.Length
, fragment
, 0);
306 // optimization: decrypt "in place", worst case: padding will reduce the size of the data
307 // this will cut in half the memory allocations (dcrFragment and dcrMAC remains)
309 // Calculate fragment size
310 if (this.CipherMode
== CipherMode
.CBC
)
312 // Calculate padding_length
313 paddingLength
= fragment
[fragment
.Length
- 1];
314 fragmentSize
= (fragment
.Length
- (paddingLength
+ 1)) - this.HashSize
;
318 fragmentSize
= fragment
.Length
- this.HashSize
;
321 dcrFragment
= new byte[fragmentSize
];
322 dcrMAC
= new byte[HashSize
];
324 Buffer
.BlockCopy(fragment
, 0, dcrFragment
, 0, dcrFragment
.Length
);
325 Buffer
.BlockCopy(fragment
, dcrFragment
.Length
, dcrMAC
, 0, dcrMAC
.Length
);
330 #region Abstract Methods
332 public abstract byte[] ComputeClientRecordMAC(ContentType contentType
, byte[] fragment
);
334 public abstract byte[] ComputeServerRecordMAC(ContentType contentType
, byte[] fragment
);
336 public abstract void ComputeMasterSecret(byte[] preMasterSecret
);
338 public abstract void ComputeKeys();
342 #region Key Generation Methods
344 public byte[] CreatePremasterSecret()
346 ClientContext context
= (ClientContext
)this.context
;
348 // Generate random bytes (total size)
349 byte[] preMasterSecret
= this.context
.GetSecureRandomBytes (48);
350 // and replace the first two bytes with the protocol version
351 // (maximum support version not actual)
352 preMasterSecret
[0] = (byte)(context
.ClientHelloProtocol
>> 8);
353 preMasterSecret
[1] = (byte)context
.ClientHelloProtocol
;
355 return preMasterSecret
;
358 public byte[] PRF(byte[] secret
, string label
, byte[] data
, int length
)
360 /* Secret Length calc exmplain from the RFC2246. Section 5
362 * S1 and S2 are the two halves of the secret and each is the same
363 * length. S1 is taken from the first half of the secret, S2 from the
364 * second half. Their length is created by rounding up the length of the
365 * overall secret divided by two; thus, if the original secret is an odd
366 * number of bytes long, the last byte of S1 will be the same as the
371 int secretLen
= secret
.Length
>> 1;
373 if ((secret
.Length
& 0x1) == 0x1)
377 TlsStream seedStream
= new TlsStream();
378 seedStream
.Write(Encoding
.ASCII
.GetBytes(label
));
379 seedStream
.Write(data
);
380 byte[] seed
= seedStream
.ToArray();
384 byte[] secret1
= new byte[secretLen
];
385 Buffer
.BlockCopy(secret
, 0, secret1
, 0, secretLen
);
388 byte[] secret2
= new byte[secretLen
];
389 Buffer
.BlockCopy(secret
, (secret
.Length
- secretLen
), secret2
, 0, secretLen
);
391 // Secret 1 processing
392 byte[] p_md5
= Expand("MD5", secret1
, seed
, length
);
394 // Secret 2 processing
395 byte[] p_sha
= Expand("SHA1", secret2
, seed
, length
);
397 // Perfor XOR of both results
398 byte[] masterSecret
= new byte[length
];
399 for (int i
= 0; i
< masterSecret
.Length
; i
++)
401 masterSecret
[i
] = (byte)(p_md5
[i
] ^ p_sha
[i
]);
407 public byte[] Expand(string hashName
, byte[] secret
, byte[] seed
, int length
)
409 int hashLength
= hashName
== "MD5" ? 16 : 20;
410 int iterations
= (int)(length
/ hashLength
);
411 if ((length
% hashLength
) > 0)
416 M
.HMAC hmac
= new M
.HMAC(hashName
, secret
);
417 TlsStream resMacs
= new TlsStream();
419 byte[][] hmacs
= new byte[iterations
+ 1][];
421 for (int i
= 1; i
<= iterations
; i
++)
423 TlsStream hcseed
= new TlsStream();
424 hmac
.TransformFinalBlock(hmacs
[i
-1], 0, hmacs
[i
-1].Length
);
425 hmacs
[i
] = hmac
.Hash
;
426 hcseed
.Write(hmacs
[i
]);
428 hmac
.TransformFinalBlock(hcseed
.ToArray(), 0, (int)hcseed
.Length
);
429 resMacs
.Write(hmac
.Hash
);
433 byte[] res
= new byte[length
];
435 Buffer
.BlockCopy(resMacs
.ToArray(), 0, res
, 0, res
.Length
);
444 #region Private Methods
446 private void createEncryptionCipher()
448 // Create and configure the symmetric algorithm
449 switch (this.cipherAlgorithmType
)
451 case CipherAlgorithmType
.Des
:
452 this.encryptionAlgorithm
= DES
.Create();
455 case CipherAlgorithmType
.Rc2
:
456 this.encryptionAlgorithm
= RC2
.Create();
459 case CipherAlgorithmType
.Rc4
:
460 this.encryptionAlgorithm
= new ARC4Managed();
463 case CipherAlgorithmType
.TripleDes
:
464 this.encryptionAlgorithm
= TripleDES
.Create();
467 case CipherAlgorithmType
.Rijndael
:
468 this.encryptionAlgorithm
= Rijndael
.Create();
472 // If it's a block cipher
473 if (this.cipherMode
== CipherMode
.CBC
)
475 // Configure encrypt algorithm
476 this.encryptionAlgorithm
.Mode
= this.cipherMode
;
477 this.encryptionAlgorithm
.Padding
= PaddingMode
.None
;
478 this.encryptionAlgorithm
.KeySize
= this.expandedKeyMaterialSize
* 8;
479 this.encryptionAlgorithm
.BlockSize
= this.blockSize
* 8;
482 // Set the key and IV for the algorithm
483 if (this.context
is ClientContext
)
485 this.encryptionAlgorithm
.Key
= this.context
.ClientWriteKey
;
486 this.encryptionAlgorithm
.IV
= this.context
.ClientWriteIV
;
490 this.encryptionAlgorithm
.Key
= this.context
.ServerWriteKey
;
491 this.encryptionAlgorithm
.IV
= this.context
.ServerWriteIV
;
494 // Create encryption cipher
495 this.encryptionCipher
= this.encryptionAlgorithm
.CreateEncryptor();
497 // Create the HMAC algorithm
498 if (this.context
is ClientContext
)
500 this.clientHMAC
= new M
.HMAC(
501 this.HashAlgorithmName
,
502 this.context
.Negotiating
.ClientWriteMAC
);
506 this.serverHMAC
= new M
.HMAC(
507 this.HashAlgorithmName
,
508 this.context
.Negotiating
.ServerWriteMAC
);
512 private void createDecryptionCipher()
514 // Create and configure the symmetric algorithm
515 switch (this.cipherAlgorithmType
)
517 case CipherAlgorithmType
.Des
:
518 this.decryptionAlgorithm
= DES
.Create();
521 case CipherAlgorithmType
.Rc2
:
522 this.decryptionAlgorithm
= RC2
.Create();
525 case CipherAlgorithmType
.Rc4
:
526 this.decryptionAlgorithm
= new ARC4Managed();
529 case CipherAlgorithmType
.TripleDes
:
530 this.decryptionAlgorithm
= TripleDES
.Create();
533 case CipherAlgorithmType
.Rijndael
:
534 this.decryptionAlgorithm
= Rijndael
.Create();
538 // If it's a block cipher
539 if (this.cipherMode
== CipherMode
.CBC
)
541 // Configure encrypt algorithm
542 this.decryptionAlgorithm
.Mode
= this.cipherMode
;
543 this.decryptionAlgorithm
.Padding
= PaddingMode
.None
;
544 this.decryptionAlgorithm
.KeySize
= this.expandedKeyMaterialSize
* 8;
545 this.decryptionAlgorithm
.BlockSize
= this.blockSize
* 8;
548 // Set the key and IV for the algorithm
549 if (this.context
is ClientContext
)
551 this.decryptionAlgorithm
.Key
= this.context
.ServerWriteKey
;
552 this.decryptionAlgorithm
.IV
= this.context
.ServerWriteIV
;
556 this.decryptionAlgorithm
.Key
= this.context
.ClientWriteKey
;
557 this.decryptionAlgorithm
.IV
= this.context
.ClientWriteIV
;
560 // Create decryption cipher
561 this.decryptionCipher
= this.decryptionAlgorithm
.CreateDecryptor();
564 if (this.context
is ClientContext
)
566 this.serverHMAC
= new M
.HMAC(
567 this.HashAlgorithmName
,
568 this.context
.Negotiating
.ServerWriteMAC
);
572 this.clientHMAC
= new M
.HMAC(
573 this.HashAlgorithmName
,
574 this.context
.Negotiating
.ClientWriteMAC
);