CtrSP800Drbg.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Encoders;
  7. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Prng.Drbg
  8. {
  9. /**
  10. * A SP800-90A CTR DRBG.
  11. */
  12. public class CtrSP800Drbg
  13. : ISP80090Drbg
  14. {
  15. private static readonly long TDEA_RESEED_MAX = 1L << (32 - 1);
  16. private static readonly long AES_RESEED_MAX = 1L << (48 - 1);
  17. private static readonly int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1);
  18. private static readonly int AES_MAX_BITS_REQUEST = 1 << (19 - 1);
  19. private readonly IEntropySource mEntropySource;
  20. private readonly IBlockCipher mEngine;
  21. private readonly int mKeySizeInBits;
  22. private readonly int mSeedLength;
  23. private readonly int mSecurityStrength;
  24. // internal state
  25. private byte[] mKey;
  26. private byte[] mV;
  27. private long mReseedCounter = 0;
  28. private bool mIsTdea = false;
  29. /**
  30. * Construct a SP800-90A CTR DRBG.
  31. * <p>
  32. * Minimum entropy requirement is the security strength requested.
  33. * </p>
  34. * @param engine underlying block cipher to use to support DRBG
  35. * @param keySizeInBits size of the key to use with the block cipher.
  36. * @param securityStrength security strength required (in bits)
  37. * @param entropySource source of entropy to use for seeding/reseeding.
  38. * @param personalizationString personalization string to distinguish this DRBG (may be null).
  39. * @param nonce nonce to further distinguish this DRBG (may be null).
  40. */
  41. public CtrSP800Drbg(IBlockCipher engine, int keySizeInBits, int securityStrength, IEntropySource entropySource,
  42. byte[] personalizationString, byte[] nonce)
  43. {
  44. if (securityStrength > 256)
  45. throw new ArgumentException("Requested security strength is not supported by the derivation function");
  46. if (GetMaxSecurityStrength(engine, keySizeInBits) < securityStrength)
  47. throw new ArgumentException("Requested security strength is not supported by block cipher and key size");
  48. if (entropySource.EntropySize < securityStrength)
  49. throw new ArgumentException("Not enough entropy for security strength required");
  50. mEntropySource = entropySource;
  51. mEngine = engine;
  52. mKeySizeInBits = keySizeInBits;
  53. mSecurityStrength = securityStrength;
  54. mSeedLength = keySizeInBits + engine.GetBlockSize() * 8;
  55. mIsTdea = IsTdea(engine);
  56. byte[] entropy = GetEntropy(); // Get_entropy_input
  57. CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString);
  58. }
  59. private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce, byte[] personalisationString)
  60. {
  61. byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalisationString);
  62. byte[] seed = Block_Cipher_df(seedMaterial, mSeedLength);
  63. int outlen = mEngine.GetBlockSize();
  64. mKey = new byte[(mKeySizeInBits + 7) / 8];
  65. mV = new byte[outlen];
  66. // mKey & mV are modified by this call
  67. CTR_DRBG_Update(seed, mKey, mV);
  68. mReseedCounter = 1;
  69. }
  70. private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
  71. {
  72. byte[] temp = new byte[seed.Length];
  73. byte[] outputBlock = new byte[mEngine.GetBlockSize()];
  74. int i = 0;
  75. int outLen = mEngine.GetBlockSize();
  76. mEngine.Init(true, new KeyParameter(ExpandKey(key)));
  77. while (i*outLen < seed.Length)
  78. {
  79. AddOneTo(v);
  80. mEngine.ProcessBlock(v, 0, outputBlock, 0);
  81. int bytesToCopy = ((temp.Length - i * outLen) > outLen)
  82. ? outLen : (temp.Length - i * outLen);
  83. Array.Copy(outputBlock, 0, temp, i * outLen, bytesToCopy);
  84. ++i;
  85. }
  86. XOR(temp, seed, temp, 0);
  87. Array.Copy(temp, 0, key, 0, key.Length);
  88. Array.Copy(temp, key.Length, v, 0, v.Length);
  89. }
  90. private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
  91. {
  92. byte[] seedMaterial = Arrays.Concatenate(GetEntropy(), additionalInput);
  93. seedMaterial = Block_Cipher_df(seedMaterial, mSeedLength);
  94. CTR_DRBG_Update(seedMaterial, mKey, mV);
  95. mReseedCounter = 1;
  96. }
  97. private void XOR(byte[] output, byte[] a, byte[] b, int bOff)
  98. {
  99. for (int i = 0; i < output.Length; i++)
  100. {
  101. output[i] = (byte)(a[i] ^ b[bOff + i]);
  102. }
  103. }
  104. private void AddOneTo(byte[] longer)
  105. {
  106. uint carry = 1;
  107. int i = longer.Length;
  108. while (--i >= 0)
  109. {
  110. carry += longer[i];
  111. longer[i] = (byte)carry;
  112. carry >>= 8;
  113. }
  114. }
  115. private byte[] GetEntropy()
  116. {
  117. byte[] entropy = mEntropySource.GetEntropy();
  118. if (entropy.Length < (mSecurityStrength + 7) / 8)
  119. throw new InvalidOperationException("Insufficient entropy provided by entropy source");
  120. return entropy;
  121. }
  122. // -- Internal state migration ---
  123. private static readonly byte[] K_BITS = Hex.DecodeStrict("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
  124. // 1. If (number_of_bits_to_return > max_number_of_bits), then return an
  125. // ERROR_FLAG.
  126. // 2. L = len (input_string)/8.
  127. // 3. N = number_of_bits_to_return/8.
  128. // Comment: L is the bitstring represention of
  129. // the integer resulting from len (input_string)/8.
  130. // L shall be represented as a 32-bit integer.
  131. //
  132. // Comment : N is the bitstring represention of
  133. // the integer resulting from
  134. // number_of_bits_to_return/8. N shall be
  135. // represented as a 32-bit integer.
  136. //
  137. // 4. S = L || N || input_string || 0x80.
  138. // 5. While (len (S) mod outlen)
  139. // Comment : Pad S with zeros, if necessary.
  140. // 0, S = S || 0x00.
  141. //
  142. // Comment : Compute the starting value.
  143. // 6. temp = the Null string.
  144. // 7. i = 0.
  145. // 8. K = Leftmost keylen bits of 0x00010203...1D1E1F.
  146. // 9. While len (temp) < keylen + outlen, do
  147. //
  148. // IV = i || 0outlen - len (i).
  149. //
  150. // 9.1
  151. //
  152. // temp = temp || BCC (K, (IV || S)).
  153. //
  154. // 9.2
  155. //
  156. // i = i + 1.
  157. //
  158. // 9.3
  159. //
  160. // Comment : i shall be represented as a 32-bit
  161. // integer, i.e., len (i) = 32.
  162. //
  163. // Comment: The 32-bit integer represenation of
  164. // i is padded with zeros to outlen bits.
  165. //
  166. // Comment: Compute the requested number of
  167. // bits.
  168. //
  169. // 10. K = Leftmost keylen bits of temp.
  170. //
  171. // 11. X = Next outlen bits of temp.
  172. //
  173. // 12. temp = the Null string.
  174. //
  175. // 13. While len (temp) < number_of_bits_to_return, do
  176. //
  177. // 13.1 X = Block_Encrypt (K, X).
  178. //
  179. // 13.2 temp = temp || X.
  180. //
  181. // 14. requested_bits = Leftmost number_of_bits_to_return of temp.
  182. //
  183. // 15. Return SUCCESS and requested_bits.
  184. private byte[] Block_Cipher_df(byte[] inputString, int bitLength)
  185. {
  186. int outLen = mEngine.GetBlockSize();
  187. int L = inputString.Length; // already in bytes
  188. int N = bitLength / 8;
  189. // 4 S = L || N || inputstring || 0x80
  190. int sLen = 4 + 4 + L + 1;
  191. int blockLen = ((sLen + outLen - 1) / outLen) * outLen;
  192. byte[] S = new byte[blockLen];
  193. copyIntToByteArray(S, L, 0);
  194. copyIntToByteArray(S, N, 4);
  195. Array.Copy(inputString, 0, S, 8, L);
  196. S[8 + L] = (byte)0x80;
  197. // S already padded with zeros
  198. byte[] temp = new byte[mKeySizeInBits / 8 + outLen];
  199. byte[] bccOut = new byte[outLen];
  200. byte[] IV = new byte[outLen];
  201. int i = 0;
  202. byte[] K = new byte[mKeySizeInBits / 8];
  203. Array.Copy(K_BITS, 0, K, 0, K.Length);
  204. while (i*outLen*8 < mKeySizeInBits + outLen *8)
  205. {
  206. copyIntToByteArray(IV, i, 0);
  207. BCC(bccOut, K, IV, S);
  208. int bytesToCopy = ((temp.Length - i * outLen) > outLen)
  209. ? outLen
  210. : (temp.Length - i * outLen);
  211. Array.Copy(bccOut, 0, temp, i * outLen, bytesToCopy);
  212. ++i;
  213. }
  214. byte[] X = new byte[outLen];
  215. Array.Copy(temp, 0, K, 0, K.Length);
  216. Array.Copy(temp, K.Length, X, 0, X.Length);
  217. temp = new byte[bitLength / 2];
  218. i = 0;
  219. mEngine.Init(true, new KeyParameter(ExpandKey(K)));
  220. while (i * outLen < temp.Length)
  221. {
  222. mEngine.ProcessBlock(X, 0, X, 0);
  223. int bytesToCopy = ((temp.Length - i * outLen) > outLen)
  224. ? outLen
  225. : (temp.Length - i * outLen);
  226. Array.Copy(X, 0, temp, i * outLen, bytesToCopy);
  227. i++;
  228. }
  229. return temp;
  230. }
  231. /*
  232. * 1. chaining_value = 0^outlen
  233. * . Comment: Set the first chaining value to outlen zeros.
  234. * 2. n = len (data)/outlen.
  235. * 3. Starting with the leftmost bits of data, split the data into n blocks of outlen bits
  236. * each, forming block(1) to block(n).
  237. * 4. For i = 1 to n do
  238. * 4.1 input_block = chaining_value ^ block(i) .
  239. * 4.2 chaining_value = Block_Encrypt (Key, input_block).
  240. * 5. output_block = chaining_value.
  241. * 6. Return output_block.
  242. */
  243. private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data)
  244. {
  245. int outlen = mEngine.GetBlockSize();
  246. byte[] chainingValue = new byte[outlen]; // initial values = 0
  247. int n = data.Length / outlen;
  248. byte[] inputBlock = new byte[outlen];
  249. mEngine.Init(true, new KeyParameter(ExpandKey(k)));
  250. mEngine.ProcessBlock(iV, 0, chainingValue, 0);
  251. for (int i = 0; i < n; i++)
  252. {
  253. XOR(inputBlock, chainingValue, data, i*outlen);
  254. mEngine.ProcessBlock(inputBlock, 0, chainingValue, 0);
  255. }
  256. Array.Copy(chainingValue, 0, bccOut, 0, bccOut.Length);
  257. }
  258. private void copyIntToByteArray(byte[] buf, int value, int offSet)
  259. {
  260. buf[offSet + 0] = ((byte)(value >> 24));
  261. buf[offSet + 1] = ((byte)(value >> 16));
  262. buf[offSet + 2] = ((byte)(value >> 8));
  263. buf[offSet + 3] = ((byte)(value));
  264. }
  265. /**
  266. * Return the block size (in bits) of the DRBG.
  267. *
  268. * @return the number of bits produced on each internal round of the DRBG.
  269. */
  270. public int BlockSize
  271. {
  272. get { return mV.Length * 8; }
  273. }
  274. /**
  275. * Populate a passed in array with random data.
  276. *
  277. * @param output output array for generated bits.
  278. * @param additionalInput additional input to be added to the DRBG in this step.
  279. * @param predictionResistant true if a reseed should be forced, false otherwise.
  280. *
  281. * @return number of bits generated, -1 if a reseed required.
  282. */
  283. public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
  284. {
  285. if (mIsTdea)
  286. {
  287. if (mReseedCounter > TDEA_RESEED_MAX)
  288. return -1;
  289. if (DrbgUtilities.IsTooLarge(output, TDEA_MAX_BITS_REQUEST / 8))
  290. throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST, "output");
  291. }
  292. else
  293. {
  294. if (mReseedCounter > AES_RESEED_MAX)
  295. return -1;
  296. if (DrbgUtilities.IsTooLarge(output, AES_MAX_BITS_REQUEST / 8))
  297. throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST, "output");
  298. }
  299. if (predictionResistant)
  300. {
  301. CTR_DRBG_Reseed_algorithm(additionalInput);
  302. additionalInput = null;
  303. }
  304. if (additionalInput != null)
  305. {
  306. additionalInput = Block_Cipher_df(additionalInput, mSeedLength);
  307. CTR_DRBG_Update(additionalInput, mKey, mV);
  308. }
  309. else
  310. {
  311. additionalInput = new byte[mSeedLength];
  312. }
  313. byte[] tmp = new byte[mV.Length];
  314. mEngine.Init(true, new KeyParameter(ExpandKey(mKey)));
  315. for (int i = 0; i <= output.Length / tmp.Length; i++)
  316. {
  317. int bytesToCopy = ((output.Length - i * tmp.Length) > tmp.Length)
  318. ? tmp.Length
  319. : (output.Length - i * mV.Length);
  320. if (bytesToCopy != 0)
  321. {
  322. AddOneTo(mV);
  323. mEngine.ProcessBlock(mV, 0, tmp, 0);
  324. Array.Copy(tmp, 0, output, i * tmp.Length, bytesToCopy);
  325. }
  326. }
  327. CTR_DRBG_Update(additionalInput, mKey, mV);
  328. mReseedCounter++;
  329. return output.Length * 8;
  330. }
  331. /**
  332. * Reseed the DRBG.
  333. *
  334. * @param additionalInput additional input to be added to the DRBG in this step.
  335. */
  336. public void Reseed(byte[] additionalInput)
  337. {
  338. CTR_DRBG_Reseed_algorithm(additionalInput);
  339. }
  340. private bool IsTdea(IBlockCipher cipher)
  341. {
  342. return cipher.AlgorithmName.Equals("DESede") || cipher.AlgorithmName.Equals("TDEA");
  343. }
  344. private int GetMaxSecurityStrength(IBlockCipher cipher, int keySizeInBits)
  345. {
  346. if (IsTdea(cipher) && keySizeInBits == 168)
  347. {
  348. return 112;
  349. }
  350. if (cipher.AlgorithmName.Equals("AES"))
  351. {
  352. return keySizeInBits;
  353. }
  354. return -1;
  355. }
  356. private byte[] ExpandKey(byte[] key)
  357. {
  358. if (mIsTdea)
  359. {
  360. // expand key to 192 bits.
  361. byte[] tmp = new byte[24];
  362. PadKey(key, 0, tmp, 0);
  363. PadKey(key, 7, tmp, 8);
  364. PadKey(key, 14, tmp, 16);
  365. return tmp;
  366. }
  367. else
  368. {
  369. return key;
  370. }
  371. }
  372. /**
  373. * Pad out a key for TDEA, setting odd parity for each byte.
  374. *
  375. * @param keyMaster
  376. * @param keyOff
  377. * @param tmp
  378. * @param tmpOff
  379. */
  380. private void PadKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff)
  381. {
  382. tmp[tmpOff + 0] = (byte)(keyMaster[keyOff + 0] & 0xfe);
  383. tmp[tmpOff + 1] = (byte)((keyMaster[keyOff + 0] << 7) | ((keyMaster[keyOff + 1] & 0xfc) >> 1));
  384. tmp[tmpOff + 2] = (byte)((keyMaster[keyOff + 1] << 6) | ((keyMaster[keyOff + 2] & 0xf8) >> 2));
  385. tmp[tmpOff + 3] = (byte)((keyMaster[keyOff + 2] << 5) | ((keyMaster[keyOff + 3] & 0xf0) >> 3));
  386. tmp[tmpOff + 4] = (byte)((keyMaster[keyOff + 3] << 4) | ((keyMaster[keyOff + 4] & 0xe0) >> 4));
  387. tmp[tmpOff + 5] = (byte)((keyMaster[keyOff + 4] << 3) | ((keyMaster[keyOff + 5] & 0xc0) >> 5));
  388. tmp[tmpOff + 6] = (byte)((keyMaster[keyOff + 5] << 2) | ((keyMaster[keyOff + 6] & 0x80) >> 6));
  389. tmp[tmpOff + 7] = (byte)(keyMaster[keyOff + 6] << 1);
  390. DesParameters.SetOddParity(tmp, tmpOff, 8);
  391. }
  392. }
  393. }
  394. #pragma warning restore
  395. #endif