/includes/gateways/direct-debit/libraries/php-encryption/Core.php

  1. <?php 
  2.  
  3. namespace Defuse\Crypto; 
  4.  
  5. use Defuse\Crypto\Exception as Ex; 
  6.  
  7. final class Core 
  8. const HEADER_VERSION_SIZE = 4; 
  9. const MINIMUM_CIPHERTEXT_SIZE = 84; 
  10.  
  11. const CURRENT_VERSION = "\xDE\xF5\x02\x00"; 
  12.  
  13. const CIPHER_METHOD = 'aes-256-ctr'; 
  14. const BLOCK_BYTE_SIZE = 16; 
  15. const KEY_BYTE_SIZE = 32; 
  16. const SALT_BYTE_SIZE = 32; 
  17. const MAC_BYTE_SIZE = 32; 
  18. const HASH_FUNCTION_NAME = 'sha256'; 
  19. const ENCRYPTION_INFO_STRING = 'DefusePHP|V2|KeyForEncryption'; 
  20. const AUTHENTICATION_INFO_STRING = 'DefusePHP|V2|KeyForAuthentication'; 
  21. const BUFFER_BYTE_SIZE = 1048576; 
  22.  
  23. const LEGACY_CIPHER_METHOD = 'aes-128-cbc'; 
  24. const LEGACY_BLOCK_BYTE_SIZE = 16; 
  25. const LEGACY_KEY_BYTE_SIZE = 16; 
  26. const LEGACY_HASH_FUNCTION_NAME = 'sha256'; 
  27. const LEGACY_MAC_BYTE_SIZE = 32; 
  28. const LEGACY_ENCRYPTION_INFO_STRING = 'DefusePHP|KeyForEncryption'; 
  29. const LEGACY_AUTHENTICATION_INFO_STRING = 'DefusePHP|KeyForAuthentication'; 
  30.  
  31. /** 
  32. * V2.0 Format: VERSION (4 bytes) || SALT (32 bytes) || IV (16 bytes) || 
  33. * CIPHERTEXT (varies) || HMAC (32 bytes) 
  34. * 
  35. * V1.0 Format: HMAC (32 bytes) || IV (16 bytes) || CIPHERTEXT (varies). 
  36. */ 
  37.  
  38. /** 
  39. * Adds an integer to a block-sized counter. 
  40. * 
  41. * @param string $ctr 
  42. * @param int $inc 
  43. * 
  44. * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  45. * 
  46. * @return string 
  47. */ 
  48. public static function incrementCounter($ctr, $inc) 
  49. if (Core::ourStrlen($ctr) !== Core::BLOCK_BYTE_SIZE) { 
  50. throw new Ex\EnvironmentIsBrokenException( 
  51. 'Trying to increment a nonce of the wrong size.' 
  52. ); 
  53.  
  54. if (! \is_int($inc)) { 
  55. throw new Ex\EnvironmentIsBrokenException( 
  56. 'Trying to increment nonce by a non-integer.' 
  57. ); 
  58.  
  59. if ($inc < 0) { 
  60. throw new Ex\EnvironmentIsBrokenException( 
  61. 'Trying to increment nonce by a negative amount.' 
  62. ); 
  63.  
  64. if ($inc > PHP_INT_MAX - 255) { 
  65. throw new Ex\EnvironmentIsBrokenException( 
  66. 'Integer overflow may occur.' 
  67. ); 
  68.  
  69. /** 
  70. * We start at the rightmost byte (big-endian) 
  71. * So, too, does OpenSSL: http://stackoverflow.com/a/3146214/2224584 
  72. */ 
  73. for ($i = Core::BLOCK_BYTE_SIZE - 1; $i >= 0; --$i) { 
  74. $sum = \ord($ctr[$i]) + $inc; 
  75.  
  76. /** Detect integer overflow and fail. */ 
  77. if (! \is_int($sum)) { 
  78. throw new Ex\EnvironmentIsBrokenException( 
  79. 'Integer overflow in CTR mode nonce increment.' 
  80. ); 
  81.  
  82. $ctr[$i] = \pack('C', $sum & 0xFF); 
  83. $inc = $sum >> 8; 
  84. return $ctr; 
  85.  
  86. /** 
  87. * Returns a random byte string of the specified length. 
  88. * 
  89. * @param int $octets 
  90. * 
  91. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  92. * 
  93. * @return string 
  94. */ 
  95. public static function secureRandom($octets) 
  96. self::ensureFunctionExists('random_bytes'); 
  97. try { 
  98. return \random_bytes($octets); 
  99. } catch (Exception $ex) { 
  100. throw new Ex\EnvironmentIsBrokenException( 
  101. 'Your system does not have a secure random number generator.' 
  102. ); 
  103.  
  104. /** 
  105. * Computes the HKDF key derivation function specified in 
  106. * http://tools.ietf.org/html/rfc5869. 
  107. * 
  108. * @param string $hash Hash Function 
  109. * @param string $ikm Initial Keying Material 
  110. * @param int $length How many bytes? 
  111. * @param string $info What sort of key are we deriving? 
  112. * @param string $salt 
  113. * 
  114. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  115. * 
  116. * @return string 
  117. */ 
  118. public static function HKDF($hash, $ikm, $length, $info = '', $salt = null) 
  119. $digest_length = Core::ourStrlen(\hash_hmac($hash, '', '', true)); 
  120.  
  121. // Sanity-check the desired output length. 
  122. if (empty($length) || ! \is_int($length) || 
  123. $length < 0 || $length > 255 * $digest_length) { 
  124. throw new Ex\EnvironmentIsBrokenException( 
  125. 'Bad output length requested of HKDF.' 
  126. ); 
  127.  
  128. // "if [salt] not provided, is set to a string of HashLen zeroes." 
  129. if (\is_null($salt)) { 
  130. $salt = \str_repeat("\x00", $digest_length); 
  131.  
  132. // HKDF-Extract: 
  133. // PRK = HMAC-Hash(salt, IKM) 
  134. // The salt is the HMAC key. 
  135. $prk = \hash_hmac($hash, $ikm, $salt, true); 
  136.  
  137. // HKDF-Expand: 
  138.  
  139. // This check is useless, but it serves as a reminder to the spec. 
  140. if (Core::ourStrlen($prk) < $digest_length) { 
  141. throw new Ex\EnvironmentIsBrokenException(); 
  142.  
  143. // T(0) = '' 
  144. $t = ''; 
  145. $last_block = ''; 
  146. for ($block_index = 1; Core::ourStrlen($t) < $length; ++$block_index) { 
  147. // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??) 
  148. $last_block = \hash_hmac( 
  149. $hash,  
  150. $last_block . $info . \chr($block_index),  
  151. $prk,  
  152. true 
  153. ); 
  154. // T = T(1) | T(2) | T(3) | ... | T(N) 
  155. $t .= $last_block; 
  156.  
  157. // ORM = first L octets of T 
  158. $orm = Core::ourSubstr($t, 0, $length); 
  159. if ($orm === false) { 
  160. throw new Ex\EnvironmentIsBrokenException(); 
  161. return $orm; 
  162.  
  163. /** 
  164. * Checks if two equal-length strings are the same without leaking 
  165. * information through side channels. 
  166. * 
  167. * @param string $expected 
  168. * @param string $given 
  169. * 
  170. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  171. * 
  172. * @return bool 
  173. */ 
  174. public static function hashEquals($expected, $given) 
  175. static $native = null; 
  176. if ($native === null) { 
  177. $native = \function_exists('hash_equals'); 
  178. if ($native) { 
  179. return \hash_equals($expected, $given); 
  180.  
  181. // We can't just compare the strings with '==', since it would make 
  182. // timing attacks possible. We could use the XOR-OR constant-time 
  183. // comparison algorithm, but that may not be a reliable defense in an 
  184. // interpreted language. So we use the approach of HMACing both strings 
  185. // with a random key and comparing the HMACs. 
  186.  
  187. // We're not attempting to make variable-length string comparison 
  188. // secure, as that's very difficult. Make sure the strings are the same 
  189. // length. 
  190. if (Core::ourStrlen($expected) !== Core::ourStrlen($given)) { 
  191. throw new Ex\EnvironmentIsBrokenException(); 
  192.  
  193. $blind = Core::secureRandom(32); 
  194. $message_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $given, $blind); 
  195. $correct_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $expected, $blind); 
  196. return $correct_compare === $message_compare; 
  197. /** 
  198. * Throws an exception if the constant doesn't exist. 
  199. * 
  200. * @param string $name 
  201. * 
  202. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  203. */ 
  204. public static function ensureConstantExists($name) 
  205. if (! \defined($name)) { 
  206. throw new Ex\EnvironmentIsBrokenException(); 
  207.  
  208. /** 
  209. * Throws an exception if the function doesn't exist. 
  210. * 
  211. * @param string $name 
  212. * 
  213. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  214. */ 
  215. public static function ensureFunctionExists($name) 
  216. if (! \function_exists($name)) { 
  217. throw new Ex\EnvironmentIsBrokenException(); 
  218.  
  219. /** 
  220. * We need these strlen() and substr() functions because when 
  221. * 'mbstring.func_overload' is set in php.ini, the standard strlen() and 
  222. * substr() are replaced by mb_strlen() and mb_substr(). 
  223. */ 
  224.  
  225. /** 
  226. * Computes the length of a string in bytes. 
  227. * 
  228. * @param string $str 
  229. * 
  230. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  231. * 
  232. * @return int 
  233. */ 
  234. public static function ourStrlen($str) 
  235. static $exists = null; 
  236. if ($exists === null) { 
  237. $exists = \function_exists('mb_strlen'); 
  238. if ($exists) { 
  239. $length = \mb_strlen($str, '8bit'); 
  240. if ($length === false) { 
  241. throw new Ex\EnvironmentIsBrokenException(); 
  242. return $length; 
  243. } else { 
  244. return \strlen($str); 
  245.  
  246. /** 
  247. * Behaves roughly like the function substr() in PHP 7 does. 
  248. * 
  249. * @param string $str 
  250. * @param int $start 
  251. * @param int $length 
  252. * 
  253. * @throws Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  254. * 
  255. * @return string 
  256. */ 
  257. public static function ourSubstr($str, $start, $length = null) 
  258. static $exists = null; 
  259. if ($exists === null) { 
  260. $exists = \function_exists('mb_substr'); 
  261.  
  262. if ($exists) { 
  263. // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP 
  264. // 5.3, so we have to find the length ourselves. 
  265. if (! isset($length)) { 
  266. if ($start >= 0) { 
  267. $length = Core::ourStrlen($str) - $start; 
  268. } else { 
  269. $length = -$start; 
  270.  
  271. // This is required to make mb_substr behavior identical to substr. 
  272. // Without this, mb_substr() would return false, contra to what the 
  273. // PHP documentation says (it doesn't say it can return false.) 
  274. if ($start === Core::ourStrlen($str) && $length === 0) { 
  275. return ''; 
  276.  
  277. if ($start > Core::ourStrlen($str)) { 
  278. return false; 
  279.  
  280. $substr = \mb_substr($str, $start, $length, '8bit'); 
  281. if (Core::ourStrlen($substr) !== $length) { 
  282. throw new EnvironmentIsBrokenException( 
  283. 'Your version of PHP has bug #66797. Its implementation of 
  284. mb_substr() is incorrect. See the details here: 
  285. https://bugs.php.net/bug.php?id=66797' 
  286. ); 
  287. return $substr; 
  288.  
  289. // Unlike mb_substr(), substr() doesn't accept NULL for length 
  290. if (isset($length)) { 
  291. return \substr($str, $start, $length); 
  292. } else { 
  293. return \substr($str, $start); 
  294.  
  295. /** 
  296. * Computes the PBKDF2 password-based key derivation function. 
  297. * 
  298. * The PBKDF2 function is defined in RFC 2898. Test vectors can be found in 
  299. * RFC 6070. This implementation of PBKDF2 was originally created by Taylor 
  300. * Hornby, with improvements from http://www.variations-of-shadow.com/. 
  301. * 
  302. * @param string $algorithm The hash algorithm to use. Recommended: SHA256 
  303. * @param string $password The password. 
  304. * @param string $salt A salt that is unique to the password. 
  305. * @param int $count Iteration count. Higher is better, but slower. Recommended: At least 1000. 
  306. * @param int $key_length The length of the derived key in bytes. 
  307. * @param bool $raw_output If true, the key is returned in raw binary format. Hex encoded otherwise. 
  308. * 
  309. * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException 
  310. * 
  311. * @return string A $key_length-byte key derived from the password and salt. 
  312. */ 
  313. public static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) 
  314. // Type checks: 
  315. if (! \is_string($algorithm)) { 
  316. throw new \InvalidArgumentException( 
  317. 'pbkdf2(): algorithm must be a string' 
  318. ); 
  319. if (! \is_string($password)) { 
  320. throw new \InvalidArgumentException( 
  321. 'pbkdf2(): password must be a string' 
  322. ); 
  323. if (! \is_string($salt)) { 
  324. throw new \InvalidArgumentException( 
  325. 'pbkdf2(): salt must be a string' 
  326. ); 
  327. // Coerce strings to integers with no information loss or overflow 
  328. $count += 0; 
  329. $key_length += 0; 
  330.  
  331. $algorithm = \strtolower($algorithm); 
  332. if (! \in_array($algorithm, \hash_algos(), true)) { 
  333. throw new Ex\EnvironmentIsBrokenException( 
  334. 'Invalid or unsupported hash algorithm.' 
  335. ); 
  336.  
  337. // Whitelist, or we could end up with people using CRC32. 
  338. $ok_algorithms = [ 
  339. 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',  
  340. 'ripemd160', 'ripemd256', 'ripemd320', 'whirlpool',  
  341. ]; 
  342. if (! \in_array($algorithm, $ok_algorithms, true)) { 
  343. throw new Ex\EnvironmentIsBrokenException( 
  344. 'Algorithm is not a secure cryptographic hash function.' 
  345. ); 
  346.  
  347. if ($count <= 0 || $key_length <= 0) { 
  348. throw new Ex\EnvironmentIsBrokenException( 
  349. 'Invalid PBKDF2 parameters.' 
  350. ); 
  351.  
  352. if (\function_exists('hash_pbkdf2')) { 
  353. // The output length is in NIBBLES (4-bits) if $raw_output is false! 
  354. if (! $raw_output) { 
  355. $key_length = $key_length * 2; 
  356. return \hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output); 
  357.  
  358. $hash_length = Core::ourStrlen(\hash($algorithm, '', true)); 
  359. $block_count = \ceil($key_length / $hash_length); 
  360.  
  361. $output = ''; 
  362. for ($i = 1; $i <= $block_count; $i++) { 
  363. // $i encoded as 4 bytes, big endian. 
  364. $last = $salt . \pack('N', $i); 
  365. // first iteration 
  366. $last = $xorsum = \hash_hmac($algorithm, $last, $password, true); 
  367. // perform the other $count - 1 iterations 
  368. for ($j = 1; $j < $count; $j++) { 
  369. $xorsum ^= ($last = \hash_hmac($algorithm, $last, $password, true)); 
  370. $output .= $xorsum; 
  371.  
  372. if ($raw_output) { 
  373. return Core::ourSubstr($output, 0, $key_length); 
  374. } else { 
  375. return Encoding::binToHex(Core::ourSubstr($output, 0, $key_length)); 
.