public function saslScrumSHA1Step($session, $input = null)
{
$session['step']++;
$query = [];
if (!is_null($input) && (!empty($input['$err']) || !empty($input['errmsg']))) {
$session['cb']($input);
return;
}
if ($session['step'] == 1) {
$session['nonce'] = base64_encode(openssl_random_pseudo_bytes(24));
$payload = 'n,,n=' . $session['user'] . ',r=' . $session['nonce'];
$query = ['saslStart' => 1, 'mechanism' => 'SCRAM-SHA-1', 'payload' => base64_encode($payload)];
$session['auth_message'] .= 'n=' . $session['user'] . ',r=' . $session['nonce'] . ',';
} elseif ($session['step'] == 2) {
$in_payload = $this->saslScrumSHA1ExtractPayload($input['payload']);
$error = null;
if (count($in_payload) != 3) {
$error = 'Incorrect number of arguments for first SCRAM-SHA-1 server message, got ' . count($in_payload) . 'expected 3';
} elseif (mb_orig_strlen($in_payload['r']) < 2) {
$error = 'Incorrect SCRAM-SHA-1 client|server nonce: ' . $in_payload['r'];
} elseif (mb_orig_strlen($in_payload['s']) < 6) {
$error = 'Incorrect SCRAM-SHA-1 salt: ' . $in_payload['s'];
} elseif (mb_orig_strlen($in_payload['i']) < 3) {
$error = 'Incorrect SCRAM-SHA-1 iteration count: ' . $in_payload['i'];
} elseif (mb_orig_strpos($in_payload['r'], $session['nonce']) !== 0) {
$error = 'Server SCRAM-SHA-1 nonce does not match client nonce';
}
if (!empty($error)) {
$session['cb'](['ok' => 0, 'errmsg' => $error]);
return;
} else {
$session['conversation_id'] = $input['conversationId'];
$session['nonce'] = $in_payload['r'];
}
$payload = 'c=biws,r=' . $session['nonce'];
$session['auth_message'] .= base64_decode($input['payload']) . ',' . $payload;
$decoded_salt = base64_decode($in_payload['s']);
$password = md5($session['user'] . ':mongo:' . $session['password']);
$salted_password = hash_pbkdf2('sha1', $password, $decoded_salt, (int) $in_payload['i'], 0, true);
$client_key = hash_hmac('sha1', 'Client Key', $salted_password, true);
$stored_key = sha1($client_key, true);
$client_sign = hash_hmac('sha1', $session['auth_message'], $stored_key, true);
$client_proof = $client_key ^ $client_sign;
$payload .= ',p=' . base64_encode($client_proof);
$query = ['saslContinue' => 1, 'conversationId' => $session['conversation_id'], 'payload' => base64_encode($payload)];
} elseif ($session['step'] == 3) {
$in_payload = $this->saslScrumSHA1ExtractPayload($input['payload']);
if (!empty($in_payload['v'])) {
$session['server_signature'] = $in_payload['v'];
$query = ['saslContinue' => 1, 'conversationId' => $session['conversation_id'], 'payload' => base64_encode('')];
}
} elseif ($session['step'] == 4) {
$in_payload = $this->saslScrumSHA1ExtractPayload($input['payload']);
$res = $input['done'] ? ['ok' => 1, 'server_signature' => $session['server_signature']] : ['ok' => 0, 'errmsg' => 'Authentication failed.'];
$session['cb']($res);
return;
}
$this->saslScrumSHA1Conversation($session['dbname'], $query, function ($res) use($session) {
$this->saslScrumSHA1Step($session, $res);
}, $session['conn']);
}