Skip to content

Commit

Permalink
Fixed Class ScramAuthenticator not found in modules/imap/hm-imap.php
Browse files Browse the repository at this point in the history
  • Loading branch information
josaphatim committed Jun 13, 2024
1 parent 680400e commit 2a47f90
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 105 deletions.
1 change: 1 addition & 0 deletions lib/framework.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
108 changes: 108 additions & 0 deletions lib/scram.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

/**
* Authenticator
* @package framework
* @subpackage crypt
*/

class ScramAuthenticator {

private $hashes = array(
'sha-1' => '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
}
}
105 changes: 0 additions & 105 deletions modules/scram/scram.php

This file was deleted.

0 comments on commit 2a47f90

Please sign in to comment.