Cryptor.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. <?php
  2. namespace RNCryptor\RNCryptor;
  3. use stdClass;
  4. class Cryptor
  5. {
  6. const DEFAULT_SCHEMA_VERSION = 3;
  7. protected $config;
  8. public function generateKey($salt, $password, $version = self::DEFAULT_SCHEMA_VERSION)
  9. {
  10. $this->configure($version);
  11. return $this->makeKey($salt, $password);
  12. }
  13. protected function aesCtrLittleEndianCrypt($payload, $key, $iv)
  14. {
  15. $numOfBlocks = ceil(strlen($payload) / strlen($iv));
  16. $counter = '';
  17. for ($i = 0; $i < $numOfBlocks; ++$i) {
  18. $counter .= $iv;
  19. // Yes, the next line only ever increments the first character
  20. // of the counter string, ignoring overflow conditions. This
  21. // matches CommonCrypto's behavior!
  22. $iv[0] = chr(ord($iv[0]) + 1);
  23. }
  24. return $payload ^ $this->encryptInternal($key, $counter, 'ecb');
  25. }
  26. protected function encryptInternal($key, $payload, $mode, $iv = null)
  27. {
  28. return openssl_encrypt($payload, $this->config->algorithm . $mode, $key, OPENSSL_RAW_DATA, (string)$iv);
  29. }
  30. protected function makeHmac(stdClass $components, $hmacKey)
  31. {
  32. $hmacMessage = '';
  33. if ($this->config->hmac->includesHeader) {
  34. $hmacMessage .= ''
  35. . $components->headers->version
  36. . $components->headers->options
  37. . (isset($components->headers->encSalt) ? $components->headers->encSalt : '')
  38. . (isset($components->headers->hmacSalt) ? $components->headers->hmacSalt : '')
  39. . $components->headers->iv;
  40. }
  41. $hmacMessage .= $components->ciphertext;
  42. $hmac = hash_hmac($this->config->hmac->algorithm, $hmacMessage, $hmacKey, true);
  43. if ($this->config->hmac->includesPadding) {
  44. $hmac = str_pad($hmac, $this->config->hmac->length, chr(0));
  45. }
  46. return $hmac;
  47. }
  48. protected function makeKey($salt, $password)
  49. {
  50. if ($this->config->truncatesMultibytePasswords) {
  51. $utf8Length = mb_strlen($password, 'utf-8');
  52. $password = substr($password, 0, $utf8Length);
  53. }
  54. $algo = $this->config->pbkdf2->prf;
  55. $iterations = $this->config->pbkdf2->iterations;
  56. $length = $this->config->pbkdf2->keyLength;
  57. return hash_pbkdf2($algo, $password, $salt, $iterations, $length, true);
  58. }
  59. protected function configure($version)
  60. {
  61. $config = new stdClass;
  62. $config->algorithm = 'aes-256-';
  63. $config->saltLength = 8;
  64. $config->ivLength = 16;
  65. $config->pbkdf2 = new stdClass;
  66. $config->pbkdf2->prf = 'sha1';
  67. $config->pbkdf2->iterations = 10000;
  68. $config->pbkdf2->keyLength = 32;
  69. $config->hmac = new stdClass();
  70. $config->hmac->length = 32;
  71. if (!$version) {
  72. $this->configureVersionZero($config);
  73. } elseif ($version <= 3) {
  74. $config->mode = 'cbc';
  75. $config->options = 1;
  76. $config->hmac->algorithm = 'sha256';
  77. $config->hmac->includesPadding = false;
  78. switch ($version) {
  79. case 1:
  80. $config->hmac->includesHeader = false;
  81. $config->truncatesMultibytePasswords = true;
  82. break;
  83. case 2:
  84. $config->hmac->includesHeader = true;
  85. $config->truncatesMultibytePasswords = true;
  86. break;
  87. case 3:
  88. $config->hmac->includesHeader = true;
  89. $config->truncatesMultibytePasswords = false;
  90. break;
  91. }
  92. } else {
  93. throw new \RuntimeException('Unsupported schema version ' . $version);
  94. }
  95. $this->config = $config;
  96. }
  97. private function configureVersionZero(stdClass $config)
  98. {
  99. $config->mode = 'ctr';
  100. $config->options = 0;
  101. $config->hmac->includesHeader = false;
  102. $config->hmac->algorithm = 'sha1';
  103. $config->hmac->includesPadding = true;
  104. $config->truncatesMultibytePasswords = true;
  105. }
  106. }