PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/library/Zend/Crypt/Key/Derivation/Scrypt.php

https://bitbucket.org/saifshuvo/zf2
PHP | 342 lines | 250 code | 18 blank | 74 comment | 28 complexity | 5708b49ba6bf040024c4cdcceb9f4a9b MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Crypt\Key\Derivation;
  10. use Zend\Crypt\Key\Derivation\Pbkdf2;
  11. /**
  12. * Scrypt key derivation function
  13. *
  14. * @see http://www.tarsnap.com/scrypt.html
  15. * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01
  16. */
  17. abstract class Scrypt
  18. {
  19. /**
  20. * Execute the scrypt algorithm
  21. *
  22. * @param string $password
  23. * @param string $salt
  24. * @param int $n CPU cost
  25. * @param int $r Memory cost
  26. * @param int $p parallelization cost
  27. * @param int $length size of the output key
  28. * @return string
  29. */
  30. public static function calc($password, $salt, $n, $r, $p, $length)
  31. {
  32. if ($n == 0 || ($n & ($n - 1)) != 0) {
  33. throw new Exception\InvalidArgumentException("N must be > 0 and a power of 2");
  34. }
  35. if ($n > PHP_INT_MAX / 128 / $r) {
  36. throw new Exception\InvalidArgumentException("Parameter n is too large");
  37. }
  38. if ($r > PHP_INT_MAX / 128 / $p) {
  39. throw new Exception\InvalidArgumentException("Parameter r is too large");
  40. }
  41. if (extension_loaded('Scrypt')) {
  42. if ($length < 16) {
  43. throw new Exception\InvalidArgumentException("Key length is too low, must be greater or equal to 16");
  44. }
  45. return self::hex2bin(scrypt($password, $salt, $n, $r, $p, $length));
  46. }
  47. $b = Pbkdf2::calc('sha256', $password, $salt, 1, $p * 128 * $r);
  48. $s = '';
  49. for ($i = 0; $i < $p; $i++) {
  50. $s .= self::scryptROMix(substr($b, $i * 128 * $r, 128 * $r), $n, $r);
  51. }
  52. return Pbkdf2::calc('sha256', $password, $s, 1, $length);
  53. }
  54. /**
  55. * scryptROMix
  56. *
  57. * @param string $b
  58. * @param int $n
  59. * @param int $r
  60. * @return string
  61. * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4
  62. */
  63. protected static function scryptROMix($b, $n, $r)
  64. {
  65. $x = $b;
  66. $v = array();
  67. for ($i = 0; $i < $n; $i++) {
  68. $v[$i] = $x;
  69. $x = self::scryptBlockMix($x, $r);
  70. }
  71. for ($i = 0; $i < $n; $i++) {
  72. $j = self::integerify($x) % $n;
  73. $t = $x ^ $v[$j];
  74. $x = self::scryptBlockMix($t, $r);
  75. }
  76. return $x;
  77. }
  78. /**
  79. * scryptBlockMix
  80. *
  81. * @param string $b
  82. * @param int $r
  83. * @return string
  84. * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-3
  85. */
  86. protected static function scryptBlockMix($b, $r)
  87. {
  88. $x = substr($b, -64);
  89. $even = '';
  90. $odd = '';
  91. $len = 2 * $r;
  92. for ($i = 0; $i < $len; $i++) {
  93. if (PHP_INT_SIZE === 4) {
  94. $x = self::salsa208Core32($x ^ substr($b, 64 * $i, 64));
  95. } else {
  96. $x = self::salsa208Core64($x ^ substr($b, 64 * $i, 64));
  97. }
  98. if ($i % 2 == 0) {
  99. $even .= $x;
  100. } else {
  101. $odd .= $x;
  102. }
  103. }
  104. return $even . $odd;
  105. }
  106. /**
  107. * Salsa 20/8 core (32 bit version)
  108. *
  109. * @param string $b
  110. * @return string
  111. * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2
  112. * @see http://cr.yp.to/salsa20.html
  113. */
  114. protected static function salsa208Core32($b)
  115. {
  116. $b32 = array();
  117. for ($i = 0; $i < 16; $i++) {
  118. list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4));
  119. }
  120. $x = $b32;
  121. for ($i = 0; $i < 8; $i += 2) {
  122. $a = ($x[ 0] + $x[12]);
  123. $x[ 4] ^= ($a << 7) | ($a >> 25) & 0x7f;
  124. $a = ($x[ 4] + $x[ 0]);
  125. $x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff;
  126. $a = ($x[ 8] + $x[ 4]);
  127. $x[12] ^= ($a << 13) | ($a >> 19) & 0x1fff;
  128. $a = ($x[12] + $x[ 8]);
  129. $x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
  130. $a = ($x[ 5] + $x[ 1]);
  131. $x[ 9] ^= ($a << 7) | ($a >> 25) & 0x7f;
  132. $a = ($x[ 9] + $x[ 5]);
  133. $x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff;
  134. $a = ($x[13] + $x[ 9]);
  135. $x[ 1] ^= ($a << 13) | ($a >> 19) & 0x1fff;
  136. $a = ($x[ 1] + $x[13]);
  137. $x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
  138. $a = ($x[10] + $x[ 6]);
  139. $x[14] ^= ($a << 7) | ($a >> 25) & 0x7f;
  140. $a = ($x[14] + $x[10]);
  141. $x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff;
  142. $a = ($x[ 2] + $x[14]);
  143. $x[ 6] ^= ($a << 13) | ($a >> 19) & 0x1fff;
  144. $a = ($x[ 6] + $x[ 2]);
  145. $x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
  146. $a = ($x[15] + $x[11]);
  147. $x[ 3] ^= ($a << 7) | ($a >> 25) & 0x7f;
  148. $a = ($x[ 3] + $x[15]);
  149. $x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff;
  150. $a = ($x[ 7] + $x[ 3]);
  151. $x[11] ^= ($a << 13) | ($a >> 19) & 0x1fff;
  152. $a = ($x[11] + $x[ 7]);
  153. $x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
  154. $a = ($x[ 0] + $x[ 3]);
  155. $x[ 1] ^= ($a << 7) | ($a >> 25) & 0x7f;
  156. $a = ($x[ 1] + $x[ 0]);
  157. $x[ 2] ^= ($a << 9) | ($a >> 23) & 0x1ff;
  158. $a = ($x[ 2] + $x[ 1]);
  159. $x[ 3] ^= ($a << 13) | ($a >> 19) & 0x1fff;
  160. $a = ($x[ 3] + $x[ 2]);
  161. $x[ 0] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
  162. $a = ($x[ 5] + $x[ 4]);
  163. $x[ 6] ^= ($a << 7) | ($a >> 25) & 0x7f;
  164. $a = ($x[ 6] + $x[ 5]);
  165. $x[ 7] ^= ($a << 9) | ($a >> 23) & 0x1ff;
  166. $a = ($x[ 7] + $x[ 6]);
  167. $x[ 4] ^= ($a << 13) | ($a >> 19) & 0x1fff;
  168. $a = ($x[ 4] + $x[ 7]);
  169. $x[ 5] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
  170. $a = ($x[10] + $x[ 9]);
  171. $x[11] ^= ($a << 7) | ($a >> 25) & 0x7f;
  172. $a = ($x[11] + $x[10]);
  173. $x[ 8] ^= ($a << 9) | ($a >> 23) & 0x1ff;
  174. $a = ($x[ 8] + $x[11]);
  175. $x[ 9] ^= ($a << 13) | ($a >> 19) & 0x1fff;
  176. $a = ($x[ 9] + $x[ 8]);
  177. $x[10] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
  178. $a = ($x[15] + $x[14]);
  179. $x[12] ^= ($a << 7) | ($a >> 25) & 0x7f;
  180. $a = ($x[12] + $x[15]);
  181. $x[13] ^= ($a << 9) | ($a >> 23) & 0x1ff;
  182. $a = ($x[13] + $x[12]);
  183. $x[14] ^= ($a << 13) | ($a >> 19) & 0x1fff;
  184. $a = ($x[14] + $x[13]);
  185. $x[15] ^= ($a << 18) | ($a >> 14) & 0x3ffff;
  186. }
  187. for ($i = 0; $i < 16; $i++) {
  188. $b32[$i] = $b32[$i] + $x[$i];
  189. }
  190. $result = '';
  191. for ($i = 0; $i < 16; $i++) {
  192. $result .= pack("V", $b32[$i]);
  193. }
  194. return $result;
  195. }
  196. /**
  197. * Salsa 20/8 core (64 bit version)
  198. *
  199. * @param string $b
  200. * @return string
  201. * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-2
  202. * @see http://cr.yp.to/salsa20.html
  203. */
  204. protected static function salsa208Core64($b)
  205. {
  206. $b32 = array();
  207. for ($i = 0; $i < 16; $i++) {
  208. list(, $b32[$i]) = unpack("V", substr($b, $i * 4, 4));
  209. }
  210. $x = $b32;
  211. for ($i = 0; $i < 8; $i += 2) {
  212. $a = ($x[ 0] + $x[12]) & 0xffffffff;
  213. $x[ 4] ^= ($a << 7) | ($a >> 25);
  214. $a = ($x[ 4] + $x[ 0]) & 0xffffffff;
  215. $x[ 8] ^= ($a << 9) | ($a >> 23);
  216. $a = ($x[ 8] + $x[ 4]) & 0xffffffff;
  217. $x[12] ^= ($a << 13) | ($a >> 19);
  218. $a = ($x[12] + $x[ 8]) & 0xffffffff;
  219. $x[ 0] ^= ($a << 18) | ($a >> 14);
  220. $a = ($x[ 5] + $x[ 1]) & 0xffffffff;
  221. $x[ 9] ^= ($a << 7) | ($a >> 25);
  222. $a = ($x[ 9] + $x[ 5]) & 0xffffffff;
  223. $x[13] ^= ($a << 9) | ($a >> 23);
  224. $a = ($x[13] + $x[ 9]) & 0xffffffff;
  225. $x[ 1] ^= ($a << 13) | ($a >> 19);
  226. $a = ($x[ 1] + $x[13]) & 0xffffffff;
  227. $x[ 5] ^= ($a << 18) | ($a >> 14);
  228. $a = ($x[10] + $x[ 6]) & 0xffffffff;
  229. $x[14] ^= ($a << 7) | ($a >> 25);
  230. $a = ($x[14] + $x[10]) & 0xffffffff;
  231. $x[ 2] ^= ($a << 9) | ($a >> 23);
  232. $a = ($x[ 2] + $x[14]) & 0xffffffff;
  233. $x[ 6] ^= ($a << 13) | ($a >> 19);
  234. $a = ($x[ 6] + $x[ 2]) & 0xffffffff;
  235. $x[10] ^= ($a << 18) | ($a >> 14);
  236. $a = ($x[15] + $x[11]) & 0xffffffff;
  237. $x[ 3] ^= ($a << 7) | ($a >> 25);
  238. $a = ($x[ 3] + $x[15]) & 0xffffffff;
  239. $x[ 7] ^= ($a << 9) | ($a >> 23);
  240. $a = ($x[ 7] + $x[ 3]) & 0xffffffff;
  241. $x[11] ^= ($a << 13) | ($a >> 19);
  242. $a = ($x[11] + $x[ 7]) & 0xffffffff;
  243. $x[15] ^= ($a << 18) | ($a >> 14);
  244. $a = ($x[ 0] + $x[ 3]) & 0xffffffff;
  245. $x[ 1] ^= ($a << 7) | ($a >> 25);
  246. $a = ($x[ 1] + $x[ 0]) & 0xffffffff;
  247. $x[ 2] ^= ($a << 9) | ($a >> 23);
  248. $a = ($x[ 2] + $x[ 1]) & 0xffffffff;
  249. $x[ 3] ^= ($a << 13) | ($a >> 19);
  250. $a = ($x[ 3] + $x[ 2]) & 0xffffffff;
  251. $x[ 0] ^= ($a << 18) | ($a >> 14);
  252. $a = ($x[ 5] + $x[ 4]) & 0xffffffff;
  253. $x[ 6] ^= ($a << 7) | ($a >> 25);
  254. $a = ($x[ 6] + $x[ 5]) & 0xffffffff;
  255. $x[ 7] ^= ($a << 9) | ($a >> 23);
  256. $a = ($x[ 7] + $x[ 6]) & 0xffffffff;
  257. $x[ 4] ^= ($a << 13) | ($a >> 19);
  258. $a = ($x[ 4] + $x[ 7]) & 0xffffffff;
  259. $x[ 5] ^= ($a << 18) | ($a >> 14);
  260. $a = ($x[10] + $x[ 9]) & 0xffffffff;
  261. $x[11] ^= ($a << 7) | ($a >> 25);
  262. $a = ($x[11] + $x[10]) & 0xffffffff;
  263. $x[ 8] ^= ($a << 9) | ($a >> 23);
  264. $a = ($x[ 8] + $x[11]) & 0xffffffff;
  265. $x[ 9] ^= ($a << 13) | ($a >> 19);
  266. $a = ($x[ 9] + $x[ 8]) & 0xffffffff;
  267. $x[10] ^= ($a << 18) | ($a >> 14);
  268. $a = ($x[15] + $x[14]) & 0xffffffff;
  269. $x[12] ^= ($a << 7) | ($a >> 25);
  270. $a = ($x[12] + $x[15]) & 0xffffffff;
  271. $x[13] ^= ($a << 9) | ($a >> 23);
  272. $a = ($x[13] + $x[12]) & 0xffffffff;
  273. $x[14] ^= ($a << 13) | ($a >> 19);
  274. $a = ($x[14] + $x[13]) & 0xffffffff;
  275. $x[15] ^= ($a << 18) | ($a >> 14);
  276. }
  277. for ($i = 0; $i < 16; $i++) {
  278. $b32[$i] = ($b32[$i] + $x[$i]) & 0xffffffff;
  279. }
  280. $result = '';
  281. for ($i = 0; $i < 16; $i++) {
  282. $result .= pack("V", $b32[$i]);
  283. }
  284. return $result;
  285. }
  286. /**
  287. * Integerify
  288. *
  289. * Integerify (B[0] ... B[2 * r - 1]) is defined as the result
  290. * of interpreting B[2 * r - 1] as a little-endian integer.
  291. * Each block B is a string of 64 bytes.
  292. *
  293. * @param string $b
  294. * @return int
  295. * @see https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-4
  296. */
  297. protected static function integerify($b)
  298. {
  299. $v = 'v';
  300. if (PHP_INT_SIZE === 8) {
  301. $v = 'V';
  302. }
  303. list(,$n) = unpack($v, substr($b, -64));
  304. return $n;
  305. }
  306. /**
  307. * Convert hex string in a binary string
  308. *
  309. * @param string $hex
  310. * @return string
  311. */
  312. protected static function hex2bin($hex)
  313. {
  314. if (version_compare(PHP_VERSION, '5.4') >= 0) {
  315. return hex2bin($hex);
  316. }
  317. $len = strlen($hex);
  318. $result = '';
  319. for ($i = 0; $i < $len; $i+=2) {
  320. $result .= chr(hexdec($hex[$i] . $hex[$i+1]));
  321. }
  322. return $result;
  323. }
  324. }