Passed
Push — master ( 1ae1d9...d63fc8 )
by Roeland
12:34 queued 12s
created
lib/private/Security/Hasher.php 1 patch
Indentation   +168 added lines, -168 removed lines patch added patch discarded remove patch
@@ -50,173 +50,173 @@
 block discarded – undo
50 50
  * @package OC\Security
51 51
  */
52 52
 class Hasher implements IHasher {
53
-	/** @var IConfig */
54
-	private $config;
55
-	/** @var array Options passed to password_hash and password_needs_rehash */
56
-	private $options = [];
57
-	/** @var string Salt used for legacy passwords */
58
-	private $legacySalt = null;
59
-
60
-	/**
61
-	 * @param IConfig $config
62
-	 */
63
-	public function __construct(IConfig $config) {
64
-		$this->config = $config;
65
-
66
-		if (\defined('PASSWORD_ARGON2I')) {
67
-			// password_hash fails, when the minimum values are undershot.
68
-			// In this case, ignore and revert to default
69
-			if ($this->config->getSystemValueInt('hashingMemoryCost', PASSWORD_ARGON2_DEFAULT_MEMORY_COST) >= 8) {
70
-				$this->options['memory_cost'] = $this->config->getSystemValueInt('hashingMemoryCost', PASSWORD_ARGON2_DEFAULT_MEMORY_COST);
71
-			}
72
-			if ($this->config->getSystemValueInt('hashingTimeCost', PASSWORD_ARGON2_DEFAULT_MEMORY_COST) >= 1) {
73
-				$this->options['time_cost'] = $this->config->getSystemValueInt('hashingTimeCost', PASSWORD_ARGON2_DEFAULT_TIME_COST);
74
-			}
75
-			if ($this->config->getSystemValueInt('hashingThreads', PASSWORD_ARGON2_DEFAULT_MEMORY_COST) >= 1) {
76
-				$this->options['threads'] = $this->config->getSystemValueInt('hashingThreads', PASSWORD_ARGON2_DEFAULT_THREADS);
77
-			}
78
-		}
79
-
80
-		$hashingCost = $this->config->getSystemValue('hashingCost', null);
81
-		if(!\is_null($hashingCost)) {
82
-			$this->options['cost'] = $hashingCost;
83
-		}
84
-	}
85
-
86
-	/**
87
-	 * Hashes a message using PHP's `password_hash` functionality.
88
-	 * Please note that the size of the returned string is not guaranteed
89
-	 * and can be up to 255 characters.
90
-	 *
91
-	 * @param string $message Message to generate hash from
92
-	 * @return string Hash of the message with appended version parameter
93
-	 */
94
-	public function hash(string $message): string {
95
-		$alg = $this->getPrefferedAlgorithm();
96
-
97
-		if (\defined('PASSWORD_ARGON2I') && $alg === PASSWORD_ARGON2I) {
98
-			return 2 . '|' . password_hash($message, PASSWORD_ARGON2I, $this->options);
99
-		}
100
-
101
-		return 1 . '|' . password_hash($message, PASSWORD_BCRYPT, $this->options);
102
-	}
103
-
104
-	/**
105
-	 * Get the version and hash from a prefixedHash
106
-	 * @param string $prefixedHash
107
-	 * @return null|array Null if the hash is not prefixed, otherwise array('version' => 1, 'hash' => 'foo')
108
-	 */
109
-	protected function splitHash(string $prefixedHash) {
110
-		$explodedString = explode('|', $prefixedHash, 2);
111
-		if(\count($explodedString) === 2) {
112
-			if((int)$explodedString[0] > 0) {
113
-				return ['version' => (int)$explodedString[0], 'hash' => $explodedString[1]];
114
-			}
115
-		}
116
-
117
-		return null;
118
-	}
119
-
120
-	/**
121
-	 * Verify legacy hashes
122
-	 * @param string $message Message to verify
123
-	 * @param string $hash Assumed hash of the message
124
-	 * @param null|string &$newHash Reference will contain the updated hash
125
-	 * @return bool Whether $hash is a valid hash of $message
126
-	 */
127
-	protected function legacyHashVerify($message, $hash, &$newHash = null): bool {
128
-		if(empty($this->legacySalt)) {
129
-			$this->legacySalt = $this->config->getSystemValue('passwordsalt', '');
130
-		}
131
-
132
-		// Verify whether it matches a legacy PHPass or SHA1 string
133
-		$hashLength = \strlen($hash);
134
-		if(($hashLength === 60 && password_verify($message.$this->legacySalt, $hash)) ||
135
-			($hashLength === 40 && hash_equals($hash, sha1($message)))) {
136
-			$newHash = $this->hash($message);
137
-			return true;
138
-		}
139
-
140
-		return false;
141
-	}
142
-
143
-	/**
144
-	 * Verify V1 (blowfish) hashes
145
-	 * @param string $message Message to verify
146
-	 * @param string $hash Assumed hash of the message
147
-	 * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one.
148
-	 * @return bool Whether $hash is a valid hash of $message
149
-	 */
150
-	protected function verifyHashV1(string $message, string $hash, &$newHash = null): bool {
151
-		if(password_verify($message, $hash)) {
152
-			if ($this->needsRehash($hash)) {
153
-				$newHash = $this->hash($message);
154
-			}
155
-			return true;
156
-		}
157
-
158
-		return false;
159
-	}
160
-
161
-	/**
162
-	 * Verify V2 (argon2i) hashes
163
-	 * @param string $message Message to verify
164
-	 * @param string $hash Assumed hash of the message
165
-	 * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one.
166
-	 * @return bool Whether $hash is a valid hash of $message
167
-	 */
168
-	protected function verifyHashV2(string $message, string $hash, &$newHash = null) : bool {
169
-		if(password_verify($message, $hash)) {
170
-			if($this->needsRehash($hash)) {
171
-				$newHash = $this->hash($message);
172
-			}
173
-			return true;
174
-		}
175
-
176
-		return false;
177
-	}
178
-
179
-	/**
180
-	 * @param string $message Message to verify
181
-	 * @param string $hash Assumed hash of the message
182
-	 * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one.
183
-	 * @return bool Whether $hash is a valid hash of $message
184
-	 */
185
-	public function verify(string $message, string $hash, &$newHash = null): bool {
186
-		$splittedHash = $this->splitHash($hash);
187
-
188
-		if(isset($splittedHash['version'])) {
189
-			switch ($splittedHash['version']) {
190
-				case 2:
191
-					return $this->verifyHashV2($message, $splittedHash['hash'], $newHash);
192
-				case 1:
193
-					return $this->verifyHashV1($message, $splittedHash['hash'], $newHash);
194
-			}
195
-		} else {
196
-			return $this->legacyHashVerify($message, $hash, $newHash);
197
-		}
198
-
199
-		return false;
200
-	}
201
-
202
-	private function needsRehash(string $hash): bool {
203
-		$algorithm = $this->getPrefferedAlgorithm();
204
-
205
-		return password_needs_rehash($hash, $algorithm, $this->options);
206
-	}
207
-
208
-	private function getPrefferedAlgorithm() {
209
-		$default = PASSWORD_BCRYPT;
210
-		if (\defined('PASSWORD_ARGON2I')) {
211
-			$default = PASSWORD_ARGON2I;
212
-		}
213
-
214
-		// Check if we should use PASSWORD_DEFAULT
215
-		if ($this->config->getSystemValue('hashing_default_password', false) === true) {
216
-			$default = PASSWORD_DEFAULT;
217
-		}
218
-
219
-		return $default;
220
-	}
53
+    /** @var IConfig */
54
+    private $config;
55
+    /** @var array Options passed to password_hash and password_needs_rehash */
56
+    private $options = [];
57
+    /** @var string Salt used for legacy passwords */
58
+    private $legacySalt = null;
59
+
60
+    /**
61
+     * @param IConfig $config
62
+     */
63
+    public function __construct(IConfig $config) {
64
+        $this->config = $config;
65
+
66
+        if (\defined('PASSWORD_ARGON2I')) {
67
+            // password_hash fails, when the minimum values are undershot.
68
+            // In this case, ignore and revert to default
69
+            if ($this->config->getSystemValueInt('hashingMemoryCost', PASSWORD_ARGON2_DEFAULT_MEMORY_COST) >= 8) {
70
+                $this->options['memory_cost'] = $this->config->getSystemValueInt('hashingMemoryCost', PASSWORD_ARGON2_DEFAULT_MEMORY_COST);
71
+            }
72
+            if ($this->config->getSystemValueInt('hashingTimeCost', PASSWORD_ARGON2_DEFAULT_MEMORY_COST) >= 1) {
73
+                $this->options['time_cost'] = $this->config->getSystemValueInt('hashingTimeCost', PASSWORD_ARGON2_DEFAULT_TIME_COST);
74
+            }
75
+            if ($this->config->getSystemValueInt('hashingThreads', PASSWORD_ARGON2_DEFAULT_MEMORY_COST) >= 1) {
76
+                $this->options['threads'] = $this->config->getSystemValueInt('hashingThreads', PASSWORD_ARGON2_DEFAULT_THREADS);
77
+            }
78
+        }
79
+
80
+        $hashingCost = $this->config->getSystemValue('hashingCost', null);
81
+        if(!\is_null($hashingCost)) {
82
+            $this->options['cost'] = $hashingCost;
83
+        }
84
+    }
85
+
86
+    /**
87
+     * Hashes a message using PHP's `password_hash` functionality.
88
+     * Please note that the size of the returned string is not guaranteed
89
+     * and can be up to 255 characters.
90
+     *
91
+     * @param string $message Message to generate hash from
92
+     * @return string Hash of the message with appended version parameter
93
+     */
94
+    public function hash(string $message): string {
95
+        $alg = $this->getPrefferedAlgorithm();
96
+
97
+        if (\defined('PASSWORD_ARGON2I') && $alg === PASSWORD_ARGON2I) {
98
+            return 2 . '|' . password_hash($message, PASSWORD_ARGON2I, $this->options);
99
+        }
100
+
101
+        return 1 . '|' . password_hash($message, PASSWORD_BCRYPT, $this->options);
102
+    }
103
+
104
+    /**
105
+     * Get the version and hash from a prefixedHash
106
+     * @param string $prefixedHash
107
+     * @return null|array Null if the hash is not prefixed, otherwise array('version' => 1, 'hash' => 'foo')
108
+     */
109
+    protected function splitHash(string $prefixedHash) {
110
+        $explodedString = explode('|', $prefixedHash, 2);
111
+        if(\count($explodedString) === 2) {
112
+            if((int)$explodedString[0] > 0) {
113
+                return ['version' => (int)$explodedString[0], 'hash' => $explodedString[1]];
114
+            }
115
+        }
116
+
117
+        return null;
118
+    }
119
+
120
+    /**
121
+     * Verify legacy hashes
122
+     * @param string $message Message to verify
123
+     * @param string $hash Assumed hash of the message
124
+     * @param null|string &$newHash Reference will contain the updated hash
125
+     * @return bool Whether $hash is a valid hash of $message
126
+     */
127
+    protected function legacyHashVerify($message, $hash, &$newHash = null): bool {
128
+        if(empty($this->legacySalt)) {
129
+            $this->legacySalt = $this->config->getSystemValue('passwordsalt', '');
130
+        }
131
+
132
+        // Verify whether it matches a legacy PHPass or SHA1 string
133
+        $hashLength = \strlen($hash);
134
+        if(($hashLength === 60 && password_verify($message.$this->legacySalt, $hash)) ||
135
+            ($hashLength === 40 && hash_equals($hash, sha1($message)))) {
136
+            $newHash = $this->hash($message);
137
+            return true;
138
+        }
139
+
140
+        return false;
141
+    }
142
+
143
+    /**
144
+     * Verify V1 (blowfish) hashes
145
+     * @param string $message Message to verify
146
+     * @param string $hash Assumed hash of the message
147
+     * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one.
148
+     * @return bool Whether $hash is a valid hash of $message
149
+     */
150
+    protected function verifyHashV1(string $message, string $hash, &$newHash = null): bool {
151
+        if(password_verify($message, $hash)) {
152
+            if ($this->needsRehash($hash)) {
153
+                $newHash = $this->hash($message);
154
+            }
155
+            return true;
156
+        }
157
+
158
+        return false;
159
+    }
160
+
161
+    /**
162
+     * Verify V2 (argon2i) hashes
163
+     * @param string $message Message to verify
164
+     * @param string $hash Assumed hash of the message
165
+     * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one.
166
+     * @return bool Whether $hash is a valid hash of $message
167
+     */
168
+    protected function verifyHashV2(string $message, string $hash, &$newHash = null) : bool {
169
+        if(password_verify($message, $hash)) {
170
+            if($this->needsRehash($hash)) {
171
+                $newHash = $this->hash($message);
172
+            }
173
+            return true;
174
+        }
175
+
176
+        return false;
177
+    }
178
+
179
+    /**
180
+     * @param string $message Message to verify
181
+     * @param string $hash Assumed hash of the message
182
+     * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one.
183
+     * @return bool Whether $hash is a valid hash of $message
184
+     */
185
+    public function verify(string $message, string $hash, &$newHash = null): bool {
186
+        $splittedHash = $this->splitHash($hash);
187
+
188
+        if(isset($splittedHash['version'])) {
189
+            switch ($splittedHash['version']) {
190
+                case 2:
191
+                    return $this->verifyHashV2($message, $splittedHash['hash'], $newHash);
192
+                case 1:
193
+                    return $this->verifyHashV1($message, $splittedHash['hash'], $newHash);
194
+            }
195
+        } else {
196
+            return $this->legacyHashVerify($message, $hash, $newHash);
197
+        }
198
+
199
+        return false;
200
+    }
201
+
202
+    private function needsRehash(string $hash): bool {
203
+        $algorithm = $this->getPrefferedAlgorithm();
204
+
205
+        return password_needs_rehash($hash, $algorithm, $this->options);
206
+    }
207
+
208
+    private function getPrefferedAlgorithm() {
209
+        $default = PASSWORD_BCRYPT;
210
+        if (\defined('PASSWORD_ARGON2I')) {
211
+            $default = PASSWORD_ARGON2I;
212
+        }
213
+
214
+        // Check if we should use PASSWORD_DEFAULT
215
+        if ($this->config->getSystemValue('hashing_default_password', false) === true) {
216
+            $default = PASSWORD_DEFAULT;
217
+        }
218
+
219
+        return $default;
220
+    }
221 221
 
222 222
 }
Please login to merge, or discard this patch.