SCrypt.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Diagnostics;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  9. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators
  10. {
  11. /// <summary>Implementation of the scrypt a password-based key derivation function.</summary>
  12. /// <remarks>
  13. /// Scrypt was created by Colin Percival and is specified in
  14. /// <a href="http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01">draft-josefsson-scrypt-kd</a>.
  15. /// </remarks>
  16. public class SCrypt
  17. {
  18. /// <summary>Generate a key using the scrypt key derivation function.</summary>
  19. /// <param name="P">the bytes of the pass phrase.</param>
  20. /// <param name="S">the salt to use for this invocation.</param>
  21. /// <param name="N">CPU/Memory cost parameter. Must be larger than 1, a power of 2 and less than
  22. /// <code>2^(128 * r / 8)</code>.</param>
  23. /// <param name="r">the block size, must be >= 1.</param>
  24. /// <param name="p">Parallelization parameter. Must be a positive integer less than or equal to
  25. /// <code>Int32.MaxValue / (128 * r * 8)</code>.</param>
  26. /// <param name="dkLen">the length of the key to generate.</param>
  27. /// <returns>the generated key.</returns>
  28. public static byte[] Generate(byte[] P, byte[] S, int N, int r, int p, int dkLen)
  29. {
  30. if (P == null)
  31. throw new ArgumentNullException("Passphrase P must be provided.");
  32. if (S == null)
  33. throw new ArgumentNullException("Salt S must be provided.");
  34. if (N <= 1 || !IsPowerOf2(N))
  35. throw new ArgumentException("Cost parameter N must be > 1 and a power of 2.");
  36. // Only value of r that cost (as an int) could be exceeded for is 1
  37. if (r == 1 && N >= 65536)
  38. throw new ArgumentException("Cost parameter N must be > 1 and < 65536.");
  39. if (r < 1)
  40. throw new ArgumentException("Block size r must be >= 1.");
  41. int maxParallel = Int32.MaxValue / (128 * r * 8);
  42. if (p < 1 || p > maxParallel)
  43. {
  44. throw new ArgumentException("Parallelisation parameter p must be >= 1 and <= " + maxParallel
  45. + " (based on block size r of " + r + ")");
  46. }
  47. if (dkLen < 1)
  48. throw new ArgumentException("Generated key length dkLen must be >= 1.");
  49. return MFcrypt(P, S, N, r, p, dkLen);
  50. }
  51. private static byte[] MFcrypt(byte[] P, byte[] S, int N, int r, int p, int dkLen)
  52. {
  53. int MFLenBytes = r * 128;
  54. byte[] bytes = SingleIterationPBKDF2(P, S, p * MFLenBytes);
  55. uint[] B = null;
  56. try
  57. {
  58. int BLen = bytes.Length >> 2;
  59. B = new uint[BLen];
  60. Pack.LE_To_UInt32(bytes, 0, B);
  61. int MFLenWords = MFLenBytes >> 2;
  62. for (int BOff = 0; BOff < BLen; BOff += MFLenWords)
  63. {
  64. // TODO These can be done in parallel threads
  65. SMix(B, BOff, N, r);
  66. }
  67. Pack.UInt32_To_LE(B, bytes, 0);
  68. return SingleIterationPBKDF2(P, bytes, dkLen);
  69. }
  70. finally
  71. {
  72. ClearAll(bytes, B);
  73. }
  74. }
  75. private static byte[] SingleIterationPBKDF2(byte[] P, byte[] S, int dkLen)
  76. {
  77. PbeParametersGenerator pGen = new Pkcs5S2ParametersGenerator(new Sha256Digest());
  78. pGen.Init(P, S, 1);
  79. KeyParameter key = (KeyParameter)pGen.GenerateDerivedMacParameters(dkLen * 8);
  80. return key.GetKey();
  81. }
  82. private static void SMix(uint[] B, int BOff, int N, int r)
  83. {
  84. int BCount = r * 32;
  85. uint[] blockX1 = new uint[16];
  86. uint[] blockX2 = new uint[16];
  87. uint[] blockY = new uint[BCount];
  88. uint[] X = new uint[BCount];
  89. uint[][] V = new uint[N][];
  90. try
  91. {
  92. Array.Copy(B, BOff, X, 0, BCount);
  93. for (int i = 0; i < N; ++i)
  94. {
  95. V[i] = (uint[])X.Clone();
  96. BlockMix(X, blockX1, blockX2, blockY, r);
  97. }
  98. uint mask = (uint)N - 1;
  99. for (int i = 0; i < N; ++i)
  100. {
  101. uint j = X[BCount - 16] & mask;
  102. Xor(X, V[j], 0, X);
  103. BlockMix(X, blockX1, blockX2, blockY, r);
  104. }
  105. Array.Copy(X, 0, B, BOff, BCount);
  106. }
  107. finally
  108. {
  109. ClearAll(V);
  110. ClearAll(X, blockX1, blockX2, blockY);
  111. }
  112. }
  113. private static void BlockMix(uint[] B, uint[] X1, uint[] X2, uint[] Y, int r)
  114. {
  115. Array.Copy(B, B.Length - 16, X1, 0, 16);
  116. int BOff = 0, YOff = 0, halfLen = B.Length >> 1;
  117. for (int i = 2 * r; i > 0; --i)
  118. {
  119. Xor(X1, B, BOff, X2);
  120. Salsa20Engine.SalsaCore(8, X2, X1);
  121. Array.Copy(X1, 0, Y, YOff, 16);
  122. YOff = halfLen + BOff - YOff;
  123. BOff += 16;
  124. }
  125. Array.Copy(Y, 0, B, 0, Y.Length);
  126. }
  127. private static void Xor(uint[] a, uint[] b, int bOff, uint[] output)
  128. {
  129. for (int i = output.Length - 1; i >= 0; --i)
  130. {
  131. output[i] = a[i] ^ b[bOff + i];
  132. }
  133. }
  134. private static void Clear(Array array)
  135. {
  136. if (array != null)
  137. {
  138. Array.Clear(array, 0, array.Length);
  139. }
  140. }
  141. private static void ClearAll(params Array[] arrays)
  142. {
  143. foreach (Array array in arrays)
  144. {
  145. Clear(array);
  146. }
  147. }
  148. // note: we know X is non-zero
  149. private static bool IsPowerOf2(int x)
  150. {
  151. Debug.Assert(x != 0);
  152. return (x & (x - 1)) == 0;
  153. }
  154. }
  155. }
  156. #pragma warning restore
  157. #endif