1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | /* |
||||
6 | * Copyright (c) Andreas Heigl<[email protected]> All rights reserved. |
||||
7 | * |
||||
8 | * Licensed under the MIT License. See LICENSE.md file in the |
||||
9 | * project root for full license information. |
||||
10 | */ |
||||
11 | |||||
12 | namespace Org_Heigl\Password; |
||||
13 | |||||
14 | use function define; |
||||
15 | use function defined; |
||||
16 | use function password_hash; |
||||
17 | use function password_needs_rehash; |
||||
18 | use function version_compare; |
||||
19 | use const PASSWORD_DEFAULT; |
||||
20 | use const PHP_VERSION; |
||||
21 | |||||
22 | final class Password |
||||
23 | { |
||||
24 | private $password; |
||||
25 | |||||
26 | private $hash; |
||||
27 | |||||
28 | private function __construct(string $password) |
||||
29 | { |
||||
30 | $this->hash = null; |
||||
31 | $this->password = sodium_crypto_secretbox( |
||||
32 | $password, |
||||
33 | ORG_HEIGL_PASSWORD_PASSWORD_NONCE, |
||||
34 | ORG_HEIGL_PASSWORD_PASSWORD_KEY |
||||
35 | ); |
||||
36 | } |
||||
37 | |||||
38 | public static function createFromPlainText(string $password) : self |
||||
39 | { |
||||
40 | return new self($password); |
||||
41 | } |
||||
42 | |||||
43 | public function __toString() |
||||
44 | { |
||||
45 | return '********'; |
||||
46 | } |
||||
47 | |||||
48 | public function __debugInfo() |
||||
49 | { |
||||
50 | return [ |
||||
51 | 'password' => '********' |
||||
52 | ]; |
||||
53 | } |
||||
54 | |||||
55 | public function matchesHash(string $hash) : bool |
||||
56 | { |
||||
57 | $this->hash = $hash; |
||||
58 | |||||
59 | return password_verify( |
||||
60 | $this->getPasswordInPlainText(), |
||||
61 | $this->hash |
||||
62 | ); |
||||
63 | } |
||||
64 | |||||
65 | /** |
||||
66 | * @deprecated Use Password::needsRehash() instead |
||||
67 | */ |
||||
68 | public function shouldBeRehashed($algorithm = PASSWORD_DEFAULT, array $options = []) : bool |
||||
69 | { |
||||
70 | if (null === $this->hash) { |
||||
71 | return true; |
||||
72 | } |
||||
73 | |||||
74 | return password_needs_rehash($this->hash, $algorithm, $options); |
||||
75 | } |
||||
76 | |||||
77 | public function needsRehash(string $algorithm = PASSWORD_DEFAULT, array $options = []): bool |
||||
78 | { |
||||
79 | if (null === $this->hash) { |
||||
80 | return true; |
||||
81 | } |
||||
82 | if (0 < version_compare(PHP_VERSION, '7.4.0')) { |
||||
83 | $algorithm = (int) $algorithm; |
||||
84 | } |
||||
85 | return password_needs_rehash($this->hash, $algorithm, $options); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
86 | } |
||||
87 | |||||
88 | /** |
||||
89 | * @deprecated Use Password::hash() instead |
||||
90 | */ |
||||
91 | public function getNewHash($algorithm = PASSWORD_DEFAULT, array $options = []) : string |
||||
92 | { |
||||
93 | return password_hash($this->getPasswordInPlainText(), $algorithm, $options); |
||||
94 | } |
||||
95 | |||||
96 | public function hash(string $algorithm = PASSWORD_DEFAULT, array $options = []): string |
||||
97 | { |
||||
98 | if (0 < version_compare(PHP_VERSION, '7.4.0')) { |
||||
99 | $algorithm = (int) $algorithm; |
||||
100 | } |
||||
101 | return password_hash($this->getPasswordInPlainText(), $algorithm, $options); |
||||
0 ignored issues
–
show
It seems like
$algorithm can also be of type string ; however, parameter $algo of password_hash() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
102 | } |
||||
103 | |||||
104 | public function getPlainTextPasswordAndYesIKnowWhatIAmDoingHere() : string |
||||
105 | { |
||||
106 | trigger_error( |
||||
107 | 'Password was leaked in clear text using the ' . |
||||
108 | '"Password::getPlainTextPasswordAndYesIKnowWhatIAmDoingHere"-function!!', |
||||
109 | E_USER_WARNING |
||||
110 | ); |
||||
111 | |||||
112 | return $this->getPasswordInPlainText(); |
||||
113 | } |
||||
114 | |||||
115 | private function getPasswordInPlainText() : string |
||||
116 | { |
||||
117 | return sodium_crypto_secretbox_open( |
||||
118 | $this->password, |
||||
119 | ORG_HEIGL_PASSWORD_PASSWORD_NONCE, |
||||
120 | ORG_HEIGL_PASSWORD_PASSWORD_KEY |
||||
121 | ); |
||||
122 | } |
||||
123 | |||||
124 | /** |
||||
125 | * @throws \Org_Heigl\Password\PasswordException |
||||
126 | */ |
||||
127 | public function __wakeup() : void |
||||
128 | { |
||||
129 | throw PasswordException::getWakeupException(); |
||||
130 | } |
||||
131 | |||||
132 | /** |
||||
133 | * @throws \Org_Heigl\Password\PasswordException |
||||
134 | */ |
||||
135 | public function __sleep() : array |
||||
136 | { |
||||
137 | throw PasswordException::getSleepException(); |
||||
138 | } |
||||
139 | |||||
140 | /** |
||||
141 | * @throws \Org_Heigl\Password\PasswordException |
||||
142 | */ |
||||
143 | public function __clone() |
||||
144 | { |
||||
145 | throw PasswordException::getCloneException(); |
||||
146 | } |
||||
147 | } |
||||
148 | |||||
149 | define('ORG_HEIGL_PASSWORD_PASSWORD_NONCE', random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES)); |
||||
150 | define('ORG_HEIGL_PASSWORD_PASSWORD_KEY', sodium_crypto_secretbox_keygen()); |
||||
151 |