From 2a47f90cf7c49f5474fb2fb13427f664351a6c96 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Thu, 13 Jun 2024 16:04:09 +0200 Subject: [PATCH] Fixed Class ScramAuthenticator not found in modules/imap/hm-imap.php --- lib/framework.php | 1 + lib/scram.php | 108 ++++++++++++++++++++++++++++++++++++++++ modules/scram/scram.php | 105 -------------------------------------- 3 files changed, 109 insertions(+), 105 deletions(-) create mode 100644 lib/scram.php delete mode 100644 modules/scram/scram.php diff --git a/lib/framework.php b/lib/framework.php index 4866662573..25644f5afc 100644 --- a/lib/framework.php +++ b/lib/framework.php @@ -29,6 +29,7 @@ require APP_PATH.'lib/crypt.php'; require APP_PATH.'lib/crypt_sodium.php'; require APP_PATH.'lib/sodium_compat.php'; +require APP_PATH.'lib/scram.php'; require APP_PATH.'lib/environment.php'; require APP_PATH.'lib/db.php'; require APP_PATH.'lib/servers.php'; diff --git a/lib/scram.php b/lib/scram.php new file mode 100644 index 0000000000..9164dc3959 --- /dev/null +++ b/lib/scram.php @@ -0,0 +1,108 @@ + 'sha1', + 'sha1' => 'sha1', + 'sha-224' => 'sha224', + 'sha224' => 'sha224', + 'sha-256' => 'sha256', + 'sha256' => 'sha256', + 'sha-384' => 'sha384', + 'sha384' => 'sha384', + 'sha-512' => 'sha512', + 'sha512' => 'sha512' +); + +private function getHashAlgorithm($scramAlgorithm) { + $parts = explode('-', strtolower($scramAlgorithm)); + return $this->hashes[$parts[1]] ?? 'sha1'; // Default to sha1 if the algorithm is not found +} +private function log($message) { + // Use Hm_Debug to add the debug message + Hm_Debug::add(sprintf($message)); +} +public function generateClientProof($username, $password, $salt, $clientNonce, $serverNonce, $algorithm) { + $iterations = 4096; + $keyLength = strlen(hash($algorithm, '', true)); // Dynamically determine key length based on algorithm + + $passwordBytes = hash($algorithm, $password, true); + $saltedPassword = hash_pbkdf2($algorithm, $passwordBytes, $salt, $iterations, $keyLength, true); + $clientKey = hash_hmac($algorithm, "Client Key", $saltedPassword, true); + $storedKey = hash($algorithm, $clientKey, true); + $authMessage = 'n=' . $username . ',r=' . $clientNonce . ',s=' . base64_encode($salt) . ',r=' . $serverNonce; + $clientSignature = hash_hmac($algorithm, $authMessage, $storedKey, true); + $clientProof = base64_encode($clientKey ^ $clientSignature); + $this->log("Client proof generated successfully"); + return $clientProof; +} + +public function authenticateScram($scramAlgorithm, $username, $password, $getServerResponse, $sendCommand) { + $algorithm = $this->getHashAlgorithm($scramAlgorithm); + + // Send initial SCRAM command + $scramCommand = 'AUTHENTICATE ' . $scramAlgorithm . "\r\n"; + $sendCommand($scramCommand); + $response = $getServerResponse(); + if (!empty($response) && substr($response[0], 0, 2) == '+ ') { + $this->log("Received server challenge: " . $response[0]); + // Extract salt and server nonce from the server's challenge + $serverChallenge = base64_decode(substr($response[0], 2)); + $parts = explode(',', $serverChallenge); + $serverNonce = base64_decode(substr($parts[0], strpos($parts[0], "=") + 1)); + $salt = base64_decode(substr($parts[1], strpos($parts[1], "=") + 1)); + + // Generate client nonce + $clientNonce = base64_encode(random_bytes(32)); + $this->log("Generated client nonce: " . $clientNonce); + + // Calculate client proof + $clientProof = $this->generateClientProof($username, $password, $salt, $clientNonce, $serverNonce, $algorithm); + + // Construct client final message + $channelBindingData = (stripos($scramAlgorithm, 'plus') !== false) ? 'c=' . base64_encode('tls-unique') . ',' : 'c=biws,'; + $clientFinalMessage = $channelBindingData . 'r=' . $serverNonce . $clientNonce . ',p=' . $clientProof; + $clientFinalMessageEncoded = base64_encode($clientFinalMessage); + $this->log("Sending client final message: " . $clientFinalMessageEncoded); + // Send client final message to server + $sendCommand($clientFinalMessageEncoded . "\r\n"); + + // Verify server's response + $response = $getServerResponse(); + if (!empty($response) && substr($response[0], 0, 2) == '+ ') { + $serverFinalMessage = base64_decode(substr($response[0], 2)); + $parts = explode(',', $serverFinalMessage); + $serverProof = substr($parts[0], strpos($parts[0], "=") + 1); + + // Generate server key + $passwordBytes = hash($algorithm, $password, true); + $saltedPassword = hash_pbkdf2($algorithm, $passwordBytes, $salt, 4096, strlen(hash($algorithm, '', true)), true); + $serverKey = hash_hmac($algorithm, "Server Key", $saltedPassword, true); + + // Calculate server signature + $authMessage = 'n=' . $username . ',r=' . $clientNonce . ',s=' . base64_encode($salt) . ',r=' . $serverNonce; + $serverSignature = base64_encode(hash_hmac($algorithm, $authMessage, $serverKey, true)); + + // Compare server signature with server proof + if ($serverSignature === $serverProof) { + $this->log("SCRAM authentication successful"); + return true; // Authentication successful if they match + } else { + $this->log("SCRAM authentication failed: Server signature mismatch"); + } + } else { + $this->log("SCRAM authentication failed: Invalid server final response"); + } + } else { + $this->log("SCRAM authentication failed: Invalid server challenge"); + } + return false; // Authentication failed +} +} \ No newline at end of file diff --git a/modules/scram/scram.php b/modules/scram/scram.php deleted file mode 100644 index 66b1ba2791..0000000000 --- a/modules/scram/scram.php +++ /dev/null @@ -1,105 +0,0 @@ - 'sha1', - 'sha1' => 'sha1', - 'sha-224' => 'sha224', - 'sha224' => 'sha224', - 'sha-256' => 'sha256', - 'sha256' => 'sha256', - 'sha-384' => 'sha384', - 'sha384' => 'sha384', - 'sha-512' => 'sha512', - 'sha512' => 'sha512' - ); - - private function getHashAlgorithm($scramAlgorithm) { - $parts = explode('-', strtolower($scramAlgorithm)); - return $this->hashes[$parts[1]] ?? 'sha1'; // Default to sha1 if the algorithm is not found - } - private function log($message) { - // Use Hm_Debug to add the debug message - Hm_Debug::add(sprintf($message)); - } - public function generateClientProof($username, $password, $salt, $clientNonce, $serverNonce, $algorithm) { - $iterations = 4096; - $keyLength = strlen(hash($algorithm, '', true)); // Dynamically determine key length based on algorithm - - $passwordBytes = hash($algorithm, $password, true); - $saltedPassword = hash_pbkdf2($algorithm, $passwordBytes, $salt, $iterations, $keyLength, true); - $clientKey = hash_hmac($algorithm, "Client Key", $saltedPassword, true); - $storedKey = hash($algorithm, $clientKey, true); - $authMessage = 'n=' . $username . ',r=' . $clientNonce . ',s=' . base64_encode($salt) . ',r=' . $serverNonce; - $clientSignature = hash_hmac($algorithm, $authMessage, $storedKey, true); - $clientProof = base64_encode($clientKey ^ $clientSignature); - $this->log("Client proof generated successfully"); - return $clientProof; - } - - public function authenticateScram($scramAlgorithm, $username, $password, $getServerResponse, $sendCommand) { - $algorithm = $this->getHashAlgorithm($scramAlgorithm); - - // Send initial SCRAM command - $scramCommand = 'AUTHENTICATE ' . $scramAlgorithm . "\r\n"; - $sendCommand($scramCommand); - $response = $getServerResponse(); - if (!empty($response) && substr($response[0], 0, 2) == '+ ') { - $this->log("Received server challenge: " . $response[0]); - // Extract salt and server nonce from the server's challenge - $serverChallenge = base64_decode(substr($response[0], 2)); - $parts = explode(',', $serverChallenge); - $serverNonce = base64_decode(substr($parts[0], strpos($parts[0], "=") + 1)); - $salt = base64_decode(substr($parts[1], strpos($parts[1], "=") + 1)); - - // Generate client nonce - $clientNonce = base64_encode(random_bytes(32)); - $this->log("Generated client nonce: " . $clientNonce); - - // Calculate client proof - $clientProof = $this->generateClientProof($username, $password, $salt, $clientNonce, $serverNonce, $algorithm); - - // Construct client final message - $channelBindingData = (stripos($scramAlgorithm, 'plus') !== false) ? 'c=' . base64_encode('tls-unique') . ',' : 'c=biws,'; - $clientFinalMessage = $channelBindingData . 'r=' . $serverNonce . $clientNonce . ',p=' . $clientProof; - $clientFinalMessageEncoded = base64_encode($clientFinalMessage); - $this->log("Sending client final message: " . $clientFinalMessageEncoded); - // Send client final message to server - $sendCommand($clientFinalMessageEncoded . "\r\n"); - - // Verify server's response - $response = $getServerResponse(); - if (!empty($response) && substr($response[0], 0, 2) == '+ ') { - $serverFinalMessage = base64_decode(substr($response[0], 2)); - $parts = explode(',', $serverFinalMessage); - $serverProof = substr($parts[0], strpos($parts[0], "=") + 1); - - // Generate server key - $passwordBytes = hash($algorithm, $password, true); - $saltedPassword = hash_pbkdf2($algorithm, $passwordBytes, $salt, 4096, strlen(hash($algorithm, '', true)), true); - $serverKey = hash_hmac($algorithm, "Server Key", $saltedPassword, true); - - // Calculate server signature - $authMessage = 'n=' . $username . ',r=' . $clientNonce . ',s=' . base64_encode($salt) . ',r=' . $serverNonce; - $serverSignature = base64_encode(hash_hmac($algorithm, $authMessage, $serverKey, true)); - - // Compare server signature with server proof - if ($serverSignature === $serverProof) { - $this->log("SCRAM authentication successful"); - return true; // Authentication successful if they match - } else { - $this->log("SCRAM authentication failed: Server signature mismatch"); - } - } else { - $this->log("SCRAM authentication failed: Invalid server final response"); - } - } else { - $this->log("SCRAM authentication failed: Invalid server challenge"); - } - return false; // Authentication failed - } -} - - -?> \ No newline at end of file