Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / password / LayeredParameterizedPassword.php
blob1adb49f037c0ebb2f3efd5e55a7cfb1fde2eac4a
1 <?php
2 /**
3 * Implements the LayeredParameterizedPassword class for the MediaWiki software.
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.
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.
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
20 * @file
23 declare( strict_types = 1 );
25 namespace MediaWiki\Password;
27 use InvalidArgumentException;
28 use UnexpectedValueException;
30 /**
31 * This password hash type layers one or more parameterized password types
32 * on top of each other.
34 * The underlying types must be parameterized. This wrapping type accumulates
35 * all the parameters and arguments from each hash and then passes the hash of
36 * the last layer as the password for the next layer.
38 * @since 1.24
40 class LayeredParameterizedPassword extends ParameterizedPassword {
41 protected function getDelimiter(): string {
42 return '!';
45 protected function getDefaultParams(): array {
46 $params = [];
48 foreach ( $this->config['types'] as $type ) {
49 $passObj = $this->factory->newFromType( $type );
51 if ( !$passObj instanceof ParameterizedPassword ) {
52 throw new UnexpectedValueException( 'Underlying type must be a parameterized password.' );
53 } elseif ( $passObj->getDelimiter() === $this->getDelimiter() ) {
54 throw new UnexpectedValueException(
55 'Underlying type cannot use same delimiter as encapsulating type.'
59 $params[] = implode( $passObj->getDelimiter(), $passObj->getDefaultParams() );
62 return $params;
65 public function crypt( string $password ): void {
66 $lastHash = $password;
67 foreach ( $this->config['types'] as $i => $type ) {
68 // Construct pseudo-hash based on params and arguments
69 /** @var ParameterizedPassword $passObj */
70 $passObj = $this->factory->newFromType( $type );
71 '@phan-var ParameterizedPassword $passObj';
73 $params = '';
74 $args = '';
75 if ( $this->params[$i] !== '' ) {
76 $params = $this->params[$i] . $passObj->getDelimiter();
78 if ( isset( $this->args[$i] ) && $this->args[$i] !== '' ) {
79 $args = $this->args[$i] . $passObj->getDelimiter();
81 $existingHash = ":$type:" . $params . $args . $this->hash;
83 // Hash the last hash with the next type in the layer
84 $passObj = $this->factory->newFromCiphertext( $existingHash );
85 '@phan-var ParameterizedPassword $passObj';
86 $passObj->crypt( $lastHash );
88 // Move over the params and args
89 $this->params[$i] = implode( $passObj->getDelimiter(), $passObj->params );
90 $this->args[$i] = implode( $passObj->getDelimiter(), $passObj->args );
91 $lastHash = $passObj->hash;
94 $this->hash = $lastHash;
97 /**
98 * Finish the hashing of a partially hashed layered hash
100 * Given a password hash that is hashed using the first layer of this object's
101 * configuration, perform the remaining layers of password hashing in order to
102 * get an updated hash with all the layers.
104 * @param ParameterizedPassword $passObj Password hash of the first layer
106 public function partialCrypt( ParameterizedPassword $passObj ) {
107 $type = $passObj->config['type'];
108 if ( $type !== $this->config['types'][0] ) {
109 throw new InvalidArgumentException( 'Only a hash in the first layer can be finished.' );
112 // Gather info from the existing hash
113 $this->params[0] = implode( $passObj->getDelimiter(), $passObj->params );
114 $this->args[0] = implode( $passObj->getDelimiter(), $passObj->args );
115 $lastHash = $passObj->hash;
117 // Layer the remaining types
118 foreach ( $this->config['types'] as $i => $type ) {
119 if ( $i == 0 ) {
120 continue;
123 // Construct pseudo-hash based on params and arguments
124 /** @var ParameterizedPassword $passObj */
125 $passObj = $this->factory->newFromType( $type );
126 '@phan-var ParameterizedPassword $passObj';
128 $params = '';
129 $args = '';
130 if ( $this->params[$i] !== '' ) {
131 $params = $this->params[$i] . $passObj->getDelimiter();
133 if ( isset( $this->args[$i] ) && $this->args[$i] !== '' ) {
134 $args = $this->args[$i] . $passObj->getDelimiter();
136 $existingHash = ":$type:" . $params . $args . $this->hash;
138 // Hash the last hash with the next type in the layer
139 $passObj = $this->factory->newFromCiphertext( $existingHash );
140 '@phan-var ParameterizedPassword $passObj';
141 $passObj->crypt( $lastHash );
143 // Move over the params and args
144 $this->params[$i] = implode( $passObj->getDelimiter(), $passObj->params );
145 $this->args[$i] = implode( $passObj->getDelimiter(), $passObj->args );
146 $lastHash = $passObj->hash;
149 $this->hash = $lastHash;
153 /** @deprecated since 1.43 use MediaWiki\\Password\\LayeredParameterizedPassword */
154 class_alias( LayeredParameterizedPassword::class, 'LayeredParameterizedPassword' );