bug 546556: More random salts when mcrypt available; SHA-256 for hashes; DB column name change - r=telliott
authorLeslie Michael Orchard <lorchard@mozilla.com>
Fri, 16 Apr 2010 15:37:14 -0400
changeset 76 511d1b8f24e6
parent 75 34e79eefc42c
child 78 fca754f53428
push id1
push usertziade@mozilla.com
push dateTue, 19 Oct 2010 10:41:37 +0000
reviewerstelliott
bugs546556
bug 546556: More random salts when mcrypt available; SHA-256 for hashes; DB column name change - r=telliott
1.0/weave_user/mysql.php
--- a/1.0/weave_user/mysql.php
+++ b/1.0/weave_user/mysql.php
@@ -45,17 +45,17 @@ require_once 'weave_constants.php';
 #Note that this object does not contain any database setup information. It assumes that the mysql
 #instance is already fully configured as part of the weave-registration server
 
 #
 #create table users
 #(
 # id int(11) NOT NULL PRIMARY KEY auto_increment
 # username varchar(32),
-# md5 varbinary(64),
+# password_hash varbinary(128),
 # email varbinary(64),
 # status tinyint(4) default '1',
 # alert text,
 # reset varbinary(32) default null,
 #) engine=InnoDB;
 #
 
 class WeaveAuthentication implements WeaveAuthenticationBase
@@ -80,37 +80,42 @@ class WeaveAuthentication implements Wea
 	 */
 	function generateSSHAPassword($password, $salt=null)
 	{
 		if (setlocale(LC_CTYPE, "UTF8", "en_US.UTF-8") == false) 
 			throw new Exception("Database Unavailable", 503);
 
 		// see also: http://blog.coenbijlsma.nl/2009/01/17/php-and-ssha-ldap-passwords/
 		if (null === $salt) {
-			mt_srand((double)microtime()*1000000);
-			$salt = pack("CCCCCCCC", 
-				mt_rand(), mt_rand(), mt_rand(), mt_rand(), 
-				mt_rand(), mt_rand(), mt_rand(), mt_rand());
+			if (function_exists('mcrypt_create_iv')) {
+				$salt = mcrypt_create_iv(8, MCRYPT_DEV_RANDOM);
+			} else {
+			   mt_srand((double)microtime()*1000000);
+			   $salt = pack("CCCCCCCC", 
+					   mt_rand(), mt_rand(), mt_rand(), mt_rand(), 
+					   mt_rand(), mt_rand(), mt_rand(), mt_rand());
+			}
 		}
-		$ssha = "{SSHA}" . 
-			base64_encode( pack("H*", sha1($password . $salt)) . $salt);
+		$ssha = "{SSHA-256}" . 
+			base64_encode( hash('sha256', $password . $salt, true) . $salt);
 		return $ssha;
 	}
 
 	/**
 	 * Determine whether the given password is valid for the given SSHA
 	 * password hash.
 	 *
 	 * @param   string $password  Cleartext password
 	 * @param   string $ssha_hash SSHA password hash
 	 * @returns boolean
 	 */
 	function validateSSHAPassword($password, $ssha_hash)
 	{
-		$salt	   = substr(base64_decode(substr($ssha_hash, 6)), 20);
+		$tag_len   = strlen('{SSHA-256}');
+		$salt	   = substr(base64_decode(substr($ssha_hash, $tag_len)), 32);
 		$test_hash = $this->generateSSHAPassword($password, $salt);
 		return ($ssha_hash == $test_hash);
 	}
 
 	/**
 	 * Hash a password using the latest password hashing method (eg SSHA)
 	 *
 	 * @param   string $password Cleartext password
@@ -157,21 +162,21 @@ class WeaveAuthentication implements Wea
 			throw new Exception(WEAVE_ERROR_MISSING_PASSWORD, 404);
 		}
 		
 		if (!$this->user_exists())
 			throw new Exception("User not found", 404);
 			
 		try
 		{
-			$insert_stmt = 'update users set md5 = :md5 where username = :username';
+			$insert_stmt = 'update users set password_hash = :password_hash where username = :username';
 			$sth = $this->_dbh->prepare($insert_stmt);
 			$sth->bindParam(':username', $this->_username);
 			$phash = $this->hash_password($password);
-			$sth->bindParam(':md5', $phash);
+			$sth->bindParam(':password_hash', $phash);
 			$sth->execute();
 		}
 		catch( PDOException $exception )
 		{
 			error_log("update_password: " . $exception->getMessage());
 			throw new Exception("Database unavailable", 503);
 		}
 		return true;
@@ -209,47 +214,47 @@ class WeaveAuthentication implements Wea
 	 * @returns mixed - null on invalid, user ID on valid.
 	 */
 	function authenticate_user($password)
 	{
 		$result = null;
 		try
 		{
 			$select_stmt = '
-				SELECT id, md5, status, alert 
+				SELECT id, password_hash, status, alert 
 				FROM users 
 				WHERE username=?
 			';
 			$sth = $this->_dbh->prepare($select_stmt);
 			$sth->execute(array($this->_username));
 			$result = $sth->fetch(PDO::FETCH_ASSOC);
 			$sth->closeCursor();
 
 			if (!$result) { 
 				// User not found.
 				return null; 
 			} else if ($result['status'] != 1) {
 				// User disabled.
 				return null;
-			} else if (strpos($result['md5'], '{SSHA}') !== false) {
-				// Looks like a {SSHA} password, so try validating it.
-				if (!$this->validateSSHAPassword($password, $result['md5'])) {
+			} else if (strpos($result['password_hash'], '{SSHA-256}') !== false) {
+				// Looks like a {SSHA-256} password, so try validating it.
+				if (!$this->validateSSHAPassword($password, $result['password_hash'])) {
 					return null;
 				}
 			} else {
 				// This might be a legacy password hash, so try fallbacks...
 				if (defined('WEAVE_MD5_FALLBACK') && WEAVE_MD5_FALLBACK &&
-						($result['md5'] == md5($password)) ) {
+						($result['password_hash'] == md5($password)) ) {
 					// Looks like a valid MD5 hash, so accept it but update it.
 					$this->update_password($password);
 				} else if (defined('WEAVE_SHA_SALT')) {
 					// We have a SHA salt, so try generating a hash with it.
 					$p_array = str_split($password, (floor(strlen($password)/2))+1);
 					$sha_hash = hash('sha256', $p_array[0].WEAVE_SHA_SALT.$p_array[1]);
-					if ($result['md5'] == $sha_hash) {
+					if ($result['password_hash'] == $sha_hash) {
 						// Looks like a valid SHA hash, so accept it but update it.
 						$this->update_password($password);
 					} else {
 						// Ran out of legacy fallbacks, so bail.
 						return null;
 					}
 				} else {
 					// Ran out of legacy fallbacks, so bail.