Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

478 lines
18 KiB

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Pure-PHP implementation of AES.
  5. *
  6. * Uses mcrypt, if available, and an internal implementation, otherwise.
  7. *
  8. * PHP versions 4 and 5
  9. *
  10. * If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
  11. * {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
  12. * it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()}
  13. * is called, again, at which point, it'll be recalculated.
  14. *
  15. * Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't
  16. * make a whole lot of sense. {@link Crypt_AES::setBlockLength() setBlockLength()}, for instance. Calling that function,
  17. * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
  18. *
  19. * Here's a short example of how to use this library:
  20. * <code>
  21. * <?php
  22. * include('Crypt/AES.php');
  23. *
  24. * $aes = new Crypt_AES();
  25. *
  26. * $aes->setKey('abcdefghijklmnop');
  27. *
  28. * $size = 10 * 1024;
  29. * $plaintext = '';
  30. * for ($i = 0; $i < $size; $i++) {
  31. * $plaintext.= 'a';
  32. * }
  33. *
  34. * echo $aes->decrypt($aes->encrypt($plaintext));
  35. * ?>
  36. * </code>
  37. *
  38. * LICENSE: This library is free software; you can redistribute it and/or
  39. * modify it under the terms of the GNU Lesser General Public
  40. * License as published by the Free Software Foundation; either
  41. * version 2.1 of the License, or (at your option) any later version.
  42. *
  43. * This library is distributed in the hope that it will be useful,
  44. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  45. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  46. * Lesser General Public License for more details.
  47. *
  48. * You should have received a copy of the GNU Lesser General Public
  49. * License along with this library; if not, write to the Free Software
  50. * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  51. * MA 02111-1307 USA
  52. *
  53. * @category Crypt
  54. * @package Crypt_AES
  55. * @author Jim Wigginton <terrafrost@php.net>
  56. * @copyright MMVIII Jim Wigginton
  57. * @license http://www.gnu.org/licenses/lgpl.txt
  58. * @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $
  59. * @link http://phpseclib.sourceforge.net
  60. */
  61. /**
  62. * Include Crypt_Rijndael
  63. */
  64. require_once 'Rijndael.php';
  65. /**#@+
  66. * @access public
  67. * @see Crypt_AES::encrypt()
  68. * @see Crypt_AES::decrypt()
  69. */
  70. /**
  71. * Encrypt / decrypt using the Counter mode.
  72. *
  73. * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
  74. *
  75. * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
  76. */
  77. define('CRYPT_AES_MODE_CTR', -1);
  78. /**
  79. * Encrypt / decrypt using the Electronic Code Book mode.
  80. *
  81. * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
  82. */
  83. define('CRYPT_AES_MODE_ECB', 1);
  84. /**
  85. * Encrypt / decrypt using the Code Book Chaining mode.
  86. *
  87. * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
  88. */
  89. define('CRYPT_AES_MODE_CBC', 2);
  90. /**#@-*/
  91. /**#@+
  92. * @access private
  93. * @see Crypt_AES::Crypt_AES()
  94. */
  95. /**
  96. * Toggles the internal implementation
  97. */
  98. define('CRYPT_AES_MODE_INTERNAL', 1);
  99. /**
  100. * Toggles the mcrypt implementation
  101. */
  102. define('CRYPT_AES_MODE_MCRYPT', 2);
  103. /**#@-*/
  104. /**
  105. * Pure-PHP implementation of AES.
  106. *
  107. * @author Jim Wigginton <terrafrost@php.net>
  108. * @version 0.1.0
  109. * @access public
  110. * @package Crypt_AES
  111. */
  112. class Crypt_AES extends Crypt_Rijndael {
  113. /**
  114. * mcrypt resource for encryption
  115. *
  116. * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
  117. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
  118. *
  119. * @see Crypt_AES::encrypt()
  120. * @var String
  121. * @access private
  122. */
  123. var $enmcrypt;
  124. /**
  125. * mcrypt resource for decryption
  126. *
  127. * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
  128. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
  129. *
  130. * @see Crypt_AES::decrypt()
  131. * @var String
  132. * @access private
  133. */
  134. var $demcrypt;
  135. /**
  136. * Default Constructor.
  137. *
  138. * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
  139. * CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used.
  140. *
  141. * @param optional Integer $mode
  142. * @return Crypt_AES
  143. * @access public
  144. */
  145. function Crypt_AES($mode = CRYPT_AES_MODE_CBC)
  146. {
  147. if ( !defined('CRYPT_AES_MODE') ) {
  148. switch (true) {
  149. case extension_loaded('mcrypt'):
  150. // i'd check to see if aes was supported, by doing in_array('des', mcrypt_list_algorithms('')),
  151. // but since that can be changed after the object has been created, there doesn't seem to be
  152. // a lot of point...
  153. define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT);
  154. break;
  155. default:
  156. define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL);
  157. }
  158. }
  159. switch ( CRYPT_AES_MODE ) {
  160. case CRYPT_AES_MODE_MCRYPT:
  161. switch ($mode) {
  162. case CRYPT_AES_MODE_ECB:
  163. $this->mode = MCRYPT_MODE_ECB;
  164. break;
  165. case CRYPT_AES_MODE_CTR:
  166. // ctr doesn't have a constant associated with it even though it appears to be fairly widely
  167. // supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to
  168. // include a compatibility layer. the layer has been implemented but, for now, is commented out.
  169. $this->mode = 'ctr';
  170. //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
  171. break;
  172. case CRYPT_AES_MODE_CBC:
  173. default:
  174. $this->mode = MCRYPT_MODE_CBC;
  175. }
  176. break;
  177. default:
  178. switch ($mode) {
  179. case CRYPT_AES_MODE_ECB:
  180. $this->mode = CRYPT_RIJNDAEL_MODE_ECB;
  181. break;
  182. case CRYPT_AES_MODE_CTR:
  183. $this->mode = CRYPT_RIJNDAEL_MODE_CTR;
  184. break;
  185. case CRYPT_AES_MODE_CBC:
  186. default:
  187. $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
  188. }
  189. }
  190. if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) {
  191. parent::Crypt_Rijndael($this->mode);
  192. }
  193. }
  194. /**
  195. * Dummy function
  196. *
  197. * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
  198. *
  199. * @access public
  200. * @param Integer $length
  201. */
  202. function setBlockLength($length)
  203. {
  204. return;
  205. }
  206. /**
  207. * Encrypts a message.
  208. *
  209. * $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the
  210. * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
  211. * URL:
  212. *
  213. * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
  214. *
  215. * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
  216. * strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that
  217. * length.
  218. *
  219. * @see Crypt_AES::decrypt()
  220. * @access public
  221. * @param String $plaintext
  222. */
  223. function encrypt($plaintext)
  224. {
  225. if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
  226. $this->_mcryptSetup();
  227. /*
  228. if ($this->mode == CRYPT_AES_MODE_CTR) {
  229. $iv = $this->encryptIV;
  230. $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($plaintext), $iv));
  231. $ciphertext = $plaintext ^ $xor;
  232. if ($this->continuousBuffer) {
  233. $this->encryptIV = $iv;
  234. }
  235. return $ciphertext;
  236. }
  237. */
  238. if ($this->mode != 'ctr') {
  239. $plaintext = $this->_pad($plaintext);
  240. }
  241. $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
  242. if (!$this->continuousBuffer) {
  243. mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
  244. }
  245. return $ciphertext;
  246. }
  247. return parent::encrypt($plaintext);
  248. }
  249. /**
  250. * Decrypts a message.
  251. *
  252. * If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is.
  253. *
  254. * @see Crypt_AES::encrypt()
  255. * @access public
  256. * @param String $ciphertext
  257. */
  258. function decrypt($ciphertext)
  259. {
  260. if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
  261. $this->_mcryptSetup();
  262. /*
  263. if ($this->mode == CRYPT_AES_MODE_CTR) {
  264. $iv = $this->decryptIV;
  265. $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($ciphertext), $iv));
  266. $plaintext = $ciphertext ^ $xor;
  267. if ($this->continuousBuffer) {
  268. $this->decryptIV = $iv;
  269. }
  270. return $plaintext;
  271. }
  272. */
  273. if ($this->mode != 'ctr') {
  274. // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
  275. // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
  276. $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));
  277. }
  278. $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
  279. if (!$this->continuousBuffer) {
  280. mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
  281. }
  282. return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
  283. }
  284. return parent::decrypt($ciphertext);
  285. }
  286. /**
  287. * Setup mcrypt
  288. *
  289. * Validates all the variables.
  290. *
  291. * @access private
  292. */
  293. function _mcryptSetup()
  294. {
  295. if (!$this->changed) {
  296. return;
  297. }
  298. if (!$this->explicit_key_length) {
  299. // this just copied from Crypt_Rijndael::_setup()
  300. $length = strlen($this->key) >> 2;
  301. if ($length > 8) {
  302. $length = 8;
  303. } else if ($length < 4) {
  304. $length = 4;
  305. }
  306. $this->Nk = $length;
  307. $this->key_size = $length << 2;
  308. }
  309. switch ($this->Nk) {
  310. case 4: // 128
  311. $this->key_size = 16;
  312. break;
  313. case 5: // 160
  314. case 6: // 192
  315. $this->key_size = 24;
  316. break;
  317. case 7: // 224
  318. case 8: // 256
  319. $this->key_size = 32;
  320. }
  321. $this->key = substr($this->key, 0, $this->key_size);
  322. $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
  323. if (!isset($this->enmcrypt)) {
  324. $mode = $this->mode;
  325. //$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;
  326. $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
  327. $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
  328. } // else should mcrypt_generic_deinit be called?
  329. mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
  330. mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
  331. $this->changed = false;
  332. }
  333. /**
  334. * Encrypts a block
  335. *
  336. * Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
  337. *
  338. * @see Crypt_Rijndael::_encryptBlock()
  339. * @access private
  340. * @param String $in
  341. * @return String
  342. */
  343. function _encryptBlock($in)
  344. {
  345. $state = unpack('N*word', $in);
  346. $Nr = $this->Nr;
  347. $w = $this->w;
  348. $t0 = $this->t0;
  349. $t1 = $this->t1;
  350. $t2 = $this->t2;
  351. $t3 = $this->t3;
  352. // addRoundKey and reindex $state
  353. $state = array(
  354. $state['word1'] ^ $w[0][0],
  355. $state['word2'] ^ $w[0][1],
  356. $state['word3'] ^ $w[0][2],
  357. $state['word4'] ^ $w[0][3]
  358. );
  359. // shiftRows + subWord + mixColumns + addRoundKey
  360. // we could loop unroll this and use if statements to do more rounds as necessary, but, in my tests, that yields
  361. // only a marginal improvement. since that also, imho, hinders the readability of the code, i've opted not to do it.
  362. for ($round = 1; $round < $this->Nr; $round++) {
  363. $state = array(
  364. $t0[$state[0] & 0xFF000000] ^ $t1[$state[1] & 0x00FF0000] ^ $t2[$state[2] & 0x0000FF00] ^ $t3[$state[3] & 0x000000FF] ^ $w[$round][0],
  365. $t0[$state[1] & 0xFF000000] ^ $t1[$state[2] & 0x00FF0000] ^ $t2[$state[3] & 0x0000FF00] ^ $t3[$state[0] & 0x000000FF] ^ $w[$round][1],
  366. $t0[$state[2] & 0xFF000000] ^ $t1[$state[3] & 0x00FF0000] ^ $t2[$state[0] & 0x0000FF00] ^ $t3[$state[1] & 0x000000FF] ^ $w[$round][2],
  367. $t0[$state[3] & 0xFF000000] ^ $t1[$state[0] & 0x00FF0000] ^ $t2[$state[1] & 0x0000FF00] ^ $t3[$state[2] & 0x000000FF] ^ $w[$round][3]
  368. );
  369. }
  370. // subWord
  371. $state = array(
  372. $this->_subWord($state[0]),
  373. $this->_subWord($state[1]),
  374. $this->_subWord($state[2]),
  375. $this->_subWord($state[3])
  376. );
  377. // shiftRows + addRoundKey
  378. $state = array(
  379. ($state[0] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[3] & 0x000000FF) ^ $this->w[$this->Nr][0],
  380. ($state[1] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[0] & 0x000000FF) ^ $this->w[$this->Nr][1],
  381. ($state[2] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[1] & 0x000000FF) ^ $this->w[$this->Nr][2],
  382. ($state[3] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[2] & 0x000000FF) ^ $this->w[$this->Nr][3]
  383. );
  384. return pack('N*', $state[0], $state[1], $state[2], $state[3]);
  385. }
  386. /**
  387. * Decrypts a block
  388. *
  389. * Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
  390. *
  391. * @see Crypt_Rijndael::_decryptBlock()
  392. * @access private
  393. * @param String $in
  394. * @return String
  395. */
  396. function _decryptBlock($in)
  397. {
  398. $state = unpack('N*word', $in);
  399. $Nr = $this->Nr;
  400. $dw = $this->dw;
  401. $dt0 = $this->dt0;
  402. $dt1 = $this->dt1;
  403. $dt2 = $this->dt2;
  404. $dt3 = $this->dt3;
  405. // addRoundKey and reindex $state
  406. $state = array(
  407. $state['word1'] ^ $dw[$this->Nr][0],
  408. $state['word2'] ^ $dw[$this->Nr][1],
  409. $state['word3'] ^ $dw[$this->Nr][2],
  410. $state['word4'] ^ $dw[$this->Nr][3]
  411. );
  412. // invShiftRows + invSubBytes + invMixColumns + addRoundKey
  413. for ($round = $this->Nr - 1; $round > 0; $round--) {
  414. $state = array(
  415. $dt0[$state[0] & 0xFF000000] ^ $dt1[$state[3] & 0x00FF0000] ^ $dt2[$state[2] & 0x0000FF00] ^ $dt3[$state[1] & 0x000000FF] ^ $dw[$round][0],
  416. $dt0[$state[1] & 0xFF000000] ^ $dt1[$state[0] & 0x00FF0000] ^ $dt2[$state[3] & 0x0000FF00] ^ $dt3[$state[2] & 0x000000FF] ^ $dw[$round][1],
  417. $dt0[$state[2] & 0xFF000000] ^ $dt1[$state[1] & 0x00FF0000] ^ $dt2[$state[0] & 0x0000FF00] ^ $dt3[$state[3] & 0x000000FF] ^ $dw[$round][2],
  418. $dt0[$state[3] & 0xFF000000] ^ $dt1[$state[2] & 0x00FF0000] ^ $dt2[$state[1] & 0x0000FF00] ^ $dt3[$state[0] & 0x000000FF] ^ $dw[$round][3]
  419. );
  420. }
  421. // invShiftRows + invSubWord + addRoundKey
  422. $state = array(
  423. $this->_invSubWord(($state[0] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[1] & 0x000000FF)) ^ $dw[0][0],
  424. $this->_invSubWord(($state[1] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[2] & 0x000000FF)) ^ $dw[0][1],
  425. $this->_invSubWord(($state[2] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[3] & 0x000000FF)) ^ $dw[0][2],
  426. $this->_invSubWord(($state[3] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[0] & 0x000000FF)) ^ $dw[0][3]
  427. );
  428. return pack('N*', $state[0], $state[1], $state[2], $state[3]);
  429. }
  430. }
  431. // vim: ts=4:sw=4:et:
  432. // vim6: fdl=1: