retesere20

Untitled

Jan 18th, 2018
224
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // changed class name from SaferCrypto to Cryptor
  2.  
  3. class Cryptor
  4. {
  5. const METHOD = 'aes-256-ctr';
  6. const HASH_ALGO = 'sha256';
  7.  
  8. /**
  9. * Encrypts (but does not authenticate) a message
  10. *
  11. * @param string $message - plaintext message
  12. * @param string $key - encryption key (raw binary expected)
  13. * @param boolean $encode - set to TRUE to return a base64-encoded
  14. * @return string (raw binary)
  15. */
  16. public static function unsafe_encrypt($message, $key, $encode = false)
  17. {
  18. $nonceSize = openssl_cipher_iv_length(self::METHOD);
  19. $nonce = openssl_random_pseudo_bytes($nonceSize);
  20.  
  21. $ciphertext = openssl_encrypt(
  22. $message,
  23. self::METHOD,
  24. $key,
  25. OPENSSL_RAW_DATA,
  26. $nonce
  27. );
  28.  
  29. // Now let's pack the IV and the ciphertext together
  30. // Naively, we can just concatenate
  31. if ($encode) {
  32. return base64_encode($nonce.$ciphertext);
  33. }
  34. return $nonce.$ciphertext;
  35. }
  36.  
  37. /**
  38. * Decrypts (but does not verify) a message
  39. *
  40. * @param string $message - ciphertext message
  41. * @param string $key - encryption key (raw binary expected)
  42. * @param boolean $encoded - are we expecting an encoded string?
  43. * @return string
  44. */
  45. public static function unsafe_decrypt($message, $key, $encoded = false)
  46. {
  47. if ($encoded) {
  48. $message = base64_decode($message, true);
  49. if ($message === false) {
  50. throw new Exception('Encryption failure');
  51. }
  52. }
  53.  
  54. $nonceSize = openssl_cipher_iv_length(self::METHOD);
  55. $nonce = mb_substr($message, 0, $nonceSize, '8bit');
  56. $ciphertext = mb_substr($message, $nonceSize, null, '8bit');
  57.  
  58. $plaintext = openssl_decrypt(
  59. $ciphertext,
  60. self::METHOD,
  61. $key,
  62. OPENSSL_RAW_DATA,
  63. $nonce
  64. );
  65.  
  66. return $plaintext;
  67. }
  68.  
  69.  
  70.  
  71. /**
  72. * Encrypts then MACs a message
  73. *
  74. * @param string $message - plaintext message
  75. * @param string $key - encryption key (raw binary expected)
  76. * @param boolean $encode - set to TRUE to return a base64-encoded string
  77. * @return string (raw binary)
  78. */
  79. public static function encrypt($message, $key, $encode = false)
  80. {
  81. $key = hex2bin(implode(unpack("H*", $key)));
  82. list($encKey, $authKey) = self::splitKeys($key);
  83.  
  84. // Pass to UnsafeCrypto::encrypt
  85. $ciphertext = self::unsafe_encrypt($message, $encKey);
  86.  
  87. // Calculate a MAC of the IV and ciphertext
  88. $mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);
  89.  
  90. if ($encode) {
  91. return base64_encode($mac.$ciphertext);
  92. }
  93. // Prepend MAC to the ciphertext and return to caller
  94. return $mac.$ciphertext;
  95. }
  96.  
  97. /**
  98. * Decrypts a message (after verifying integrity)
  99. *
  100. * @param string $message - ciphertext message
  101. * @param string $key - encryption key (raw binary expected)
  102. * @param boolean $encoded - are we expecting an encoded string?
  103. * @return string (raw binary)
  104. */
  105. public static function decrypt($message, $key, $encoded = false)
  106. {
  107. $key = hex2bin(implode(unpack("H*", $key)));
  108. list($encKey, $authKey) = self::splitKeys($key);
  109. if ($encoded) {
  110. $message = base64_decode($message, true);
  111. if ($message === false) {
  112. throw new Exception('Encryption failure');
  113. }
  114. }
  115.  
  116. // Hash Size -- in case HASH_ALGO is changed
  117. $hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit');
  118. $mac = mb_substr($message, 0, $hs, '8bit');
  119.  
  120. $ciphertext = mb_substr($message, $hs, null, '8bit');
  121.  
  122. $calculated = hash_hmac(
  123. self::HASH_ALGO,
  124. $ciphertext,
  125. $authKey,
  126. true
  127. );
  128.  
  129. if (!self::hashEquals($mac, $calculated)) {
  130. throw new Exception('Encryption failure');
  131. }
  132.  
  133. // Pass to UnsafeCrypto::decrypt
  134. $plaintext = self::unsafe_decrypt($ciphertext, $encKey);
  135.  
  136. return $plaintext;
  137. }
  138.  
  139. /**
  140. * Splits a key into two separate keys; one for encryption and the other for authenticaiton
  141. *
  142. * @param string $masterKey (raw binary)
  143. * @return array (two raw binary strings)
  144. */
  145. protected static function splitKeys($masterKey)
  146. {
  147. // You really want to implement HKDF here instead!
  148. return [
  149. hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),
  150. hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)
  151. ];
  152. }
  153.  
  154. /**
  155. * Compare two strings without leaking timing information
  156. *
  157. * @param string $a
  158. * @param string $b
  159. * @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW
  160. * @return boolean
  161. */
  162. protected static function hashEquals($a, $b)
  163. {
  164. if (function_exists('hash_equals')) {
  165. return hash_equals($a, $b);
  166. }
  167. $nonce = openssl_random_pseudo_bytes(32);
  168. return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce);
  169. }
  170. }
RAW Paste Data