Passed
Push — master ( bba3a1...c550ac )
by Joas
17:32 queued 14s
created
lib/private/Authentication/Token/PublicKeyTokenProvider.php 2 patches
Indentation   +495 added lines, -495 removed lines patch added patch discarded remove patch
@@ -46,499 +46,499 @@
 block discarded – undo
46 46
 use Psr\Log\LoggerInterface;
47 47
 
48 48
 class PublicKeyTokenProvider implements IProvider {
49
-	public const TOKEN_MIN_LENGTH = 22;
50
-
51
-	use TTransactional;
52
-
53
-	/** @var PublicKeyTokenMapper */
54
-	private $mapper;
55
-
56
-	/** @var ICrypto */
57
-	private $crypto;
58
-
59
-	/** @var IConfig */
60
-	private $config;
61
-
62
-	private IDBConnection $db;
63
-
64
-	/** @var LoggerInterface */
65
-	private $logger;
66
-
67
-	/** @var ITimeFactory */
68
-	private $time;
69
-
70
-	/** @var CappedMemoryCache */
71
-	private $cache;
72
-
73
-	private IHasher $hasher;
74
-
75
-	public function __construct(PublicKeyTokenMapper $mapper,
76
-								ICrypto $crypto,
77
-								IConfig $config,
78
-								IDBConnection $db,
79
-								LoggerInterface $logger,
80
-								ITimeFactory $time,
81
-								IHasher $hasher) {
82
-		$this->mapper = $mapper;
83
-		$this->crypto = $crypto;
84
-		$this->config = $config;
85
-		$this->db = $db;
86
-		$this->logger = $logger;
87
-		$this->time = $time;
88
-
89
-		$this->cache = new CappedMemoryCache();
90
-		$this->hasher = $hasher;
91
-	}
92
-
93
-	/**
94
-	 * {@inheritDoc}
95
-	 */
96
-	public function generateToken(string $token,
97
-								  string $uid,
98
-								  string $loginName,
99
-								  ?string $password,
100
-								  string $name,
101
-								  int $type = IToken::TEMPORARY_TOKEN,
102
-								  int $remember = IToken::DO_NOT_REMEMBER): IToken {
103
-		if (strlen($token) < self::TOKEN_MIN_LENGTH) {
104
-			$exception = new InvalidTokenException('Token is too short, minimum of ' . self::TOKEN_MIN_LENGTH . ' characters is required, ' . strlen($token) . ' characters given');
105
-			$this->logger->error('Invalid token provided when generating new token', ['exception' => $exception]);
106
-			throw $exception;
107
-		}
108
-
109
-		if (mb_strlen($name) > 128) {
110
-			$name = mb_substr($name, 0, 120) . '…';
111
-		}
112
-
113
-		// We need to check against one old token to see if there is a password
114
-		// hash that we can reuse for detecting outdated passwords
115
-		$randomOldToken = $this->mapper->getFirstTokenForUser($uid);
116
-		$oldTokenMatches = $randomOldToken && $randomOldToken->getPasswordHash() && $this->hasher->verify(sha1($password) . $password, $randomOldToken->getPasswordHash());
117
-
118
-		$dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
119
-
120
-		if ($oldTokenMatches) {
121
-			$dbToken->setPasswordHash($randomOldToken->getPasswordHash());
122
-		}
123
-
124
-		$this->mapper->insert($dbToken);
125
-
126
-		if (!$oldTokenMatches && $password !== null) {
127
-			$this->updatePasswords($uid, $password);
128
-		}
129
-
130
-		// Add the token to the cache
131
-		$this->cache[$dbToken->getToken()] = $dbToken;
132
-
133
-		return $dbToken;
134
-	}
135
-
136
-	public function getToken(string $tokenId): IToken {
137
-		/**
138
-		 * Token length: 72
139
-		 * @see \OC\Core\Controller\ClientFlowLoginController::generateAppPassword
140
-		 * @see \OC\Core\Controller\AppPasswordController::getAppPassword
141
-		 * @see \OC\Core\Command\User\AddAppPassword::execute
142
-		 * @see \OC\Core\Service\LoginFlowV2Service::flowDone
143
-		 * @see \OCA\Talk\MatterbridgeManager::generatePassword
144
-		 * @see \OCA\Preferred_Providers\Controller\PasswordController::generateAppPassword
145
-		 * @see \OCA\GlobalSiteSelector\TokenHandler::generateAppPassword
146
-		 *
147
-		 * Token length: 22-256 - https://www.php.net/manual/en/session.configuration.php#ini.session.sid-length
148
-		 * @see \OC\User\Session::createSessionToken
149
-		 *
150
-		 * Token length: 29
151
-		 * @see \OCA\Settings\Controller\AuthSettingsController::generateRandomDeviceToken
152
-		 * @see \OCA\Registration\Service\RegistrationService::generateAppPassword
153
-		 */
154
-		if (strlen($tokenId) < self::TOKEN_MIN_LENGTH) {
155
-			throw new InvalidTokenException('Token is too short for a generated token, should be the password during basic auth');
156
-		}
157
-
158
-		$tokenHash = $this->hashToken($tokenId);
159
-
160
-		if (isset($this->cache[$tokenHash])) {
161
-			if ($this->cache[$tokenHash] instanceof DoesNotExistException) {
162
-				$ex = $this->cache[$tokenHash];
163
-				throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
164
-			}
165
-			$token = $this->cache[$tokenHash];
166
-		} else {
167
-			try {
168
-				$token = $this->mapper->getToken($tokenHash);
169
-				$this->cache[$token->getToken()] = $token;
170
-			} catch (DoesNotExistException $ex) {
171
-				try {
172
-					$token = $this->mapper->getToken($this->hashTokenWithEmptySecret($tokenId));
173
-					$this->cache[$token->getToken()] = $token;
174
-					$this->rotate($token, $tokenId, $tokenId);
175
-				} catch (DoesNotExistException $ex2) {
176
-					$this->cache[$tokenHash] = $ex2;
177
-					throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
178
-				}
179
-			}
180
-		}
181
-
182
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
183
-			throw new ExpiredTokenException($token);
184
-		}
185
-
186
-		if ($token->getType() === IToken::WIPE_TOKEN) {
187
-			throw new WipeTokenException($token);
188
-		}
189
-
190
-		if ($token->getPasswordInvalid() === true) {
191
-			//The password is invalid we should throw an TokenPasswordExpiredException
192
-			throw new TokenPasswordExpiredException($token);
193
-		}
194
-
195
-		return $token;
196
-	}
197
-
198
-	public function getTokenById(int $tokenId): IToken {
199
-		try {
200
-			$token = $this->mapper->getTokenById($tokenId);
201
-		} catch (DoesNotExistException $ex) {
202
-			throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex);
203
-		}
204
-
205
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
206
-			throw new ExpiredTokenException($token);
207
-		}
208
-
209
-		if ($token->getType() === IToken::WIPE_TOKEN) {
210
-			throw new WipeTokenException($token);
211
-		}
212
-
213
-		if ($token->getPasswordInvalid() === true) {
214
-			//The password is invalid we should throw an TokenPasswordExpiredException
215
-			throw new TokenPasswordExpiredException($token);
216
-		}
217
-
218
-		return $token;
219
-	}
220
-
221
-	public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
222
-		$this->cache->clear();
223
-
224
-		return $this->atomic(function () use ($oldSessionId, $sessionId) {
225
-			$token = $this->getToken($oldSessionId);
226
-
227
-			if (!($token instanceof PublicKeyToken)) {
228
-				throw new InvalidTokenException("Invalid token type");
229
-			}
230
-
231
-			$password = null;
232
-			if (!is_null($token->getPassword())) {
233
-				$privateKey = $this->decrypt($token->getPrivateKey(), $oldSessionId);
234
-				$password = $this->decryptPassword($token->getPassword(), $privateKey);
235
-			}
236
-			$newToken = $this->generateToken(
237
-				$sessionId,
238
-				$token->getUID(),
239
-				$token->getLoginName(),
240
-				$password,
241
-				$token->getName(),
242
-				IToken::TEMPORARY_TOKEN,
243
-				$token->getRemember()
244
-			);
245
-
246
-			$this->mapper->delete($token);
247
-
248
-			return $newToken;
249
-		}, $this->db);
250
-	}
251
-
252
-	public function invalidateToken(string $token) {
253
-		$this->cache->clear();
254
-
255
-		$this->mapper->invalidate($this->hashToken($token));
256
-		$this->mapper->invalidate($this->hashTokenWithEmptySecret($token));
257
-	}
258
-
259
-	public function invalidateTokenById(string $uid, int $id) {
260
-		$this->cache->clear();
261
-
262
-		$this->mapper->deleteById($uid, $id);
263
-	}
264
-
265
-	public function invalidateOldTokens() {
266
-		$this->cache->clear();
267
-
268
-		$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
269
-		$this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
270
-		$this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
271
-		$rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
272
-		$this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
273
-		$this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
274
-	}
275
-
276
-	public function updateToken(IToken $token) {
277
-		$this->cache->clear();
278
-
279
-		if (!($token instanceof PublicKeyToken)) {
280
-			throw new InvalidTokenException("Invalid token type");
281
-		}
282
-		$this->mapper->update($token);
283
-	}
284
-
285
-	public function updateTokenActivity(IToken $token) {
286
-		$this->cache->clear();
287
-
288
-		if (!($token instanceof PublicKeyToken)) {
289
-			throw new InvalidTokenException("Invalid token type");
290
-		}
291
-
292
-		$activityInterval = $this->config->getSystemValueInt('token_auth_activity_update', 60);
293
-		$activityInterval = min(max($activityInterval, 0), 300);
294
-
295
-		/** @var PublicKeyToken $token */
296
-		$now = $this->time->getTime();
297
-		if ($token->getLastActivity() < ($now - $activityInterval)) {
298
-			$token->setLastActivity($now);
299
-			$this->mapper->updateActivity($token, $now);
300
-		}
301
-	}
302
-
303
-	public function getTokenByUser(string $uid): array {
304
-		return $this->mapper->getTokenByUser($uid);
305
-	}
306
-
307
-	public function getPassword(IToken $savedToken, string $tokenId): string {
308
-		if (!($savedToken instanceof PublicKeyToken)) {
309
-			throw new InvalidTokenException("Invalid token type");
310
-		}
311
-
312
-		if ($savedToken->getPassword() === null) {
313
-			throw new PasswordlessTokenException();
314
-		}
315
-
316
-		// Decrypt private key with tokenId
317
-		$privateKey = $this->decrypt($savedToken->getPrivateKey(), $tokenId);
318
-
319
-		// Decrypt password with private key
320
-		return $this->decryptPassword($savedToken->getPassword(), $privateKey);
321
-	}
322
-
323
-	public function setPassword(IToken $token, string $tokenId, string $password) {
324
-		$this->cache->clear();
325
-
326
-		if (!($token instanceof PublicKeyToken)) {
327
-			throw new InvalidTokenException("Invalid token type");
328
-		}
329
-
330
-		// When changing passwords all temp tokens are deleted
331
-		$this->mapper->deleteTempToken($token);
332
-
333
-		// Update the password for all tokens
334
-		$tokens = $this->mapper->getTokenByUser($token->getUID());
335
-		$hashedPassword = $this->hashPassword($password);
336
-		foreach ($tokens as $t) {
337
-			$publicKey = $t->getPublicKey();
338
-			$t->setPassword($this->encryptPassword($password, $publicKey));
339
-			$t->setPasswordHash($hashedPassword);
340
-			$this->updateToken($t);
341
-		}
342
-	}
343
-
344
-	private function hashPassword(string $password): string {
345
-		return $this->hasher->hash(sha1($password) . $password);
346
-	}
347
-
348
-	public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
349
-		$this->cache->clear();
350
-
351
-		if (!($token instanceof PublicKeyToken)) {
352
-			throw new InvalidTokenException("Invalid token type");
353
-		}
354
-
355
-		// Decrypt private key with oldTokenId
356
-		$privateKey = $this->decrypt($token->getPrivateKey(), $oldTokenId);
357
-		// Encrypt with the new token
358
-		$token->setPrivateKey($this->encrypt($privateKey, $newTokenId));
359
-
360
-		$token->setToken($this->hashToken($newTokenId));
361
-		$this->updateToken($token);
362
-
363
-		return $token;
364
-	}
365
-
366
-	private function encrypt(string $plaintext, string $token): string {
367
-		$secret = $this->config->getSystemValue('secret');
368
-		return $this->crypto->encrypt($plaintext, $token . $secret);
369
-	}
370
-
371
-	/**
372
-	 * @throws InvalidTokenException
373
-	 */
374
-	private function decrypt(string $cipherText, string $token): string {
375
-		$secret = $this->config->getSystemValue('secret');
376
-		try {
377
-			return $this->crypto->decrypt($cipherText, $token . $secret);
378
-		} catch (\Exception $ex) {
379
-			// Retry with empty secret as a fallback for instances where the secret might not have been set by accident
380
-			try {
381
-				return $this->crypto->decrypt($cipherText, $token);
382
-			} catch (\Exception $ex2) {
383
-				// Delete the invalid token
384
-				$this->invalidateToken($token);
385
-				throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex2);
386
-			}
387
-		}
388
-	}
389
-
390
-	private function encryptPassword(string $password, string $publicKey): string {
391
-		openssl_public_encrypt($password, $encryptedPassword, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
392
-		$encryptedPassword = base64_encode($encryptedPassword);
393
-
394
-		return $encryptedPassword;
395
-	}
396
-
397
-	private function decryptPassword(string $encryptedPassword, string $privateKey): string {
398
-		$encryptedPassword = base64_decode($encryptedPassword);
399
-		openssl_private_decrypt($encryptedPassword, $password, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
400
-
401
-		return $password;
402
-	}
403
-
404
-	private function hashToken(string $token): string {
405
-		$secret = $this->config->getSystemValue('secret');
406
-		return hash('sha512', $token . $secret);
407
-	}
408
-
409
-	/**
410
-	 * @deprecated Fallback for instances where the secret might not have been set by accident
411
-	 */
412
-	private function hashTokenWithEmptySecret(string $token): string {
413
-		return hash('sha512', $token);
414
-	}
415
-
416
-	/**
417
-	 * @throws \RuntimeException when OpenSSL reports a problem
418
-	 */
419
-	private function newToken(string $token,
420
-							  string $uid,
421
-							  string $loginName,
422
-							  $password,
423
-							  string $name,
424
-							  int $type,
425
-							  int $remember): PublicKeyToken {
426
-		$dbToken = new PublicKeyToken();
427
-		$dbToken->setUid($uid);
428
-		$dbToken->setLoginName($loginName);
429
-
430
-		$config = array_merge([
431
-			'digest_alg' => 'sha512',
432
-			'private_key_bits' => $password !== null && strlen($password) > 250 ? 4096 : 2048,
433
-		], $this->config->getSystemValue('openssl', []));
434
-
435
-		// Generate new key
436
-		$res = openssl_pkey_new($config);
437
-		if ($res === false) {
438
-			$this->logOpensslError();
439
-			throw new \RuntimeException('OpenSSL reported a problem');
440
-		}
441
-
442
-		if (openssl_pkey_export($res, $privateKey, null, $config) === false) {
443
-			$this->logOpensslError();
444
-			throw new \RuntimeException('OpenSSL reported a problem');
445
-		}
446
-
447
-		// Extract the public key from $res to $pubKey
448
-		$publicKey = openssl_pkey_get_details($res);
449
-		$publicKey = $publicKey['key'];
450
-
451
-		$dbToken->setPublicKey($publicKey);
452
-		$dbToken->setPrivateKey($this->encrypt($privateKey, $token));
453
-
454
-		if (!is_null($password) && $this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
455
-			if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
456
-				throw new \RuntimeException('Trying to save a password with more than 469 characters is not supported. If you want to use big passwords, disable the auth.storeCryptedPassword option in config.php');
457
-			}
458
-			$dbToken->setPassword($this->encryptPassword($password, $publicKey));
459
-			$dbToken->setPasswordHash($this->hashPassword($password));
460
-		}
461
-
462
-		$dbToken->setName($name);
463
-		$dbToken->setToken($this->hashToken($token));
464
-		$dbToken->setType($type);
465
-		$dbToken->setRemember($remember);
466
-		$dbToken->setLastActivity($this->time->getTime());
467
-		$dbToken->setLastCheck($this->time->getTime());
468
-		$dbToken->setVersion(PublicKeyToken::VERSION);
469
-
470
-		return $dbToken;
471
-	}
472
-
473
-	public function markPasswordInvalid(IToken $token, string $tokenId) {
474
-		$this->cache->clear();
475
-
476
-		if (!($token instanceof PublicKeyToken)) {
477
-			throw new InvalidTokenException("Invalid token type");
478
-		}
479
-
480
-		$token->setPasswordInvalid(true);
481
-		$this->mapper->update($token);
482
-	}
483
-
484
-	public function updatePasswords(string $uid, string $password) {
485
-		$this->cache->clear();
486
-
487
-		// prevent setting an empty pw as result of pw-less-login
488
-		if ($password === '' || !$this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
489
-			return;
490
-		}
491
-
492
-		// Update the password for all tokens
493
-		$tokens = $this->mapper->getTokenByUser($uid);
494
-		$newPasswordHash = null;
495
-
496
-		/**
497
-		 * - true: The password hash could not be verified anymore
498
-		 *     and the token needs to be updated with the newly encrypted password
499
-		 * - false: The hash could still be verified
500
-		 * - missing: The hash needs to be verified
501
-		 */
502
-		$hashNeedsUpdate = [];
503
-
504
-		foreach ($tokens as $t) {
505
-			if (!isset($hashNeedsUpdate[$t->getPasswordHash()])) {
506
-				if ($t->getPasswordHash() === null) {
507
-					$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
508
-				} elseif (!$this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) {
509
-					$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
510
-				} else {
511
-					$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = false;
512
-				}
513
-			}
514
-			$needsUpdating = $hashNeedsUpdate[$t->getPasswordHash() ?: ''] ?? true;
515
-
516
-			if ($needsUpdating) {
517
-				if ($newPasswordHash === null) {
518
-					$newPasswordHash = $this->hashPassword($password);
519
-				}
520
-
521
-				$publicKey = $t->getPublicKey();
522
-				$t->setPassword($this->encryptPassword($password, $publicKey));
523
-				$t->setPasswordHash($newPasswordHash);
524
-				$t->setPasswordInvalid(false);
525
-				$this->updateToken($t);
526
-			}
527
-		}
528
-
529
-		// If password hashes are different we update them all to be equal so
530
-		// that the next execution only needs to verify once
531
-		if (count($hashNeedsUpdate) > 1) {
532
-			$newPasswordHash = $this->hashPassword($password);
533
-			$this->mapper->updateHashesForUser($uid, $newPasswordHash);
534
-		}
535
-	}
536
-
537
-	private function logOpensslError() {
538
-		$errors = [];
539
-		while ($error = openssl_error_string()) {
540
-			$errors[] = $error;
541
-		}
542
-		$this->logger->critical('Something is wrong with your openssl setup: ' . implode(', ', $errors));
543
-	}
49
+    public const TOKEN_MIN_LENGTH = 22;
50
+
51
+    use TTransactional;
52
+
53
+    /** @var PublicKeyTokenMapper */
54
+    private $mapper;
55
+
56
+    /** @var ICrypto */
57
+    private $crypto;
58
+
59
+    /** @var IConfig */
60
+    private $config;
61
+
62
+    private IDBConnection $db;
63
+
64
+    /** @var LoggerInterface */
65
+    private $logger;
66
+
67
+    /** @var ITimeFactory */
68
+    private $time;
69
+
70
+    /** @var CappedMemoryCache */
71
+    private $cache;
72
+
73
+    private IHasher $hasher;
74
+
75
+    public function __construct(PublicKeyTokenMapper $mapper,
76
+                                ICrypto $crypto,
77
+                                IConfig $config,
78
+                                IDBConnection $db,
79
+                                LoggerInterface $logger,
80
+                                ITimeFactory $time,
81
+                                IHasher $hasher) {
82
+        $this->mapper = $mapper;
83
+        $this->crypto = $crypto;
84
+        $this->config = $config;
85
+        $this->db = $db;
86
+        $this->logger = $logger;
87
+        $this->time = $time;
88
+
89
+        $this->cache = new CappedMemoryCache();
90
+        $this->hasher = $hasher;
91
+    }
92
+
93
+    /**
94
+     * {@inheritDoc}
95
+     */
96
+    public function generateToken(string $token,
97
+                                    string $uid,
98
+                                    string $loginName,
99
+                                  ?string $password,
100
+                                    string $name,
101
+                                    int $type = IToken::TEMPORARY_TOKEN,
102
+                                    int $remember = IToken::DO_NOT_REMEMBER): IToken {
103
+        if (strlen($token) < self::TOKEN_MIN_LENGTH) {
104
+            $exception = new InvalidTokenException('Token is too short, minimum of ' . self::TOKEN_MIN_LENGTH . ' characters is required, ' . strlen($token) . ' characters given');
105
+            $this->logger->error('Invalid token provided when generating new token', ['exception' => $exception]);
106
+            throw $exception;
107
+        }
108
+
109
+        if (mb_strlen($name) > 128) {
110
+            $name = mb_substr($name, 0, 120) . '…';
111
+        }
112
+
113
+        // We need to check against one old token to see if there is a password
114
+        // hash that we can reuse for detecting outdated passwords
115
+        $randomOldToken = $this->mapper->getFirstTokenForUser($uid);
116
+        $oldTokenMatches = $randomOldToken && $randomOldToken->getPasswordHash() && $this->hasher->verify(sha1($password) . $password, $randomOldToken->getPasswordHash());
117
+
118
+        $dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
119
+
120
+        if ($oldTokenMatches) {
121
+            $dbToken->setPasswordHash($randomOldToken->getPasswordHash());
122
+        }
123
+
124
+        $this->mapper->insert($dbToken);
125
+
126
+        if (!$oldTokenMatches && $password !== null) {
127
+            $this->updatePasswords($uid, $password);
128
+        }
129
+
130
+        // Add the token to the cache
131
+        $this->cache[$dbToken->getToken()] = $dbToken;
132
+
133
+        return $dbToken;
134
+    }
135
+
136
+    public function getToken(string $tokenId): IToken {
137
+        /**
138
+         * Token length: 72
139
+         * @see \OC\Core\Controller\ClientFlowLoginController::generateAppPassword
140
+         * @see \OC\Core\Controller\AppPasswordController::getAppPassword
141
+         * @see \OC\Core\Command\User\AddAppPassword::execute
142
+         * @see \OC\Core\Service\LoginFlowV2Service::flowDone
143
+         * @see \OCA\Talk\MatterbridgeManager::generatePassword
144
+         * @see \OCA\Preferred_Providers\Controller\PasswordController::generateAppPassword
145
+         * @see \OCA\GlobalSiteSelector\TokenHandler::generateAppPassword
146
+         *
147
+         * Token length: 22-256 - https://www.php.net/manual/en/session.configuration.php#ini.session.sid-length
148
+         * @see \OC\User\Session::createSessionToken
149
+         *
150
+         * Token length: 29
151
+         * @see \OCA\Settings\Controller\AuthSettingsController::generateRandomDeviceToken
152
+         * @see \OCA\Registration\Service\RegistrationService::generateAppPassword
153
+         */
154
+        if (strlen($tokenId) < self::TOKEN_MIN_LENGTH) {
155
+            throw new InvalidTokenException('Token is too short for a generated token, should be the password during basic auth');
156
+        }
157
+
158
+        $tokenHash = $this->hashToken($tokenId);
159
+
160
+        if (isset($this->cache[$tokenHash])) {
161
+            if ($this->cache[$tokenHash] instanceof DoesNotExistException) {
162
+                $ex = $this->cache[$tokenHash];
163
+                throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
164
+            }
165
+            $token = $this->cache[$tokenHash];
166
+        } else {
167
+            try {
168
+                $token = $this->mapper->getToken($tokenHash);
169
+                $this->cache[$token->getToken()] = $token;
170
+            } catch (DoesNotExistException $ex) {
171
+                try {
172
+                    $token = $this->mapper->getToken($this->hashTokenWithEmptySecret($tokenId));
173
+                    $this->cache[$token->getToken()] = $token;
174
+                    $this->rotate($token, $tokenId, $tokenId);
175
+                } catch (DoesNotExistException $ex2) {
176
+                    $this->cache[$tokenHash] = $ex2;
177
+                    throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
178
+                }
179
+            }
180
+        }
181
+
182
+        if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
183
+            throw new ExpiredTokenException($token);
184
+        }
185
+
186
+        if ($token->getType() === IToken::WIPE_TOKEN) {
187
+            throw new WipeTokenException($token);
188
+        }
189
+
190
+        if ($token->getPasswordInvalid() === true) {
191
+            //The password is invalid we should throw an TokenPasswordExpiredException
192
+            throw new TokenPasswordExpiredException($token);
193
+        }
194
+
195
+        return $token;
196
+    }
197
+
198
+    public function getTokenById(int $tokenId): IToken {
199
+        try {
200
+            $token = $this->mapper->getTokenById($tokenId);
201
+        } catch (DoesNotExistException $ex) {
202
+            throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex);
203
+        }
204
+
205
+        if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
206
+            throw new ExpiredTokenException($token);
207
+        }
208
+
209
+        if ($token->getType() === IToken::WIPE_TOKEN) {
210
+            throw new WipeTokenException($token);
211
+        }
212
+
213
+        if ($token->getPasswordInvalid() === true) {
214
+            //The password is invalid we should throw an TokenPasswordExpiredException
215
+            throw new TokenPasswordExpiredException($token);
216
+        }
217
+
218
+        return $token;
219
+    }
220
+
221
+    public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
222
+        $this->cache->clear();
223
+
224
+        return $this->atomic(function () use ($oldSessionId, $sessionId) {
225
+            $token = $this->getToken($oldSessionId);
226
+
227
+            if (!($token instanceof PublicKeyToken)) {
228
+                throw new InvalidTokenException("Invalid token type");
229
+            }
230
+
231
+            $password = null;
232
+            if (!is_null($token->getPassword())) {
233
+                $privateKey = $this->decrypt($token->getPrivateKey(), $oldSessionId);
234
+                $password = $this->decryptPassword($token->getPassword(), $privateKey);
235
+            }
236
+            $newToken = $this->generateToken(
237
+                $sessionId,
238
+                $token->getUID(),
239
+                $token->getLoginName(),
240
+                $password,
241
+                $token->getName(),
242
+                IToken::TEMPORARY_TOKEN,
243
+                $token->getRemember()
244
+            );
245
+
246
+            $this->mapper->delete($token);
247
+
248
+            return $newToken;
249
+        }, $this->db);
250
+    }
251
+
252
+    public function invalidateToken(string $token) {
253
+        $this->cache->clear();
254
+
255
+        $this->mapper->invalidate($this->hashToken($token));
256
+        $this->mapper->invalidate($this->hashTokenWithEmptySecret($token));
257
+    }
258
+
259
+    public function invalidateTokenById(string $uid, int $id) {
260
+        $this->cache->clear();
261
+
262
+        $this->mapper->deleteById($uid, $id);
263
+    }
264
+
265
+    public function invalidateOldTokens() {
266
+        $this->cache->clear();
267
+
268
+        $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
269
+        $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
270
+        $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
271
+        $rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
272
+        $this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
273
+        $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
274
+    }
275
+
276
+    public function updateToken(IToken $token) {
277
+        $this->cache->clear();
278
+
279
+        if (!($token instanceof PublicKeyToken)) {
280
+            throw new InvalidTokenException("Invalid token type");
281
+        }
282
+        $this->mapper->update($token);
283
+    }
284
+
285
+    public function updateTokenActivity(IToken $token) {
286
+        $this->cache->clear();
287
+
288
+        if (!($token instanceof PublicKeyToken)) {
289
+            throw new InvalidTokenException("Invalid token type");
290
+        }
291
+
292
+        $activityInterval = $this->config->getSystemValueInt('token_auth_activity_update', 60);
293
+        $activityInterval = min(max($activityInterval, 0), 300);
294
+
295
+        /** @var PublicKeyToken $token */
296
+        $now = $this->time->getTime();
297
+        if ($token->getLastActivity() < ($now - $activityInterval)) {
298
+            $token->setLastActivity($now);
299
+            $this->mapper->updateActivity($token, $now);
300
+        }
301
+    }
302
+
303
+    public function getTokenByUser(string $uid): array {
304
+        return $this->mapper->getTokenByUser($uid);
305
+    }
306
+
307
+    public function getPassword(IToken $savedToken, string $tokenId): string {
308
+        if (!($savedToken instanceof PublicKeyToken)) {
309
+            throw new InvalidTokenException("Invalid token type");
310
+        }
311
+
312
+        if ($savedToken->getPassword() === null) {
313
+            throw new PasswordlessTokenException();
314
+        }
315
+
316
+        // Decrypt private key with tokenId
317
+        $privateKey = $this->decrypt($savedToken->getPrivateKey(), $tokenId);
318
+
319
+        // Decrypt password with private key
320
+        return $this->decryptPassword($savedToken->getPassword(), $privateKey);
321
+    }
322
+
323
+    public function setPassword(IToken $token, string $tokenId, string $password) {
324
+        $this->cache->clear();
325
+
326
+        if (!($token instanceof PublicKeyToken)) {
327
+            throw new InvalidTokenException("Invalid token type");
328
+        }
329
+
330
+        // When changing passwords all temp tokens are deleted
331
+        $this->mapper->deleteTempToken($token);
332
+
333
+        // Update the password for all tokens
334
+        $tokens = $this->mapper->getTokenByUser($token->getUID());
335
+        $hashedPassword = $this->hashPassword($password);
336
+        foreach ($tokens as $t) {
337
+            $publicKey = $t->getPublicKey();
338
+            $t->setPassword($this->encryptPassword($password, $publicKey));
339
+            $t->setPasswordHash($hashedPassword);
340
+            $this->updateToken($t);
341
+        }
342
+    }
343
+
344
+    private function hashPassword(string $password): string {
345
+        return $this->hasher->hash(sha1($password) . $password);
346
+    }
347
+
348
+    public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
349
+        $this->cache->clear();
350
+
351
+        if (!($token instanceof PublicKeyToken)) {
352
+            throw new InvalidTokenException("Invalid token type");
353
+        }
354
+
355
+        // Decrypt private key with oldTokenId
356
+        $privateKey = $this->decrypt($token->getPrivateKey(), $oldTokenId);
357
+        // Encrypt with the new token
358
+        $token->setPrivateKey($this->encrypt($privateKey, $newTokenId));
359
+
360
+        $token->setToken($this->hashToken($newTokenId));
361
+        $this->updateToken($token);
362
+
363
+        return $token;
364
+    }
365
+
366
+    private function encrypt(string $plaintext, string $token): string {
367
+        $secret = $this->config->getSystemValue('secret');
368
+        return $this->crypto->encrypt($plaintext, $token . $secret);
369
+    }
370
+
371
+    /**
372
+     * @throws InvalidTokenException
373
+     */
374
+    private function decrypt(string $cipherText, string $token): string {
375
+        $secret = $this->config->getSystemValue('secret');
376
+        try {
377
+            return $this->crypto->decrypt($cipherText, $token . $secret);
378
+        } catch (\Exception $ex) {
379
+            // Retry with empty secret as a fallback for instances where the secret might not have been set by accident
380
+            try {
381
+                return $this->crypto->decrypt($cipherText, $token);
382
+            } catch (\Exception $ex2) {
383
+                // Delete the invalid token
384
+                $this->invalidateToken($token);
385
+                throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex2);
386
+            }
387
+        }
388
+    }
389
+
390
+    private function encryptPassword(string $password, string $publicKey): string {
391
+        openssl_public_encrypt($password, $encryptedPassword, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
392
+        $encryptedPassword = base64_encode($encryptedPassword);
393
+
394
+        return $encryptedPassword;
395
+    }
396
+
397
+    private function decryptPassword(string $encryptedPassword, string $privateKey): string {
398
+        $encryptedPassword = base64_decode($encryptedPassword);
399
+        openssl_private_decrypt($encryptedPassword, $password, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
400
+
401
+        return $password;
402
+    }
403
+
404
+    private function hashToken(string $token): string {
405
+        $secret = $this->config->getSystemValue('secret');
406
+        return hash('sha512', $token . $secret);
407
+    }
408
+
409
+    /**
410
+     * @deprecated Fallback for instances where the secret might not have been set by accident
411
+     */
412
+    private function hashTokenWithEmptySecret(string $token): string {
413
+        return hash('sha512', $token);
414
+    }
415
+
416
+    /**
417
+     * @throws \RuntimeException when OpenSSL reports a problem
418
+     */
419
+    private function newToken(string $token,
420
+                                string $uid,
421
+                                string $loginName,
422
+                                $password,
423
+                                string $name,
424
+                                int $type,
425
+                                int $remember): PublicKeyToken {
426
+        $dbToken = new PublicKeyToken();
427
+        $dbToken->setUid($uid);
428
+        $dbToken->setLoginName($loginName);
429
+
430
+        $config = array_merge([
431
+            'digest_alg' => 'sha512',
432
+            'private_key_bits' => $password !== null && strlen($password) > 250 ? 4096 : 2048,
433
+        ], $this->config->getSystemValue('openssl', []));
434
+
435
+        // Generate new key
436
+        $res = openssl_pkey_new($config);
437
+        if ($res === false) {
438
+            $this->logOpensslError();
439
+            throw new \RuntimeException('OpenSSL reported a problem');
440
+        }
441
+
442
+        if (openssl_pkey_export($res, $privateKey, null, $config) === false) {
443
+            $this->logOpensslError();
444
+            throw new \RuntimeException('OpenSSL reported a problem');
445
+        }
446
+
447
+        // Extract the public key from $res to $pubKey
448
+        $publicKey = openssl_pkey_get_details($res);
449
+        $publicKey = $publicKey['key'];
450
+
451
+        $dbToken->setPublicKey($publicKey);
452
+        $dbToken->setPrivateKey($this->encrypt($privateKey, $token));
453
+
454
+        if (!is_null($password) && $this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
455
+            if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
456
+                throw new \RuntimeException('Trying to save a password with more than 469 characters is not supported. If you want to use big passwords, disable the auth.storeCryptedPassword option in config.php');
457
+            }
458
+            $dbToken->setPassword($this->encryptPassword($password, $publicKey));
459
+            $dbToken->setPasswordHash($this->hashPassword($password));
460
+        }
461
+
462
+        $dbToken->setName($name);
463
+        $dbToken->setToken($this->hashToken($token));
464
+        $dbToken->setType($type);
465
+        $dbToken->setRemember($remember);
466
+        $dbToken->setLastActivity($this->time->getTime());
467
+        $dbToken->setLastCheck($this->time->getTime());
468
+        $dbToken->setVersion(PublicKeyToken::VERSION);
469
+
470
+        return $dbToken;
471
+    }
472
+
473
+    public function markPasswordInvalid(IToken $token, string $tokenId) {
474
+        $this->cache->clear();
475
+
476
+        if (!($token instanceof PublicKeyToken)) {
477
+            throw new InvalidTokenException("Invalid token type");
478
+        }
479
+
480
+        $token->setPasswordInvalid(true);
481
+        $this->mapper->update($token);
482
+    }
483
+
484
+    public function updatePasswords(string $uid, string $password) {
485
+        $this->cache->clear();
486
+
487
+        // prevent setting an empty pw as result of pw-less-login
488
+        if ($password === '' || !$this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
489
+            return;
490
+        }
491
+
492
+        // Update the password for all tokens
493
+        $tokens = $this->mapper->getTokenByUser($uid);
494
+        $newPasswordHash = null;
495
+
496
+        /**
497
+         * - true: The password hash could not be verified anymore
498
+         *     and the token needs to be updated with the newly encrypted password
499
+         * - false: The hash could still be verified
500
+         * - missing: The hash needs to be verified
501
+         */
502
+        $hashNeedsUpdate = [];
503
+
504
+        foreach ($tokens as $t) {
505
+            if (!isset($hashNeedsUpdate[$t->getPasswordHash()])) {
506
+                if ($t->getPasswordHash() === null) {
507
+                    $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
508
+                } elseif (!$this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) {
509
+                    $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
510
+                } else {
511
+                    $hashNeedsUpdate[$t->getPasswordHash() ?: ''] = false;
512
+                }
513
+            }
514
+            $needsUpdating = $hashNeedsUpdate[$t->getPasswordHash() ?: ''] ?? true;
515
+
516
+            if ($needsUpdating) {
517
+                if ($newPasswordHash === null) {
518
+                    $newPasswordHash = $this->hashPassword($password);
519
+                }
520
+
521
+                $publicKey = $t->getPublicKey();
522
+                $t->setPassword($this->encryptPassword($password, $publicKey));
523
+                $t->setPasswordHash($newPasswordHash);
524
+                $t->setPasswordInvalid(false);
525
+                $this->updateToken($t);
526
+            }
527
+        }
528
+
529
+        // If password hashes are different we update them all to be equal so
530
+        // that the next execution only needs to verify once
531
+        if (count($hashNeedsUpdate) > 1) {
532
+            $newPasswordHash = $this->hashPassword($password);
533
+            $this->mapper->updateHashesForUser($uid, $newPasswordHash);
534
+        }
535
+    }
536
+
537
+    private function logOpensslError() {
538
+        $errors = [];
539
+        while ($error = openssl_error_string()) {
540
+            $errors[] = $error;
541
+        }
542
+        $this->logger->critical('Something is wrong with your openssl setup: ' . implode(', ', $errors));
543
+    }
544 544
 }
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -101,19 +101,19 @@  discard block
 block discarded – undo
