CipherSuite.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. // Transport Security Layer (TLS)
  2. // Copyright (c) 2003-2004 Carlos Guzman Alvarez
  3. // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
  4. //
  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:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  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.
  23. //
  24. using System;
  25. using System.IO;
  26. using System.Text;
  27. using System.Security.Cryptography;
  28. using Mono.Security;
  29. using Mono.Security.Cryptography;
  30. using M = Mono.Security.Cryptography;
  31. namespace Mono.Security.Protocol.Tls
  32. {
  33. internal abstract class CipherSuite
  34. {
  35. #region Static Fields
  36. public static byte[] EmptyArray = new byte[0];
  37. #endregion
  38. #region Fields
  39. private short code;
  40. private string name;
  41. private CipherAlgorithmType cipherAlgorithmType;
  42. private HashAlgorithmType hashAlgorithmType;
  43. private ExchangeAlgorithmType exchangeAlgorithmType;
  44. private bool isExportable;
  45. private CipherMode cipherMode;
  46. private byte keyMaterialSize;
  47. private int keyBlockSize;
  48. private byte expandedKeyMaterialSize;
  49. private short effectiveKeyBits;
  50. private byte ivSize;
  51. private byte blockSize;
  52. private Context context;
  53. private SymmetricAlgorithm encryptionAlgorithm;
  54. private ICryptoTransform encryptionCipher;
  55. private SymmetricAlgorithm decryptionAlgorithm;
  56. private ICryptoTransform decryptionCipher;
  57. private KeyedHashAlgorithm clientHMAC;
  58. private KeyedHashAlgorithm serverHMAC;
  59. #endregion
  60. #region Protected Properties
  61. protected ICryptoTransform EncryptionCipher
  62. {
  63. get { return this.encryptionCipher; }
  64. }
  65. protected ICryptoTransform DecryptionCipher
  66. {
  67. get { return this.decryptionCipher; }
  68. }
  69. protected KeyedHashAlgorithm ClientHMAC
  70. {
  71. get { return this.clientHMAC; }
  72. }
  73. protected KeyedHashAlgorithm ServerHMAC
  74. {
  75. get { return this.serverHMAC; }
  76. }
  77. #endregion
  78. #region Properties
  79. public CipherAlgorithmType CipherAlgorithmType
  80. {
  81. get { return this.cipherAlgorithmType; }
  82. }
  83. public string HashAlgorithmName
  84. {
  85. get
  86. {
  87. switch (this.hashAlgorithmType)
  88. {
  89. case HashAlgorithmType.Md5:
  90. return "MD5";
  91. case HashAlgorithmType.Sha1:
  92. return "SHA1";
  93. default:
  94. return "None";
  95. }
  96. }
  97. }
  98. public HashAlgorithmType HashAlgorithmType
  99. {
  100. get { return this.hashAlgorithmType; }
  101. }
  102. public int HashSize
  103. {
  104. get
  105. {
  106. switch (this.hashAlgorithmType)
  107. {
  108. case HashAlgorithmType.Md5:
  109. return 16;
  110. case HashAlgorithmType.Sha1:
  111. return 20;
  112. default:
  113. return 0;
  114. }
  115. }
  116. }
  117. public ExchangeAlgorithmType ExchangeAlgorithmType
  118. {
  119. get { return this.exchangeAlgorithmType; }
  120. }
  121. public CipherMode CipherMode
  122. {
  123. get { return this.cipherMode; }
  124. }
  125. public short Code
  126. {
  127. get { return this.code; }
  128. }
  129. public string Name
  130. {
  131. get { return this.name; }
  132. }
  133. public bool IsExportable
  134. {
  135. get { return this.isExportable; }
  136. }
  137. public byte KeyMaterialSize
  138. {
  139. get { return this.keyMaterialSize; }
  140. }
  141. public int KeyBlockSize
  142. {
  143. get { return this.keyBlockSize; }
  144. }
  145. public byte ExpandedKeyMaterialSize
  146. {
  147. get { return this.expandedKeyMaterialSize; }
  148. }
  149. public short EffectiveKeyBits
  150. {
  151. get { return this.effectiveKeyBits; }
  152. }
  153. public byte IvSize
  154. {
  155. get { return this.ivSize; }
  156. }
  157. /*
  158. public byte BlockSize
  159. {
  160. get { return this.blockSize; }
  161. }
  162. */
  163. public Context Context
  164. {
  165. get { return this.context; }
  166. set
  167. {
  168. this.context = value;
  169. }
  170. }
  171. #endregion
  172. #region Constructors
  173. public CipherSuite(
  174. short code, string name, CipherAlgorithmType cipherAlgorithmType,
  175. HashAlgorithmType hashAlgorithmType, ExchangeAlgorithmType exchangeAlgorithmType,
  176. bool exportable, bool blockMode, byte keyMaterialSize,
  177. byte expandedKeyMaterialSize, short effectiveKeyBits,
  178. byte ivSize, byte blockSize)
  179. {
  180. this.code = code;
  181. this.name = name;
  182. this.cipherAlgorithmType = cipherAlgorithmType;
  183. this.hashAlgorithmType = hashAlgorithmType;
  184. this.exchangeAlgorithmType = exchangeAlgorithmType;
  185. this.isExportable = exportable;
  186. if (blockMode)
  187. {
  188. this.cipherMode = CipherMode.CBC;
  189. }
  190. this.keyMaterialSize = keyMaterialSize;
  191. this.expandedKeyMaterialSize= expandedKeyMaterialSize;
  192. this.effectiveKeyBits = effectiveKeyBits;
  193. this.ivSize = ivSize;
  194. this.blockSize = blockSize;
  195. this.keyBlockSize = (this.keyMaterialSize + this.HashSize + this.ivSize) << 1;
  196. }
  197. #endregion
  198. #region Methods
  199. internal void Write (byte[] array, int offset, short value)
  200. {
  201. if (offset > array.Length - 2)
  202. throw new ArgumentException ("offset");
  203. array [offset ] = (byte) (value >> 8);
  204. array [offset + 1] = (byte) value;
  205. }
  206. internal void Write (byte[] array, int offset, ulong value)
  207. {
  208. if (offset > array.Length - 8)
  209. throw new ArgumentException ("offset");
  210. array [offset ] = (byte) (value >> 56);
  211. array [offset + 1] = (byte) (value >> 48);
  212. array [offset + 2] = (byte) (value >> 40);
  213. array [offset + 3] = (byte) (value >> 32);
  214. array [offset + 4] = (byte) (value >> 24);
  215. array [offset + 5] = (byte) (value >> 16);
  216. array [offset + 6] = (byte) (value >> 8);
  217. array [offset + 7] = (byte) value;
  218. }
  219. public void InitializeCipher()
  220. {
  221. this.createEncryptionCipher();
  222. this.createDecryptionCipher();
  223. }
  224. public byte[] EncryptRecord(byte[] fragment, byte[] mac)
  225. {
  226. // Encryption ( fragment + mac [+ padding + padding_length] )
  227. int length = fragment.Length + mac.Length;
  228. int padlen = 0;
  229. if (this.CipherMode == CipherMode.CBC) {
  230. // Calculate padding_length
  231. length++; // keep an extra byte
  232. padlen = (this.blockSize - length % this.blockSize);
  233. if (padlen == this.blockSize) {
  234. padlen = 0;
  235. }
  236. length += padlen;
  237. }
  238. byte[] plain = new byte [length];
  239. Buffer.BlockCopy (fragment, 0, plain, 0, fragment.Length);
  240. Buffer.BlockCopy (mac, 0, plain, fragment.Length, mac.Length);
  241. if (padlen > 0) {
  242. int start = fragment.Length + mac.Length;
  243. for (int i = start; i < (start + padlen + 1); i++) {
  244. plain[i] = (byte)padlen;
  245. }
  246. }
  247. this.EncryptionCipher.TransformBlock (plain, 0, plain.Length, plain, 0);
  248. return plain;
  249. }
  250. public void DecryptRecord(byte[] fragment, out byte[] dcrFragment, out byte[] dcrMAC)
  251. {
  252. int fragmentSize = 0;
  253. int paddingLength = 0;
  254. // Decrypt message fragment ( fragment + mac [+ padding + padding_length] )
  255. this.DecryptionCipher.TransformBlock(fragment, 0, fragment.Length, fragment, 0);
  256. // optimization: decrypt "in place", worst case: padding will reduce the size of the data
  257. // this will cut in half the memory allocations (dcrFragment and dcrMAC remains)
  258. // Calculate fragment size
  259. if (this.CipherMode == CipherMode.CBC)
  260. {
  261. // Calculate padding_length
  262. paddingLength = fragment[fragment.Length - 1];
  263. fragmentSize = (fragment.Length - (paddingLength + 1)) - this.HashSize;
  264. }
  265. else
  266. {
  267. fragmentSize = fragment.Length - this.HashSize;
  268. }
  269. dcrFragment = new byte[fragmentSize];
  270. dcrMAC = new byte[HashSize];
  271. Buffer.BlockCopy(fragment, 0, dcrFragment, 0, dcrFragment.Length);
  272. Buffer.BlockCopy(fragment, dcrFragment.Length, dcrMAC, 0, dcrMAC.Length);
  273. }
  274. #endregion
  275. #region Abstract Methods
  276. public abstract byte[] ComputeClientRecordMAC(ContentType contentType, byte[] fragment);
  277. public abstract byte[] ComputeServerRecordMAC(ContentType contentType, byte[] fragment);
  278. public abstract void ComputeMasterSecret(byte[] preMasterSecret);
  279. public abstract void ComputeKeys();
  280. #endregion
  281. #region Key Generation Methods
  282. public byte[] CreatePremasterSecret()
  283. {
  284. ClientContext context = (ClientContext)this.context;
  285. // Generate random bytes (total size)
  286. byte[] preMasterSecret = this.context.GetSecureRandomBytes (48);
  287. // and replace the first two bytes with the protocol version
  288. // (maximum support version not actual)
  289. preMasterSecret [0] = (byte)(context.ClientHelloProtocol >> 8);
  290. preMasterSecret [1] = (byte)context.ClientHelloProtocol;
  291. return preMasterSecret;
  292. }
  293. public byte[] PRF(byte[] secret, string label, byte[] data, int length)
  294. {
  295. /* Secret Length calc exmplain from the RFC2246. Section 5
  296. *
  297. * S1 and S2 are the two halves of the secret and each is the same
  298. * length. S1 is taken from the first half of the secret, S2 from the
  299. * second half. Their length is created by rounding up the length of the
  300. * overall secret divided by two; thus, if the original secret is an odd
  301. * number of bytes long, the last byte of S1 will be the same as the
  302. * first byte of S2.
  303. */
  304. // split secret in 2
  305. int secretLen = secret.Length >> 1;
  306. // rounding up
  307. if ((secret.Length & 0x1) == 0x1)
  308. secretLen++;
  309. // Seed
  310. TlsStream seedStream = new TlsStream();
  311. seedStream.Write(Encoding.ASCII.GetBytes(label));
  312. seedStream.Write(data);
  313. byte[] seed = seedStream.ToArray();
  314. seedStream.Reset();
  315. // Secret 1
  316. byte[] secret1 = new byte[secretLen];
  317. Buffer.BlockCopy(secret, 0, secret1, 0, secretLen);
  318. // Secret2
  319. byte[] secret2 = new byte[secretLen];
  320. Buffer.BlockCopy(secret, (secret.Length - secretLen), secret2, 0, secretLen);
  321. // Secret 1 processing
  322. byte[] p_md5 = Expand("MD5", secret1, seed, length);
  323. // Secret 2 processing
  324. byte[] p_sha = Expand("SHA1", secret2, seed, length);
  325. // Perfor XOR of both results
  326. byte[] masterSecret = new byte[length];
  327. for (int i = 0; i < masterSecret.Length; i++)
  328. {
  329. masterSecret[i] = (byte)(p_md5[i] ^ p_sha[i]);
  330. }
  331. return masterSecret;
  332. }
  333. public byte[] Expand(string hashName, byte[] secret, byte[] seed, int length)
  334. {
  335. int hashLength = hashName == "MD5" ? 16 : 20;
  336. int iterations = (int)(length / hashLength);
  337. if ((length % hashLength) > 0)
  338. {
  339. iterations++;
  340. }
  341. M.HMAC hmac = new M.HMAC(hashName, secret);
  342. TlsStream resMacs = new TlsStream();
  343. byte[][] hmacs = new byte[iterations + 1][];
  344. hmacs[0] = seed;
  345. for (int i = 1; i <= iterations; i++)
  346. {
  347. TlsStream hcseed = new TlsStream();
  348. hmac.TransformFinalBlock(hmacs[i-1], 0, hmacs[i-1].Length);
  349. hmacs[i] = hmac.Hash;
  350. hcseed.Write(hmacs[i]);
  351. hcseed.Write(seed);
  352. hmac.TransformFinalBlock(hcseed.ToArray(), 0, (int)hcseed.Length);
  353. resMacs.Write(hmac.Hash);
  354. hcseed.Reset();
  355. }
  356. byte[] res = new byte[length];
  357. Buffer.BlockCopy(resMacs.ToArray(), 0, res, 0, res.Length);
  358. resMacs.Reset();
  359. return res;
  360. }
  361. #endregion
  362. #region Private Methods
  363. private void createEncryptionCipher()
  364. {
  365. // Create and configure the symmetric algorithm
  366. switch (this.cipherAlgorithmType)
  367. {
  368. case CipherAlgorithmType.Des:
  369. this.encryptionAlgorithm = DES.Create();
  370. break;
  371. case CipherAlgorithmType.Rc2:
  372. this.encryptionAlgorithm = RC2.Create();
  373. break;
  374. case CipherAlgorithmType.Rc4:
  375. this.encryptionAlgorithm = new ARC4Managed();
  376. break;
  377. case CipherAlgorithmType.TripleDes:
  378. this.encryptionAlgorithm = TripleDES.Create();
  379. break;
  380. case CipherAlgorithmType.Rijndael:
  381. this.encryptionAlgorithm = Rijndael.Create();
  382. break;
  383. }
  384. // If it's a block cipher
  385. if (this.cipherMode == CipherMode.CBC)
  386. {
  387. // Configure encrypt algorithm
  388. this.encryptionAlgorithm.Mode = this.cipherMode;
  389. this.encryptionAlgorithm.Padding = PaddingMode.None;
  390. this.encryptionAlgorithm.KeySize = this.expandedKeyMaterialSize * 8;
  391. this.encryptionAlgorithm.BlockSize = this.blockSize * 8;
  392. }
  393. // Set the key and IV for the algorithm
  394. if (this.context is ClientContext)
  395. {
  396. this.encryptionAlgorithm.Key = this.context.ClientWriteKey;
  397. this.encryptionAlgorithm.IV = this.context.ClientWriteIV;
  398. }
  399. else
  400. {
  401. this.encryptionAlgorithm.Key = this.context.ServerWriteKey;
  402. this.encryptionAlgorithm.IV = this.context.ServerWriteIV;
  403. }
  404. // Create encryption cipher
  405. this.encryptionCipher = this.encryptionAlgorithm.CreateEncryptor();
  406. // Create the HMAC algorithm
  407. if (this.context is ClientContext)
  408. {
  409. this.clientHMAC = new M.HMAC(
  410. this.HashAlgorithmName,
  411. this.context.Negotiating.ClientWriteMAC);
  412. }
  413. else
  414. {
  415. this.serverHMAC = new M.HMAC(
  416. this.HashAlgorithmName,
  417. this.context.Negotiating.ServerWriteMAC);
  418. }
  419. }
  420. private void createDecryptionCipher()
  421. {
  422. // Create and configure the symmetric algorithm
  423. switch (this.cipherAlgorithmType)
  424. {
  425. case CipherAlgorithmType.Des:
  426. this.decryptionAlgorithm = DES.Create();
  427. break;
  428. case CipherAlgorithmType.Rc2:
  429. this.decryptionAlgorithm = RC2.Create();
  430. break;
  431. case CipherAlgorithmType.Rc4:
  432. this.decryptionAlgorithm = new ARC4Managed();
  433. break;
  434. case CipherAlgorithmType.TripleDes:
  435. this.decryptionAlgorithm = TripleDES.Create();
  436. break;
  437. case CipherAlgorithmType.Rijndael:
  438. this.decryptionAlgorithm = Rijndael.Create();
  439. break;
  440. }
  441. // If it's a block cipher
  442. if (this.cipherMode == CipherMode.CBC)
  443. {
  444. // Configure encrypt algorithm
  445. this.decryptionAlgorithm.Mode = this.cipherMode;
  446. this.decryptionAlgorithm.Padding = PaddingMode.None;
  447. this.decryptionAlgorithm.KeySize = this.expandedKeyMaterialSize * 8;
  448. this.decryptionAlgorithm.BlockSize = this.blockSize * 8;
  449. }
  450. // Set the key and IV for the algorithm
  451. if (this.context is ClientContext)
  452. {
  453. this.decryptionAlgorithm.Key = this.context.ServerWriteKey;
  454. this.decryptionAlgorithm.IV = this.context.ServerWriteIV;
  455. }
  456. else
  457. {
  458. this.decryptionAlgorithm.Key = this.context.ClientWriteKey;
  459. this.decryptionAlgorithm.IV = this.context.ClientWriteIV;
  460. }
  461. // Create decryption cipher
  462. this.decryptionCipher = this.decryptionAlgorithm.CreateDecryptor();
  463. // Create the HMAC
  464. if (this.context is ClientContext)
  465. {
  466. this.serverHMAC = new M.HMAC(
  467. this.HashAlgorithmName,
  468. this.context.Negotiating.ServerWriteMAC);
  469. }
  470. else
  471. {
  472. this.clientHMAC = new M.HMAC(
  473. this.HashAlgorithmName,
  474. this.context.Negotiating.ClientWriteMAC);
  475. }
  476. }
  477. #endregion
  478. }
  479. }