/** * Performs an authentication attempt * * @return \Zend\Authentication\Result * @throws \Zend\Authentication\Adapter\Exception\ExceptionInterface If authentication cannot be performed */ public function authenticate() { $getHeader = $this->options['proxy_auth'] ? 'Proxy-Authorization' : 'Authorization'; if (!($authHeader = $this->request->headers->get($getHeader))) { return $this->challengeClient(); } // Decode the Authorization header $authorizationHeader = base64_decode(substr($authHeader, strlen('Basic '))); if (!$authorizationHeader) { throw new RuntimeException('Unable to base64_decode Authorization header value'); } // See ZF-1253. Validate the credentials the same way the digest // implementation does. If invalid credentials are detected, // re-challenge the client. if (!ctype_print($authorizationHeader)) { return $this->challengeClient(); } // Fix for ZF-1515: Now re-challenges on empty username or password $credentials = array_filter(explode(':', $authorizationHeader)); if (count($credentials) !== 2) { return $this->challengeClient(); } $result = $this->resolver->resolve($credentials[0], $this->options['realm'], $credentials[1]); if ($result instanceof Result && $result->isValid()) { return $result; } if (!$result instanceof Result && !is_array($result) && Utils::compareStrings($result, $credentials[1])) { $identity = ['username' => $credentials[0], 'realm' => $this->options['realm']]; return new Result(Result::SUCCESS, $identity); } elseif (is_array($result)) { return new Result(Result::SUCCESS, $result); } return $this->challengeClient(); }
/** * 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); }
/** * Verify if a password is correct against a hash value * * @param string $password * @param string $hash * @throws Exception\RuntimeException when the hash is unable to be processed * @return bool */ public function verify($password, $hash) { $result = crypt($password, $hash); return Utils::compareStrings($hash, $result); }
/** * Defined by Zend\Authentication\Adapter\AdapterInterface * * @throws Exception\ExceptionInterface * @return AuthenticationResult */ public function authenticate() { $optionsRequired = array('filename', 'realm', 'identity', 'credential'); foreach ($optionsRequired as $optionRequired) { if (null === $this->{$optionRequired}) { throw new Exception\RuntimeException("Option '{$optionRequired}' must be set before authentication"); } } ErrorHandler::start(E_WARNING); $fileHandle = fopen($this->filename, 'r'); $error = ErrorHandler::stop(); if (false === $fileHandle) { throw new Exception\UnexpectedValueException("Cannot open '{$this->filename}' for reading", 0, $error); } $id = "{$this->identity}:{$this->realm}"; $idLength = strlen($id); $result = array('code' => AuthenticationResult::FAILURE, 'identity' => array('realm' => $this->realm, 'username' => $this->identity), 'messages' => array()); while (($line = fgets($fileHandle)) !== false) { $line = trim($line); if (empty($line)) { break; } if (substr($line, 0, $idLength) === $id) { if (CryptUtils::compareStrings(substr($line, -32), md5("{$this->identity}:{$this->realm}:{$this->credential}"))) { $result['code'] = AuthenticationResult::SUCCESS; } else { $result['code'] = AuthenticationResult::FAILURE_CREDENTIAL_INVALID; $result['messages'][] = 'Password incorrect'; } return new AuthenticationResult($result['code'], $result['identity'], $result['messages']); } } $result['code'] = AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND; $result['messages'][] = "Username '{$this->identity}' and realm '{$this->realm}' combination not found"; return new AuthenticationResult($result['code'], $result['identity'], $result['messages']); }
public function testCompareStringsBasic() { $this->assertTrue(Utils::compareStrings('test', 'test')); $this->assertFalse(Utils::compareStrings('test', 'Test')); }
/** * Verify if a password is correct against a hash value * * @param string $password * @param string $hash * @return bool */ public function verify($password, $hash) { if (substr($hash, 0, 5) === '{SHA}') { $hash2 = '{SHA}' . base64_encode(sha1($password, true)); return Utils::compareStrings($hash, $hash2); } if (substr($hash, 0, 6) === '$apr1$') { $token = explode('$', $hash); if (empty($token[2])) { throw new Exception\InvalidArgumentException('The APR1 password format is not valid'); } $hash2 = $this->apr1Md5($password, $token[2]); return Utils::compareStrings($hash, $hash2); } $bcryptPattern = '/\\$2[ay]?\\$[0-9]{2}\\$[' . addcslashes(static::BASE64, '+/') . '\\.]{53}/'; if (strlen($hash) > 13 && !preg_match($bcryptPattern, $hash)) { // digest if (empty($this->userName) || empty($this->authName)) { throw new Exception\RuntimeException('You must specify UserName and AuthName (realm) to verify the digest'); } $hash2 = md5($this->userName . ':' . $this->authName . ':' . $password); return Utils::compareStrings($hash, $hash2); } return Utils::compareStrings($hash, crypt($password, $hash)); }
/** * Digest Authentication * * @param string $header Client's Authorization header * @throws Exception\ExceptionInterface * @return Authentication\Result Valid auth result only on successful auth */ protected function _digestAuth($header) { if (empty($header)) { throw new Exception\RuntimeException('The value of the client Authorization header is required'); } if (empty($this->digestResolver)) { throw new Exception\RuntimeException('A digestResolver object must be set before doing Digest authentication'); } $data = $this->_parseDigestAuth($header); if ($data === false) { $this->response->setStatusCode(400); return new Authentication\Result(Authentication\Result::FAILURE_UNCATEGORIZED, array(), array('Invalid Authorization header format')); } // See ZF-1052. This code was a bit too unforgiving of invalid // usernames. Now, if the username is bad, we re-challenge the client. if ('::invalid::' == $data['username']) { return $this->_challengeClient(); } // Verify that the client sent back the same nonce if ($this->_calcNonce() != $data['nonce']) { return $this->_challengeClient(); } // The opaque value is also required to match, but of course IE doesn't // play ball. if (!$this->ieNoOpaque && $this->_calcOpaque() != $data['opaque']) { return $this->_challengeClient(); } // Look up the user's password hash. If not found, deny access. // This makes no assumptions about how the password hash was // constructed beyond that it must have been built in such a way as // to be recreatable with the current settings of this object. $ha1 = $this->digestResolver->resolve($data['username'], $data['realm']); if ($ha1 === false) { return $this->_challengeClient(); } // If MD5-sess is used, a1 value is made of the user's password // hash with the server and client nonce appended, separated by // colons. if ($this->algo == 'MD5-sess') { $ha1 = hash('md5', $ha1 . ':' . $data['nonce'] . ':' . $data['cnonce']); } // Calculate h(a2). The value of this hash depends on the qop // option selected by the client and the supported hash functions switch ($data['qop']) { case 'auth': $a2 = $this->request->getMethod() . ':' . $data['uri']; break; case 'auth-int': // Should be REQUEST_METHOD . ':' . uri . ':' . hash(entity-body), // but this isn't supported yet, so fall through to default case // Should be REQUEST_METHOD . ':' . uri . ':' . hash(entity-body), // but this isn't supported yet, so fall through to default case default: throw new Exception\RuntimeException('Client requested an unsupported qop option'); } // Using hash() should make parameterizing the hash algorithm // easier $ha2 = hash('md5', $a2); // Calculate the server's version of the request-digest. This must // match $data['response']. See RFC 2617, section 3.2.2.1 $message = $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $ha2; $digest = hash('md5', $ha1 . ':' . $message); // If our digest matches the client's let them in, otherwise return // a 401 code and exit to prevent access to the protected resource. if (CryptUtils::compareStrings($digest, $data['response'])) { $identity = array('username' => $data['username'], 'realm' => $data['realm']); return new Authentication\Result(Authentication\Result::SUCCESS, $identity); } return $this->_challengeClient(); }
/** * 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; }
/** * @param string $expected * @param string $actual * @return bool */ public static function compareStrings($expected, $actual) { return Utils::compareStrings($expected, $actual); }