PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/Applications/Shadowsocks/Encryptor.php

https://gitlab.com/shinvdu/shadowsocks-php
PHP | 349 lines | 327 code | 6 blank | 16 comment | 44 complexity | 1c4880cb4affedaf9e90fb18836d0858 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of workerman.
  4. *
  5. * Licensed under The MIT License
  6. * For full copyright and license information, please see the MIT-LICENSE.txt
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @author walkor<walkor@workerman.net>
  10. * @copyright walkor<walkor@workerman.net>
  11. * @link http://www.workerman.net/
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. /**
  15. * 加密解密类
  16. * @author walkor<walkor@workerman.net>
  17. */
  18. class Encryptor
  19. {
  20. protected $_key;
  21. protected $_method;
  22. protected $_cipher;
  23. protected $_decipher;
  24. protected $_bytesToKeyResults = array();
  25. protected static $_cachedTables = array();
  26. protected static $_encryptTable = array();
  27. protected static $_decryptTable = array();
  28. protected $_cipherIv;
  29. protected $_ivSent;
  30. protected static $_methodSupported = array(
  31. 'aes-128-cfb'=> array(16, 16),
  32. 'aes-192-cfb'=> array(24, 16),
  33. 'aes-256-cfb'=> array(32, 16),
  34. 'bf-cfb'=> array(16, 8),
  35. 'camellia-128-cfb'=> array(16, 16),
  36. 'camellia-192-cfb'=> array(24, 16),
  37. 'camellia-256-cfb'=> array(32, 16),
  38. 'cast5-cfb'=> array(16, 8),
  39. 'des-cfb'=> array(8, 8),
  40. 'idea-cfb'=>array(16, 8),
  41. 'rc2-cfb'=> array(16, 8),
  42. 'rc4'=> array(16, 0),
  43. 'rc4-md5'=> array(16, 16),
  44. 'seed-cfb'=> array(16, 16)
  45. );
  46. public static function initTable($key)
  47. {
  48. $_ref = self::getTable($key);
  49. self::$_encryptTable = $_ref[0];
  50. self::$_decryptTable = $_ref[1];
  51. }
  52. public function __construct($key, $method)
  53. {
  54. $this->_key = $key;
  55. $this->_method = $method;
  56. if($this->_method == 'table')
  57. {
  58. $this->_method = null;
  59. }
  60. if($this->_method)
  61. {
  62. $iv_size = openssl_cipher_iv_length($this->_method);
  63. $iv = openssl_random_pseudo_bytes($iv_size);
  64. $this->_cipher = $this->getcipher($this->_key, $this->_method, 1, $iv);
  65. }
  66. else
  67. {
  68. if(!self::$_encryptTable)
  69. {
  70. $_ref = self::getTable($this->_key);
  71. self::$_encryptTable = $_ref[0];
  72. self::$_decryptTable = $_ref[1];
  73. }
  74. }
  75. }
  76. protected static function getTable($key)
  77. {
  78. if (isset(self::$_cachedTables[$key]))
  79. {
  80. return self::$_cachedTables[$key];
  81. }
  82. $int32Max = pow(2, 32);
  83. $table = array();
  84. $decrypt_table = array();
  85. $hash = md5($key, true);
  86. $tmp = unpack('V2', $hash);
  87. $al = $tmp[1];
  88. $ah = $tmp[2];
  89. $i = 0;
  90. while ($i < 256) {
  91. $table[$i] = $i;
  92. $i++;
  93. }
  94. $i = 1;
  95. while ($i < 1024) {
  96. $table = merge_sort($table, function($x, $y)use($ah, $al, $i, $int32Max) {
  97. return (($ah % ($x + $i)) * $int32Max + $al) % ($x + $i) - (($ah % ($y + $i)) * $int32Max + $al) % ($y + $i);
  98. });
  99. $i++;
  100. }
  101. $table = array_values($table);
  102. $i = 0;
  103. while ($i < 256) {
  104. $decrypt_table[$table[$i]] = $i;
  105. ++$i;
  106. }
  107. ksort($decrypt_table);
  108. $decrypt_table = array_values($decrypt_table);
  109. $result = array($table, $decrypt_table);
  110. self::$_cachedTables[$key] = $result;
  111. return $result;
  112. }
  113. public static function substitute($table, $buf)
  114. {
  115. $i = 0;
  116. $len = strlen($buf);
  117. while ($i < $len) {
  118. $buf[$i] = chr($table[ord($buf[$i])]);
  119. $i++;
  120. }
  121. return $buf;
  122. }
  123. protected function getCipher($password, $method, $op, $iv)
  124. {
  125. $method = strtolower($method);
  126. $m = $this->getCipherLen($method);
  127. if($m)
  128. {
  129. $ref = $this->EVPBytesToKey($password, $m[0], $m[1]);
  130. $key = $ref[0];
  131. $iv_ = $ref[1];
  132. if ($iv == null)
  133. {
  134. $iv = $iv_;
  135. }
  136. if ($op === 1)
  137. {
  138. $this->_cipherIv = substr($iv, 0, $m[1]);
  139. }
  140. $iv = substr($iv, 0, $m[1]);
  141. if ($method === 'rc4-md5')
  142. {
  143. return createRc4Md5Cipher($key, $iv, $op);
  144. }
  145. else
  146. {
  147. if($op === 1)
  148. {
  149. return new Encipher($method, $key, $iv);
  150. }
  151. else
  152. {
  153. return new Decipher($method, $key, $iv);
  154. }
  155. }
  156. }
  157. }
  158. public function encrypt($buffer)
  159. {
  160. if($this->_method)
  161. {
  162. $result = $this->_cipher->update($buffer);
  163. if ($this->_ivSent)
  164. {
  165. return $result;
  166. }
  167. else
  168. {
  169. $this->_ivSent = true;
  170. return $this->_cipherIv.$result;
  171. }
  172. }
  173. else
  174. {
  175. return self::substitute(self::$_encryptTable, $buffer);
  176. }
  177. }
  178. public function decrypt($buffer)
  179. {
  180. if($this->_method)
  181. {
  182. if(!$this->_decipher)
  183. {
  184. $decipher_iv_len = $this->getCipherLen($this->_method);
  185. $decipher_iv_len = $decipher_iv_len[1];
  186. $decipher_iv = substr($buffer, 0, $decipher_iv_len);
  187. $this->_decipher = $this->getCipher($this->_key, $this->_method, 0, $decipher_iv);
  188. $result = $this->_decipher->update(substr($buffer, $decipher_iv_len));
  189. return $result;
  190. }
  191. else
  192. {
  193. $result = $this->_decipher->update($buffer);
  194. return $result;
  195. }
  196. }
  197. else
  198. {
  199. return self::substitute(self::$_decryptTable, $buffer);
  200. }
  201. }
  202. protected function createRc4Md5Cipher($key, $iv, $op)
  203. {
  204. $rc4_key = md5($key.$iv);
  205. if($op === 1)
  206. {
  207. return new Encipher('rc4', $rc4_key, '');
  208. }
  209. else
  210. {
  211. return Decipher('rc4', $rc4_key, '');
  212. }
  213. }
  214. protected function EVPBytesToKey($password, $key_len, $iv_len)
  215. {
  216. $cache_key = "$password:$key_len:$iv_len";
  217. if(isset($this->_bytesToKeyResults[$cache_key]))
  218. {
  219. return $this->_bytesToKeyResults[$cache_key];
  220. }
  221. $m = array();
  222. $i = 0;
  223. $count = 0;
  224. while ($count < $key_len + $iv_len)
  225. {
  226. $data = $password;
  227. if ($i > 0)
  228. {
  229. $data = $m[$i-1] . $password;
  230. }
  231. $d = md5($data, true);
  232. $m[] = $d;
  233. $count += strlen($d);
  234. $i += 1;
  235. }
  236. $ms = '';
  237. foreach($m as $buf)
  238. {
  239. $ms .= $buf;
  240. }
  241. $key = substr($ms, 0, $key_len);
  242. $iv = substr($ms, $key_len, $key_len + $iv_len);
  243. $this->bytesToKeyResults[$password] = array($key, $iv);
  244. return array($key, $iv);
  245. }
  246. protected function getCipherLen($method)
  247. {
  248. $method = strtolower($method);
  249. return isset(self::$_methodSupported[$method]) ? self::$_methodSupported[$method] : null;
  250. }
  251. }
  252. class Encipher
  253. {
  254. protected $_algorithm;
  255. protected $_key;
  256. protected $_iv;
  257. protected $_tail;
  258. protected $_ivLength;
  259. public function __construct($algorithm, $key, $iv)
  260. {
  261. $this->_algorithm = $algorithm;
  262. $this->_key = $key;
  263. $this->_iv = $iv;
  264. $this->_ivLength = openssl_cipher_iv_length($algorithm);
  265. }
  266. public function update($data)
  267. {
  268. if (strlen($data) == 0)
  269. return '';
  270. $tl = strlen($this->_tail);
  271. if ($tl)
  272. $data = $this->_tail . $data;
  273. $b = openssl_encrypt($data, $this->_algorithm, $this->_key, OPENSSL_RAW_DATA, $this->_iv);
  274. $result = substr($b, $tl);
  275. $dataLength = strlen($data);
  276. $mod = $dataLength%$this->_ivLength;
  277. if ($dataLength >= $this->_ivLength) {
  278. $iPos = -($mod + $this->_ivLength);
  279. $this->_iv = substr($b, $iPos, $this->_ivLength);
  280. }
  281. $this->_tail = $mod!=0 ? substr($data, -$mod):'';
  282. return $result;
  283. }
  284. }
  285. class Decipher extends Encipher
  286. {
  287. public function update($data)
  288. {
  289. if (strlen($data) == 0)
  290. return '';
  291. $tl = strlen($this->_tail);
  292. if ($tl)
  293. $data = $this->_tail . $data;
  294. $b = openssl_decrypt($data, $this->_algorithm, $this->_key, OPENSSL_RAW_DATA, $this->_iv);
  295. $result = substr($b, $tl);
  296. $dataLength = strlen($data);
  297. $mod = $dataLength%$this->_ivLength;
  298. if ($dataLength >= $this->_ivLength) {
  299. $iPos = -($mod + $this->_ivLength);
  300. $this->_iv = substr($data, $iPos, $this->_ivLength);
  301. }
  302. $this->_tail = $mod!=0 ? substr($data, -$mod):'';
  303. return $result;
  304. }
  305. }
  306. function merge_sort($array, $comparison)
  307. {
  308. if (count($array) < 2) {
  309. return $array;
  310. }
  311. $middle = ceil(count($array) / 2);
  312. return merge(merge_sort(slice($array, 0, $middle), $comparison), merge_sort(slice($array, $middle), $comparison), $comparison);
  313. }
  314. function slice($table, $start, $end = null)
  315. {
  316. $table = array_values($table);
  317. if($end)
  318. {
  319. return array_slice($table, $start, $end);
  320. }
  321. else
  322. {
  323. return array_slice($table, $start);
  324. }
  325. }
  326. function merge($left, $right, $comparison)
  327. {
  328. $result = array();
  329. while ((count($left) > 0) && (count($right) > 0)) {
  330. if(call_user_func($comparison, $left[0], $right[0]) <= 0){
  331. $result[] = array_shift($left);
  332. } else {
  333. $result[] = array_shift($right);
  334. }
  335. }
  336. while (count($left) > 0) {
  337. $result[] = array_shift($left);
  338. }
  339. while (count($right) > 0) {
  340. $result[] = array_shift($right);
  341. }
  342. return $result;
  343. }