Encryption::getMethods()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
cc 2
nc 1
nop 0
crap 6
1
<?php
2
/**
3
 * Encryption basic class.
4
 *
5
 * @package App
6
 *
7
 * @copyright YetiForce S.A.
8
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
9
 * @author    Mariusz Krzaczkowski <[email protected]>
10
 * @author    Tomasz Kur <[email protected]>
11
 * @author    Radosław Skrzypczak <[email protected]>
12
 */
13
14
namespace App;
15
16
/**
17
 * Class to encrypt and decrypt data.
18
 */
19
class Encryption extends Base
20
{
21
	/** @var int The encryption ID for the configuration */
22
	public const TARGET_SETTINGS = 0;
23
	/** @var string Table name */
24
	public const TABLE_NAME = 'a_#__encryption';
25
	/** @var int Encryption status */
26
	public const STATUS_ACTIVE = 1;
27
	/** @var int Encryption status */
28
	public const STATUS_WORKING = 2;
29
30
	/** @var array Recommended encryption methods */
31
	public static $recommendedMethods = [
32
		'aes-256-cbc', 'aes-256-ctr', 'aes-192-cbc', 'aes-192-ctr',
33
	];
34
35
	/**
36
	 * Function to get instance.
37
	 *
38
	 * @param int $target self::TARGET_SETTINGS or module ID
39 4
	 *
40
	 * @return self
41 4
	 */
42 3
	public static function getInstance(int $target = self::TARGET_SETTINGS)
43
	{
44 3
		if (Cache::has('Encryption', $target)) {
45 3
			return Cache::get('Encryption', $target);
46 3
		}
47
		if (self::TARGET_SETTINGS === $target) {
48
			$instance = \App\Encryptions\Settings::getInstance($target);
49
		} else {
50 3
			$instance = \App\Encryptions\Module::getInstance($target);
51 3
		}
52 3
		Cache::save('Encryption', $target, $instance, Cache::LONG);
53
		return $instance;
54
	}
55
56
	/**
57
	 * Specifies the length of the vector.
58
	 *
59
	 * @param string $method
60
	 *
61
	 * @return int
62
	 */
63
	public static function getLengthVector($method)
64
	{
65
		return openssl_cipher_iv_length($method);
66
	}
67
68
	/**
69
	 * Get vector.
70
	 *
71
	 * @return string
72
	 */
73
	public function getVector(): string
74
	{
75
		return $this->get('vector') ?? '';
76
	}
77
78
	/**
79
	 * Get target ID.
80
	 *
81
	 * @return int
82
	 */
83
	public function getTarget()
84
	{
85
		return $this->get('target');
86
	}
87
88
	/**
89
	 * Get method.
90
	 *
91
	 * @return string
92
	 */
93
	public function getMethod()
94
	{
95
		return $this->get('method');
96
	}
97
98
	/**
99
	 * Get options or options default value(0).
100
	 *
101
	 * @return int
102
	 */
103
	public function getOptions(): int
104
	{
105
		return $this->get('options') ?? 0;
106
	}
107
108
	/**
109
	 * Function to encrypt data.
110
	 *
111
	 * @param string $decrypted
112
	 * @param bool   $testMode
113
	 *
114
	 * @return string
115
	 */
116
	public function encrypt($decrypted, bool $testMode = false)
117
	{
118
		if (!$this->isActive($testMode)) {
119
			return $decrypted;
120
		}
121
		$encrypted = openssl_encrypt($decrypted, $this->getMethod(), $this->get('pass'), $this->getOptions(), $this->get('vector'));
122
		return base64_encode($encrypted);
123
	}
124
125
	/**
126
	 * Function to decrypt data.
127
	 *
128
	 * @param string $encrypted
129
	 * @param bool   $testMode
130
	 *
131
	 * @return string
132
	 */
133
	public function decrypt($encrypted, bool $testMode = false)
134
	{
135
		if (!$this->isActive($testMode)) {
136
			return $encrypted;
137
		}
138
		return openssl_decrypt(base64_decode($encrypted), $this->getMethod(), $this->get('pass'), $this->getOptions(), $this->get('vector'));
139
	}
140
141
	/**
142
	 * Returns list method of encryption.
143
	 *
144
	 * @return string[]
145
	 */
146
	public static function getMethods()
147
	{
148
		return array_filter(openssl_get_cipher_methods(), fn ($methodName) => false === stripos($methodName, 'gcm') && false === stripos($methodName, 'ccm'));
149
	}
150
151
	/**
152
	 * Checks if encrypt or decrypt is possible.
153
	 *
154
	 * @param bool $testMode
155
	 *
156
	 * @return bool
157
	 */
158 9
	public function isActive(bool $testMode = false)
159
	{
160 9
		return $testMode;
161 3
	}
162
163 6
	/**
164 6
	 * Check if the encryption change has been set.
165
	 *
166
	 * @return bool
167
	 */
168
	public function isReady(): bool
169
	{
170
		return (new \App\Db\Query())->from('s_#__batchmethod')->where(['method' => static::class . '::recalculatePasswords', 'status' => \App\BatchMethod::STATUS_ENABLED])->exists();
171
	}
172
173
	/**
174 9
	 * Check if the encryption change has started.
175
	 *
176 9
	 * @return bool
177 3
	 */
178
	public function isRunning(): bool
179 6
	{
180
		$result = (new \App\Db\Query())->from('s_#__batchmethod')->where(['method' => static::class . '::recalculatePasswords', 'status' => [\App\BatchMethod::STATUS_ENABLED, \App\BatchMethod::STATUS_RUNNING, \App\BatchMethod::STATUS_HALTED, \App\BatchMethod::STATUS_COMPLETED]])->exists();
181
		return $result || (new \App\Db\Query())->from(self::TABLE_NAME)->where(['target' => $this->getTarget(), 'status' => self::STATUS_WORKING])->exists();
182
	}
183
184
	/**
185
	 * Encryption change.
186
	 */
187 7
	public function reload()
188
	{
189
		$db = \App\Db::getInstance('admin');
190 7
		\App\BatchMethod::deleteByMethod(static::class . '::recalculatePasswords');
191 7
		(new \App\BatchMethod(['method' => static::class . '::recalculatePasswords', 'params' => [$this->get('method'), $this->get('pass'), $this->get('vector'), $this->getTarget()]]))->save();
192
		if (!(new \App\Db\Query())->from(self::TABLE_NAME)->where(['target' => $this->getTarget()])->exists($db)) {
193
			$db->createCommand()->insert(self::TABLE_NAME, ['method' => '', 'pass' => '', 'target' => $this->getTarget(), 'status' => self::STATUS_WORKING])->execute();
194
		} else {
195
			$db->createCommand()->update(self::TABLE_NAME, ['status' => self::STATUS_WORKING], ['target' => $this->getTarget()])->execute();
196
		}
197
		\App\Cache::delete('Encryption', $this->getTarget());
198
	}
199 12
200
	/**
201 12
	 * Generate random password.
202 6
	 *
203
	 * @param int   $length
204 6
	 * @param mixed $type
205
	 *
206
	 * @return string
207
	 */
208
	public static function generatePassword($length = 10, $type = 'lbd')
209
	{
210
		$chars = [];
211
		if (false !== strpos($type, 'l')) {
212
			$chars[] = 'abcdefghjkmnpqrstuvwxyz';
213
		}
214
		if (false !== strpos($type, 'b')) {
215 17
			$chars[] = 'ABCDEFGHJKMNPQRSTUVWXYZ';
216
		}
217 17
		if (false !== strpos($type, 'd')) {
218 17
			$chars[] = '0123456789';
219 17
		}
220
		if (false !== strpos($type, 's')) {
221 17
			$chars[] = '!"#$%&\'()*+,-./:;<=>?@[\]^_{|}';
222 17
		}
223
		$password = $allChars = '';
224 17
		foreach ($chars as $char) {
225 14
			$allChars .= $char;
226
			$password .= $char[array_rand(str_split($char))];
227 17
		}
228
		$allChars = str_split($allChars);
229
		$missing = $length - \count($chars);
230 17
		for ($i = 0; $i < $missing; ++$i) {
231 17
			$password .= $allChars[array_rand($allChars)];
232 17
		}
233 17
		return str_shuffle($password);
234
	}
235 17
236 17
	/**
237 17
	 * Generate user password.
238 17
	 *
239
	 * @param int $length
240 17
	 *
241
	 * @return string
242
	 */
243
	public static function generateUserPassword($length = 10)
244
	{
245
		$passDetail = \Settings_Password_Record_Model::getUserPassConfig();
246
		if ($length > $passDetail['max_length']) {
247
			$length = $passDetail['max_length'];
248
		}
249
		if ($length < $passDetail['min_length']) {
250
			$length = $passDetail['min_length'];
251
		}
252
		$type = 'l';
253
		if ('true' === $passDetail['numbers']) {
254
			$type .= 'd';
255
		}
256
		if ('true' === $passDetail['big_letters']) {
257
			$type .= 'b';
258
		}
259
		if ('true' === $passDetail['special']) {
260
			$type .= 's';
261
		}
262
		return static::generatePassword($length, $type);
263
	}
264
265
	/**
266
	 * Function to create a hash.
267
	 *
268
	 * @param string $text
269
	 *
270
	 * @return string
271
	 */
272
	public static function createHash($text)
273
	{
274
		return crypt($text, '$1$' . \App\Config::main('application_unique_key'));
275
	}
276
277
	/**
278
	 * Function to create a password hash.
279
	 *
280
	 * @param string $text
281
	 * @param string $pepper
282
	 *
283
	 * @return string
284
	 */
285
	public static function createPasswordHash(string $text, string $pepper): string
286
	{
287
		return password_hash(hash_hmac('sha256', $text, $pepper . \App\Config::main('application_unique_key')), \defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_DEFAULT);
0 ignored issues
show
Bug Best Practice introduced by
The expression return password_hash(has...: App\PASSWORD_DEFAULT) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
288
	}
289
290
	/**
291
	 * Check password hash.
292
	 *
293
	 * @param string $password
294
	 * @param string $hash
295
	 * @param string $pepper
296
	 *
297
	 * @return bool
298
	 */
299
	public static function verifyPasswordHash(string $password, string $hash, string $pepper): bool
300
	{
301
		return password_verify(hash_hmac('sha256', $password, $pepper . \App\Config::main('application_unique_key')), $hash);
302
	}
303
}
304