Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
91.67% |
22 / 24 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
BcryptPassword | |
91.67% |
22 / 24 |
|
75.00% |
3 / 4 |
7.03 | |
0.00% |
0 / 1 |
getDefaultParams | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getDelimiter | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
parseHash | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
crypt | |
88.89% |
16 / 18 |
|
0.00% |
0 / 1 |
4.02 |
1 | <?php |
2 | /** |
3 | * Implements the BcryptPassword class for the MediaWiki software. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | */ |
22 | |
23 | declare( strict_types = 1 ); |
24 | |
25 | /** |
26 | * A Bcrypt-hashed password |
27 | * |
28 | * This is a computationally complex password hash for use in modern applications. |
29 | * The number of rounds can be configured by $wgPasswordConfig['bcrypt']['cost']. |
30 | * |
31 | * @since 1.24 |
32 | */ |
33 | class BcryptPassword extends ParameterizedPassword { |
34 | protected function getDefaultParams(): array { |
35 | return [ |
36 | 'rounds' => $this->config['cost'], |
37 | ]; |
38 | } |
39 | |
40 | protected function getDelimiter(): string { |
41 | return '$'; |
42 | } |
43 | |
44 | protected function parseHash( ?string $hash ): void { |
45 | parent::parseHash( $hash ); |
46 | |
47 | $this->params['rounds'] = (int)$this->params['rounds']; |
48 | } |
49 | |
50 | /** |
51 | * @note Callers should make sure that bcrypt is available before calling this method. |
52 | * |
53 | * @param string $password Password to encrypt |
54 | * |
55 | * @throws PasswordError If bcrypt has an unknown error |
56 | */ |
57 | public function crypt( string $password ): void { |
58 | if ( !defined( 'CRYPT_BLOWFISH' ) ) { |
59 | throw new RuntimeException( 'Bcrypt is not supported.' ); |
60 | } |
61 | |
62 | // Either use existing hash or make a new salt |
63 | // Bcrypt expects 22 characters of base64-encoded salt |
64 | // Note: bcrypt does not use MIME base64. It uses its own base64 without any '=' padding. |
65 | // It expects a 128 bit salt, so it will ignore anything after the first 128 bits |
66 | if ( !isset( $this->args[0] ) ) { |
67 | $this->args[] = substr( |
68 | // Replace + with ., because bcrypt uses a non-MIME base64 format |
69 | strtr( |
70 | // Random base64 encoded string |
71 | base64_encode( random_bytes( 16 ) ), |
72 | '+', '.' |
73 | ), |
74 | 0, 22 |
75 | ); |
76 | } |
77 | |
78 | $hash = crypt( $password, |
79 | sprintf( '$2y$%02d$%s', (int)$this->params['rounds'], $this->args[0] ) ); |
80 | |
81 | if ( strlen( $hash ) <= 13 ) { |
82 | throw new PasswordError( 'Error when hashing password.' ); |
83 | } |
84 | |
85 | // Strip the $2y$ |
86 | $parts = explode( $this->getDelimiter(), substr( $hash, 4 ) ); |
87 | $this->params['rounds'] = (int)$parts[0]; |
88 | $this->args[0] = substr( $parts[1], 0, 22 ); |
89 | $this->hash = substr( $parts[1], 22 ); |
90 | } |
91 | } |