101 101
 								  int $type = IToken::TEMPORARY_TOKEN,
102 102
 								  int $remember = IToken::DO_NOT_REMEMBER): IToken {
103 103
 		if (strlen($token) < self::TOKEN_MIN_LENGTH) {
104
-			$exception = new InvalidTokenException('Token is too short, minimum of ' . self::TOKEN_MIN_LENGTH . ' characters is required, ' . strlen($token) . ' characters given');
104
+			$exception = new InvalidTokenException('Token is too short, minimum of '.self::TOKEN_MIN_LENGTH.' characters is required, '.strlen($token).' characters given');
105 105
 			$this->logger->error('Invalid token provided when generating new token', ['exception' => $exception]);
106 106
 			throw $exception;
107 107
 		}
108 108
 
109 109
 		if (mb_strlen($name) > 128) {
110
-			$name = mb_substr($name, 0, 120) . '…';
110
+			$name = mb_substr($name, 0, 120).'…';
111 111
 		}
112 112
 
113 113
 		// We need to check against one old token to see if there is a password
114 114
 		// hash that we can reuse for detecting outdated passwords
115 115
 		$randomOldToken = $this->mapper->getFirstTokenForUser($uid);
116
-		$oldTokenMatches = $randomOldToken && $randomOldToken->getPasswordHash() && $this->hasher->verify(sha1($password) . $password, $randomOldToken->getPasswordHash());
116
+		$oldTokenMatches = $randomOldToken && $randomOldToken->getPasswordHash() && $this->hasher->verify(sha1($password).$password, $randomOldToken->getPasswordHash());
117 117
 
118 118
 		$dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
119 119
 
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
 		if (isset($this->cache[$tokenHash])) {
161 161
 			if ($this->cache[$tokenHash] instanceof DoesNotExistException) {
162 162
 				$ex = $this->cache[$tokenHash];
163
-				throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
163
+				throw new InvalidTokenException("Token does not exist: ".$ex->getMessage(), 0, $ex);
164 164
 			}
165 165
 			$token = $this->cache[$tokenHash];
166 166
 		} else {
@@ -174,12 +174,12 @@  discard block
 block discarded – undo
174 174
 					$this->rotate($token, $tokenId, $tokenId);
175 175
 				} catch (DoesNotExistException $ex2) {
176 176
 					$this->cache[$tokenHash] = $ex2;
177
-					throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
177
+					throw new InvalidTokenException("Token does not exist: ".$ex->getMessage(), 0, $ex);
178 178
 				}
179 179
 			}
