From eb43cb67b65e06f5e59160e08320e27631206033 Mon Sep 17 00:00:00 2001
From: Jaime Perez Crespo <jaime.perez@uninett.no>
Date: Thu, 16 Apr 2015 11:55:16 +0200
Subject: [PATCH] Reformat SimpleSAML_Utils_Crypto. Remove
 SimpleSAML_Utils_Crypto::apr1Md5Hash() and
 SimpleSAML_Utils_Crypto::apr1Md5Valid(), since we are using now an external
 library for that.

---
 lib/SimpleSAML/Utils/Crypto.php | 231 +++++++++++++-------------------
 1 file changed, 95 insertions(+), 136 deletions(-)

diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php
index 1bf2dd210..265e43c1d 100644
--- a/lib/SimpleSAML/Utils/Crypto.php
+++ b/lib/SimpleSAML/Utils/Crypto.php
@@ -1,142 +1,101 @@
 <?php
 
+
 /**
- * A class for crypto related functions
+ * A class for cryptography-related functions
  *
- * @author Dyonisius Visser, TERENA. <visser@terena.org>
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
  */
-class SimpleSAML_Utils_Crypto {
-
-	/**
-	 * This function generates a password hash
-	 * @param $password  The unencrypted password
-	 * @param $algo      The hashing algorithm, capitals, optionally prepended with 'S' (salted)
-	 * @param $salt      Optional salt
-	 */
-	public static function pwHash($password, $algo, $salt = NULL) {
-		assert('is_string($algo)');
-		assert('is_string($password)');
-
-		if(in_array(strtolower($algo), hash_algos())) {
-			$php_algo = strtolower($algo); // 'sha256' etc
-			// LDAP compatibility
-			return '{' . preg_replace('/^SHA1$/', 'SHA', $algo) . '}'
-				.base64_encode(hash($php_algo, $password, TRUE));
-		}
-
-		// Salt
-		if(!$salt) {
-			// Default 8 byte salt, but 4 byte for LDAP SHA1 hashes
-			$bytes = ($algo == 'SSHA1') ? 4 : 8;
-			$salt = SimpleSAML_Utilities::generateRandomBytes($bytes);
-		}
-
-		if($algo[0] == 'S' && in_array(substr(strtolower($algo),1), hash_algos())) {
-			$php_algo = substr(strtolower($algo),1); // 'sha256' etc
-			// Salted hash, with LDAP compatibility
-			return '{' . preg_replace('/^SSHA1$/', 'SSHA', $algo) . '}' .
-				base64_encode(hash($php_algo, $password.$salt, TRUE) . $salt);
-		}
-
-		throw new Exception('Hashing algoritm \'' . strtolower($algo) . '\' not supported');
-
-	}
-
-
-	/**
-	 * This function checks if a password is valid
-	 * @param $crypted  Password as appears in password file, optionally prepended with algorithm
-	 * @param $clear    Password to check
-	 */
-	public static function pwValid($crypted, $clear) {
-		assert('is_string($crypted)');
-		assert('is_string($clear)');
-
-		// Match algorithm string ('{SSHA256}', '{MD5}')
-		if(preg_match('/^{(.*?)}(.*)$/', $crypted, $matches)) {
-
-			// LDAP compatibility
-			$algo = preg_replace('/^(S?SHA)$/', '${1}1', $matches[1]);
-
-			$cryptedpw =  $matches[2];
-
-			if(in_array(strtolower($algo), hash_algos())) {
-				// Unsalted hash
-				return ( $crypted == self::pwHash($clear, $algo) );
-			}
-
-			if($algo[0] == 'S' && in_array(substr(strtolower($algo),1), hash_algos())) {
-				$php_algo = substr(strtolower($algo),1);
-				// Salted hash
-				$hash_length = strlen(hash($php_algo, 'whatever', TRUE));
-				$salt = substr(base64_decode($cryptedpw), $hash_length);
-				return ( $crypted == self::pwHash($clear, $algo, $salt) );
-			}
-
-			throw new Exception('Hashing algoritm \'' . strtolower($algo) . '\' not supported');
-
-		} else {
-			return $crypted === $clear;
-		}
-	}
-
-	/**
-	 * This function generates an Apache 'apr1' password hash, which uses a modified
-	 * version of MD5: http://httpd.apache.org/docs/2.2/misc/password_encryptions.html
-	 * @param $password  The unencrypted password
-	 * @param $salt      Optional salt
-	 */
-	public static function apr1Md5Hash($password, $salt = NULL) {
-		assert('is_string($password)');
-
-		$chars = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-		if(!$salt) {
-			$salt = substr(str_shuffle($allowed_chars), 0, 8);
-		}
-
-		$len = strlen($password);
-		$text = $password.'$apr1$'.$salt;
-		$bin = pack("H32", md5($password.$salt.$password));
-		for($i = $len; $i > 0; $i -= 16) { $text .= substr($bin, 0, min(16, $i)); }
-		for($i = $len; $i > 0; $i >>= 1) { $text .= ($i & 1) ? chr(0) : $password{0}; }
-		$bin = pack("H32", md5($text));
-		for($i = 0; $i < 1000; $i++) {
-			$new = ($i & 1) ? $password : $bin;
-			if ($i % 3) $new .= $salt;
-			if ($i % 7) $new .= $password;
-			$new .= ($i & 1) ? $bin : $password;
-			$bin = pack("H32", md5($new));
-		}
-		$tmp= '';
-		for ($i = 0; $i < 5; $i++) {
-			$k = $i + 6;
-			$j = $i + 12;
-			if ($j == 16) $j = 5;
-			$tmp = $bin[$i].$bin[$k].$bin[$j].$tmp;
-		}
-		$tmp = chr(0).chr(0).$bin[11].$tmp;
-		$tmp = strtr(
-			strrev(substr(base64_encode($tmp), 2)),
-			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
-			$chars
-		);
-		return "$"."apr1"."$".$salt."$".$tmp;
-	}
-
-
-	/**
-	 * This function verifies an Apache 'apr1' password hash
-	 */
-	public static function apr1Md5Valid($crypted, $clear) {
-		assert('is_string($crypted)');
-		assert('is_string($clear)');
-		$pattern = '/^\$apr1\$([A-Za-z0-9\.\/]{8})\$([A-Za-z0-9\.\/]{22})$/';
-
-		if(preg_match($pattern, $crypted, $matches)) {
-			$salt = $matches[1];
-			return ( $crypted == self::apr1Md5Hash($clear, $salt) );
-		}
-		return FALSE;
-	}
+class SimpleSAML_Utils_Crypto
+{
+
+    /**
+     * This function hashes a password with a given algorithm.
+     *
+     * @param string $password The password to hash.
+     * @param string $algorithm The hashing algorithm, uppercase, optionally prepended with 'S' (salted). See
+     *     hash_algos() for a complete list of hashing algorithms.
+     * @param string $salt An optional salt to use.
+     *
+     * @return string The hashed password.
+     * @throws SimpleSAML_Error_Exception If the algorithm specified is not supported, or the input parameters are not
+     *     strings.
+     * @see hash_algos()
+     * @author Dyonisius Visser, TERENA <visser@terena.org>
+     * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+     */
+    public static function pwHash($password, $algorithm, $salt = null)
+    {
+        if (!is_string($algorithm) || !is_string($password)) {
+            throw new SimpleSAML_Error_Exception('Invalid input parameters.');
+        }
+
+        // hash w/o salt
+        if (in_array(strtolower($algorithm), hash_algos())) {
+            $alg_str = '{'.str_replace('SHA1', 'SHA', $algorithm).'}'; // LDAP compatibility
+            $hash = hash(strtolower($algorithm), $password, true);
+            return $alg_str.base64_encode($hash);
+        }
+
+        // hash w/ salt
+        if (!$salt) { // no salt provided, generate one
+            // default 8 byte salt, but 4 byte for LDAP SHA1 hashes
+            $bytes = ($algorithm == 'SSHA1') ? 4 : 8;
+            $salt = SimpleSAML_Utilities::generateRandomBytes($bytes);
+        }
+
+        if ($algorithm[0] == 'S' && in_array(substr(strtolower($algorithm), 1), hash_algos())) {
+            $alg = substr(strtolower($algorithm), 1); // 'sha256' etc
+            $alg_str = '{'.str_replace('SSHA1', 'SSHA', $algorithm).'}'; // LDAP compatibility
+            $hash = hash($alg, $password.$salt, true);
+            return $alg_str.base64_encode($hash.$salt);
+        }
+
+        throw new SimpleSAML_Error_Exception('Hashing algorithm \''.strtolower($algorithm).'\' is not supported');
+    }
+
+
+    /**
+     * This function checks if a password is valid
+     *
+     * @param string $hash The password as it appears in password file, optionally prepended with algorithm.
+     * @param string $password The password to check in clear.
+     *
+     * @return boolean True if the hash corresponds with the given password, false otherwise.
+     * @throws SimpleSAML_Error_Exception If the algorithm specified is not supported, or the input parameters are not
+     *     strings.
+     * @author Dyonisius Visser, TERENA <visser@terena.org>
+     */
+    public static function pwValid($hash, $password)
+    {
+        if (!is_string($hash) || !is_string($password)) {
+            throw new SimpleSAML_Error_Exception('Invalid input parameters.');
+        }
+
+        // match algorithm string (e.g. '{SSHA256}', '{MD5}')
+        if (preg_match('/^{(.*?)}(.*)$/', $hash, $matches)) {
+
+            // LDAP compatibility
+            $alg = preg_replace('/^(S?SHA)$/', '${1}1', $matches[1]);
+
+            // hash w/o salt
+            if (in_array(strtolower($alg), hash_algos())) {
+                return $hash === self::pwHash($password, $alg);
+            }
+
+            // hash w/ salt
+            if ($alg[0] === 'S' && in_array(substr(strtolower($alg), 1), hash_algos())) {
+                $php_alg = substr(strtolower($alg), 1);
+
+                // get hash length of this algorithm to learn how long the salt is
+                $hash_length = strlen(hash($php_alg, '', true));
+                $salt = substr(base64_decode($matches[2]), $hash_length);
+                return ($hash === self::pwHash($password, $alg, $salt));
+            }
+        } else {
+            return $hash === $password;
+        }
+
+        throw new SimpleSAML_Error_Exception('Hashing algorithm \''.strtolower($alg).'\' is not supported');
+    }
 }
-- 
GitLab