/** * Generate the new key * * @param string $hash The hash algorithm to be used by HMAC * @param string $password The source password/key * @param string $salt * @param integer $iterations The number of iterations * @param integer $length The output size * @return string */ public static function calc($hash, $password, $salt, $iterations, $length) { if (!in_array($hash, Hmac::getSupportedAlgorithms())) { throw new Exception\InvalidArgumentException("The hash algorihtm {$hash} is not supported by " . __CLASS__); } $num = ceil($length / Hmac::getOutputSize($hash, Hmac::BINARY)); $result = ''; for ($block = 0; $block < $num; $block++) { $hmac = Hmac::compute($password, $hash, $salt . pack('N', $block), Hmac::BINARY); for ($i = 1; $i < $iterations; $i++) { $hmac ^= Hmac::compute($password, $hash, $hmac, Hmac::BINARY); } $result .= $hmac; } return substr($result, 0, $length); }
/** * Generate the new key * * @param string $hash The hash algorithm to be used by HMAC * @param string $password The source password/key * @param string $salt * @param int $iterations The number of iterations * @param int $length The output size * @throws Exception\InvalidArgumentException * @return string */ public static function calc($hash, $password, $salt, $iterations, $length) { if (!Hmac::isSupported($hash)) { throw new Exception\InvalidArgumentException("The hash algorithm {$hash} is not supported by " . __CLASS__); } $num = ceil($length / Hmac::getOutputSize($hash, Hmac::OUTPUT_BINARY)); $result = ''; for ($block = 1; $block <= $num; $block++) { $hmac = hash_hmac($hash, $salt . pack('N', $block), $password, Hmac::OUTPUT_BINARY); $mix = $hmac; for ($i = 1; $i < $iterations; $i++) { $hmac = hash_hmac($hash, $hmac, $password, Hmac::OUTPUT_BINARY); $mix ^= $hmac; } $result .= $mix; } return substr($result, 0, $length); }
/** * Decrypt * * @param string $data * @return string|boolean * @throws Exception\InvalidArgumentException */ public function decrypt($data) { if (!is_string($data)) { throw new Exception\InvalidArgumentException('The data to decrypt must be a string'); } if ('' === $data) { throw new Exception\InvalidArgumentException('The data to decrypt cannot be empty'); } if (empty($this->key)) { throw new Exception\InvalidArgumentException('No key specified for the decryption'); } if (empty($this->cipher)) { throw new Exception\InvalidArgumentException('No symmetric cipher specified'); } $hmacSize = Hmac::getOutputSize($this->hash); $hmac = substr($data, 0, $hmacSize); $ciphertext = substr($data, $hmacSize); if (!$this->binaryOutput) { $ciphertext = base64_decode($ciphertext); } $iv = substr($ciphertext, 0, $this->cipher->getSaltSize()); $keySize = $this->cipher->getKeySize(); // generate the encryption key and the HMAC key for the authentication $hash = Pbkdf2::calc(self::KEY_DERIV_HMAC, $this->getKey(), $iv, $this->keyIteration, $keySize * 2); // set the decryption key $this->cipher->setKey(substr($hash, 0, $keySize)); // set the key for HMAC $keyHmac = substr($hash, $keySize); $hmacNew = Hmac::compute($keyHmac, $this->hash, $this->cipher->getAlgorithm() . $ciphertext); if (!Utils::compareStrings($hmacNew, $hmac)) { return false; } return $this->cipher->decrypt($ciphertext); }
/** * Decrypt a file * * @param string $fileIn * @param string $fileOut * @param bool $compress * @return bool * @throws Exception\InvalidArgumentException */ public function decrypt($fileIn, $fileOut) { $this->checkFileInOut($fileIn, $fileOut); if (empty($this->key)) { throw new Exception\InvalidArgumentException('No key specified for decryption'); } $read = fopen($fileIn, "r"); $write = fopen($fileOut, "w"); $hmacRead = fread($read, Hmac::getOutputSize($this->getHashAlgorithm())); $iv = fread($read, $this->cipher->getSaltSize()); $tot = filesize($fileIn); $hmac = $iv; $size = mb_strlen($iv, '8bit') + mb_strlen($hmacRead, '8bit'); $keys = Pbkdf2::calc($this->getPbkdf2HashAlgorithm(), $this->getKey(), $iv, $this->getKeyIteration(), $this->cipher->getKeySize() * 2); $padding = $this->cipher->getPadding(); $this->cipher->setPadding(new Symmetric\Padding\NoPadding()); $this->cipher->setKey(mb_substr($keys, 0, $this->cipher->getKeySize(), '8bit')); $this->cipher->setMode('cbc'); $blockSize = $this->cipher->getBlockSize(); $hashAlgo = $this->getHashAlgorithm(); $algorithm = $this->cipher->getAlgorithm(); $saltSize = $this->cipher->getSaltSize(); $keyHmac = mb_substr($keys, $this->cipher->getKeySize(), null, '8bit'); while ($data = fread($read, self::BUFFER_SIZE)) { $size += mb_strlen($data, '8bit'); // Unpadding if last block if ($size + $blockSize >= $tot) { $this->cipher->setPadding($padding); $data .= fread($read, $blockSize); } $result = $this->cipher->decrypt($iv . $data); $hmac = Hmac::compute($keyHmac, $hashAlgo, $algorithm . $hmac . $data); $iv = mb_substr($data, -1 * $saltSize, null, '8bit'); if (fwrite($write, $result) !== mb_strlen($result, '8bit')) { return false; } } fclose($write); fclose($read); // check for data integrity if (!Utils::compareStrings($hmac, $hmacRead)) { unlink($fileOut); return false; } return true; }