180 180
 		}
181 181
 
182
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
182
+		if ((int) $token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
183 183
 			throw new ExpiredTokenException($token);
184 184
 		}
185 185
 
@@ -199,10 +199,10 @@  discard block
 block discarded – undo
199 199
 		try {
200 200
 			$token = $this->mapper->getTokenById($tokenId);
201 201
 		} catch (DoesNotExistException $ex) {
202
-			throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex);
202
+			throw new InvalidTokenException("Token with ID $tokenId does not exist: ".$ex->getMessage(), 0, $ex);
203 203
 		}
204 204
 
205
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
205
+		if ((int) $token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
206 206
 			throw new ExpiredTokenException($token);
207 207
 		}
208 208
 
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
 	public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
222 222
 		$this->cache->clear();
223 223
 
224
-		return $this->atomic(function () use ($oldSessionId, $sessionId) {
224
+		return $this->atomic(function() use ($oldSessionId, $sessionId) {
225 225
 			$token = $this->getToken($oldSessionId);
226 226
 
227 227
 			if (!($token instanceof PublicKeyToken)) {
@@ -266,10 +266,10 @@  discard block
 block discarded – undo
266 266
 		$this->cache->clear();
267 267
 
268 268
 		$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
269
-		$this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
269
+		$this->logger->debug('Invalidating session tokens older than '.date('c', $olderThan), ['app' => 'cron']);
270 270
 		$this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
271 271
 		$rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
272
-		$this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
272
+		$this->logger->debug('Invalidating remembered session tokens older than '.date('c', $rememberThreshold), ['app' => 'cron']);
273 273
 		$this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
274 274
 	}
275 275
 
@@ -342,7 +342,7 @@  discard block
 block discarded – undo
342 342
 	}
343 343
 
344 344
 	private function hashPassword(string $password): string {
345
-		return $this->hasher->hash(sha1($password) . $password);
345
+		return $this->hasher->hash(sha1($password).$password);
346 346
 	}
347 347
 
348 348
 	public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
@@ -365,7 +365,7 @@  discard block
 block discarded – undo
365 365
 
366 366
 	private function encrypt(string $plaintext, string $token): string {
367 367
 		$secret = $this->config->getSystemValue('secret');
368
-		return $this->crypto->encrypt($plaintext, $token . $secret);
368
+		return $this->crypto->encrypt($plaintext, $token.$secret);
369 369
 	}
370 370
 
371 371
 	/**
@@ -374,7 +374,7 @@  discard block
 block discarded – undo
374 374
 	private function decrypt(string $cipherText, string $token): string {
375 375
 		$secret = $this->config->getSystemValue('secret');
376 376
 		try {
377
-			return $this->crypto->decrypt($cipherText, $token . $secret);
377
+			return $this->crypto->decrypt($cipherText, $token.$secret);
378 378
 		} catch (\Exception $ex) {
379 379
 			// Retry with empty secret as a fallback for instances where the secret might not have been set by accident
380 380
 			try {
@@ -382,7 +382,7 @@  discard block
 block discarded – undo
382 382
 			} catch (\Exception $ex2) {
383 383
 				// Delete the invalid token
384 384
 				$this->invalidateToken($token);
385
-				throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex2);
385
+				throw new InvalidTokenException("Could not decrypt token password: ".$ex->getMessage(), 0, $ex2);
386 386
 			}
387 387
 		}
388 388
 	}
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
 
404 404
 	private function hashToken(string $token): string {
405 405
 		$secret = $this->config->getSystemValue('secret');
406
-		return hash('sha512', $token . $secret);
406
+		return hash('sha512', $token.$secret);
407 407
 	}
408 408
 
409 409
 	/**
@@ -505,7 +505,7 @@  discard block
 block discarded – undo
505 505
 			if (!isset($hashNeedsUpdate[$t->getPasswordHash()])) {
506 506
 				if ($t->getPasswordHash() === null) {
507 507
 					$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
508
-				} elseif (!$this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) {
508
+				} elseif (!$this->hasher->verify(sha1($password).$password, $t->getPasswordHash())) {
509 509
 					$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
510 510
 				} else {
511 511
 					$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = false;
@@ -539,6 +539,6 @@  discard block
 block discarded – undo
539 539
 		while ($error = openssl_error_string()) {
540 540
 			$errors[] = $error;
541 541
 		}
542
-		$this->logger->critical('Something is wrong with your openssl setup: ' . implode(', ', $errors));
542
+		$this->logger->critical('Something is wrong with your openssl setup: '.implode(', ', $errors));
543 543
 	}
544 544
 }
Please login to merge, or discard this patch.