GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Branch dev (1848ee)
by Liuta
03:12
created

Xcloner_Encryption   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 356
Duplicated Lines 0 %

Importance

Changes 10
Bugs 8 Features 0
Metric Value
eloc 159
c 10
b 8
f 0
dl 0
loc 356
rs 4.08
wmc 59

9 Methods

Rating   Name   Duplication   Size   Complexity  
A get_decrypted_target_backup_file_name() 0 3 1
A get_encrypted_target_backup_file_name() 0 3 1
A is_encrypted_file() 0 11 3
A __construct() 0 13 3
A get_backup_encryption_key() 0 7 2
A verify_encrypted_file() 0 8 2
A get_xcloner_path() 0 6 2
F encrypt_file() 0 104 20
F decrypt_file() 0 117 25

How to fix   Complexity   

Complex Class

Complex classes like Xcloner_Encryption often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Xcloner_Encryption, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: thinkovi
5
 * Date: 2018-11-27
6
 * Time: 12:11
7
 */
8
9
//namespace XCloner;
10
11
class Xcloner_Encryption
12
{
13
	const FILE_ENCRYPTION_BLOCKS = 1024 * 1024;
14
	const FILE_ENCRYPTION_SUFFIX = ".encrypted";
15
	const FILE_DECRYPTION_SUFFIX = ".decrypted";
16
17
	private $xcloner_settings;
18
	private $logger;
19
	private $xcloner_container;
20
	private $verification = false;
21
22
	/**
23
	 * Xcloner_Encryption constructor.
24
	 * @param Xcloner $xcloner_container
25
	 */
26
	public function __construct(Xcloner $xcloner_container)
27
	{
28
		$this->xcloner_container = $xcloner_container;
29
		if (method_exists($xcloner_container, 'get_xcloner_settings')) {
30
			$this->xcloner_settings = $xcloner_container->get_xcloner_settings();
31
		} else {
32
			$this->xcloner_settings = "";
33
		}
34
35
		if (method_exists($xcloner_container, 'get_xcloner_logger')) {
36
			$this->logger = $xcloner_container->get_xcloner_logger()->withName("xcloner_encryption");
37
		} else {
38
			$this->logger = "";
39
		}
40
	}
41
42
	/**
43
	 * Returns the backup encryption key
44
	 *
45
	 * @return string|null
46
	 */
47
	public function get_backup_encryption_key()
48
	{
49
		if (is_object($this->xcloner_settings)) {
50
			return $this->xcloner_settings->get_xcloner_encryption_key();
51
		}
52
53
		return null;
54
55
	}
56
57
	/**
58
	 * Check if provided filename has encrypted suffix
59
	 *
60
	 * @param $filename
61
	 * @return bool
62
	 */
63
	public function is_encrypted_file($filename) {
64
		$fp = fopen($this->get_xcloner_path().$filename, 'r');
65
		if (is_resource($fp)) {
66
			$encryption_length = fread($fp, 16);
67
			fclose($fp);
68
			if (is_numeric($encryption_length)) {
69
				return true;
70
			}
71
		}
72
73
		return false;
74
75
	}
76
77
	/**
78
	 * Returns the filename with encrypted suffix
79
	 *
80
	 * @param string $filename
81
	 * @return string
82
	 */
83
	public function get_encrypted_target_backup_file_name($filename) {
84
85
		return str_replace(self::FILE_DECRYPTION_SUFFIX, "", $filename).self::FILE_ENCRYPTION_SUFFIX;
86
	}
87
88
	/**
89
	 * Returns the filename without encrypted suffix
90
	 *
91
	 * @param string $filename
92
	 * @return string
93
	 */
94
	public function get_decrypted_target_backup_file_name($filename) {
95
96
		return str_replace(self::FILE_ENCRYPTION_SUFFIX, "", $filename).self::FILE_DECRYPTION_SUFFIX;
97
	}
98
99
	/**
100
	 * Encrypt the passed file and saves the result in a new file with ".enc" as suffix.
101
	 *
102
	 * @param string $source Path to file that should be encrypted
103
	 * @param string $dest   File name where the encryped file should be written to.
104
	 * @param string $key    The key used for the encryption
105
	 * @param int $start   Start position for reading when doing incremental mode.
106
	 * @param string $iv   The IV key to use.
107
	 * @param bool $verification   Weather we should we try to verify the decryption.
108
	 * @return array|false  Returns array or FALSE if an error occured
109
     * @throws Exception
110
	 */
111
	public function encrypt_file($source, $dest = "", $key = "", $start = 0, $iv = 0, $verification = true, $recursive = false)
112
	{
113
		if (is_object($this->logger)) {
114
			$this->logger->info(sprintf('Encrypting file %s at position %d IV %s', $source, $start, base64_encode($iv)));
115
		}
116
117
		//$key = substr(sha1($key, true), 0, 16);
118
		if (!$key) {
119
			$key = $this->get_backup_encryption_key();
120
		}
121
		$key_digest = openssl_digest($key, "md5", true);
122
123
		$keep_local = 1;
124
		if (!$dest) {
125
			$dest = $this->get_encrypted_target_backup_file_name($source);
126
			$keep_local = 0;
127
		}
128
129
		if (!$iv || !$start) {
130
			//$iv = openssl_random_pseudo_bytes(16);
131
			$iv = str_pad(self::FILE_ENCRYPTION_BLOCKS, 16, "0000000000000000", STR_PAD_LEFT);
132
		}
133
134
		if (!$start) {
135
			$fpOut = fopen($this->get_xcloner_path().$dest, 'w');
136
		} else {
137
			$fpOut = fopen($this->get_xcloner_path().$dest, 'a');
138
		}
139
140
		if (is_resource($fpOut)) {
141
142
			// Put the initialization vector to the beginning of the file
143
			if (!$start) {
144
				fwrite($fpOut, $iv);
145
			}
146
147
			if (file_exists($this->get_xcloner_path().$source) &&
148
				$fpIn = fopen($this->get_xcloner_path().$source, 'rb')) {
149
150
				fseek($fpIn, (int)$start);
151
152
				if (!feof($fpIn)) {
153
154
					$plaintext = fread($fpIn, 16 * self::FILE_ENCRYPTION_BLOCKS);
155
					$ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', $key_digest, OPENSSL_RAW_DATA, $iv);
156
157
					// Use the first 16 bytes of the ciphertext as the next initialization vector
158
					$iv = substr($ciphertext, 0, 16);
159
					//$iv = openssl_random_pseudo_bytes(16);
160
161
					fwrite($fpOut, $ciphertext);
162
163
					$start = ftell($fpIn);
164
165
					fclose($fpOut);
166
167
					if (!feof($fpIn)) {
168
						fclose($fpIn);
169
						//echo "\n NEW:".$key.md5($iv);
170
						//self::encryptFile($source, $dest, $key, $start, $iv);
171
						if ($recursive) {
172
							$this->encrypt_file($source, $dest, $key, $start, ($iv), $verification, $recursive);
173
						} else {
174
175
							if (($iv) != base64_decode(base64_encode($iv)))
176
							{
177
								throw new \Exception('Could not encode IV for transport');
178
							}
179
180
							return array(
181
								"start" => $start,
182
								"iv" => base64_encode($iv),
183
								"target_file" => $dest,
184
								"finished" => 0
185
							);
186
						}
187
					}
188
189
				}
190
			} else {
191
				if (is_object($this->logger)) {
192
					$this->logger->error('Unable to read source file for encryption.');
193
				}
194
				throw new \Exception("Unable to read source file for encryption.");
195
			}
196
		} else {
197
			if (is_object($this->logger)) {
198
				$this->logger->error('Unable to write destination file for encryption.');
199
			}
200
			throw new \Exception("Unable to write destination file for encryption.");
201
		}
202
203
		if ($verification) {
204
			$this->verify_encrypted_file($dest);
205
		}
206
207
		//we replace the original backup with the encrypted one
208
		if (!$keep_local && copy($this->get_xcloner_path().$dest,
209
			 $this->get_xcloner_path().$source)) {
210
			unlink($this->get_xcloner_path().$dest);
211
		}
212
213
214
		return array("target_file" => $dest, "finished" => 1);
215
	}
216
217
	/**
218
	 * @param string $file
219
	 */
220
	public function verify_encrypted_file($file) {
221
		if (is_object($this->logger)) {
222
			$this->logger->info(sprintf('Verifying encrypted file %s', $file));
223
		}
224
225
		$this->verification = true;
226
		$this->decrypt_file($file);
227
		$this->verification = false;
228
	}
229
230
	/**
231
	 * Dencrypt the passed file and saves the result in a new file, removing the
232
	 * last 4 characters from file name.
233
	 *
234
	 * @param string $source Path to file that should be decrypted
235
	 * @param string $dest   File name where the decryped file should be written to.
236
	 * @param string $key    The key used for the decryption (must be the same as for encryption)
237
	 * @param int $start   Start position for reading when doing incremental mode.
238
	 * @param string $iv   The IV key to use.
239
	 * @return array|false  Returns array or FALSE if an error occured
240
     * @throws Exception
241
	 */
242
	public function decrypt_file($source, $dest = "", $key = "", $start = 0, $iv = 0, $recursive = false)
243
	{
244
		if (is_object($this->logger)) {
245
			$this->logger->info(sprintf('Decrypting file %s at position %d with IV %s', $source, $start, base64_encode($iv)));
246
		}
247
248
		//$key = substr(sha1($key, true), 0, 16);
249
		if (!$key) {
250
			$key = $this->get_backup_encryption_key();
251
		}
252
253
		$key_digest = openssl_digest($key, "md5", true);
254
255
		$keep_local = 1;
256
		if (!$dest) {
257
			$dest = $this->get_decrypted_target_backup_file_name($source);
258
			$keep_local = 0;
259
		}
260
261
		if (!$start) {
262
			if ($this->verification) {
263
				$fpOut = fopen("php://stdout", 'w');
264
			} else {
265
				$fpOut = fopen($this->get_xcloner_path().$dest, 'w');
266
			}
267
		} else {
268
			if ($this->verification) {
269
				$fpOut = fopen("php://stdout", 'a');
270
			} else {
271
				$fpOut = fopen($this->get_xcloner_path().$dest, 'a');
272
			}
273
		}
274
275
		if (is_resource($fpOut)) {
276
			if (file_exists($this->get_xcloner_path().$source) &&
277
				$fpIn = fopen($this->get_xcloner_path().$source, 'rb')) {
278
279
				$encryption_length = (int)fread($fpIn, 16);
280
				if (!$encryption_length) {
281
					$encryption_length = self::FILE_ENCRYPTION_BLOCKS;
282
				}
283
284
				fseek($fpIn, (int)$start);
285
286
				// Get the initialzation vector from the beginning of the file
287
				if (!$iv) {
288
					$iv = fread($fpIn, 16);
289
				}
290
291
				if (!feof($fpIn)) {
292
293
					// we have to read one block more for decrypting than for encrypting
294
					$ciphertext = fread($fpIn, 16 * ($encryption_length + 1));
295
					$plaintext = openssl_decrypt($ciphertext, 'AES-128-CBC', $key_digest, OPENSSL_RAW_DATA, $iv);
296
297
					if (!$plaintext) {
298
						unlink($this->get_xcloner_path().$dest);
299
						if (is_object($this->logger)) {
300
							$this->logger->error('Backup decryption failed, please check your provided Encryption Key.');
301
						}
302
						throw new \Exception("Backup decryption failed, please check your provided Encryption Key.");
303
					}
304
305
					// Use the first 16 bytes of the ciphertext as the next initialization vector
306
					$iv = substr($ciphertext, 0, 16);
307
308
					if (!$this->verification) {
309
						fwrite($fpOut, $plaintext);
310
					}
311
312
					$start = ftell($fpIn);
313
314
					fclose($fpOut);
315
316
					if (!feof($fpIn)) {
317
						fclose($fpIn);
318
						if ($this->verification || $recursive) {
319
							unset($ciphertext);
320
							unset($plaintext);
321
							$this->decrypt_file($source, $dest, $key, $start, $iv, $recursive);
322
						} else {
323
							if (($iv) != base64_decode(base64_encode($iv)))
324
							{
325
								throw new \Exception('Could not encode IV for transport');
326
							}
327
328
							return array(
329
								"start" => $start,
330
								"encryption_length" => $encryption_length,
331
								"iv" => base64_encode($iv),
332
								"target_file" => $dest,
333
								"finished" => 0
334
							);
335
					}
336
					}
337
338
				}
339
			} else {
340
				if (is_object($this->logger)) {
341
					$this->logger->error('Unable to read source file for decryption');
342
				}
343
				throw new \Exception("Unable to read source file for decryption");
344
			}
345
		} else {
346
			if (is_object($this->logger)) {
347
				$this->logger->error('Unable to write destination file for decryption');
348
			}
349
			throw new \Exception("Unable to write destination file for decryption");
350
		}
351
352
		//we replace the original backup with the encrypted one
353
		if (!$keep_local && !$this->verification && copy($this->get_xcloner_path().$dest,
354
			$this->get_xcloner_path().$source)) {
355
			unlink($this->get_xcloner_path().$dest);
356
		}
357
358
		return array("target_file" => $dest, "finished" => 1);
359
	}
360
361
	public function get_xcloner_path() {
362
		if (is_object($this->xcloner_settings)) {
363
			return $this->xcloner_settings->get_xcloner_store_path().DS;
364
		}
365
366
		return null;
367
	}
368
369
}
370
371
372
try {
373
374
	if (isset($argv[1])) {
375
376
		class Xcloner {
377
			/**
378
			 * Xcloner constructor.
379
			 */
380
			public function __construct()
381
			{
382
			}
383
		}
384
		$xcloner_encryption = new Xcloner_Encryption(new Xcloner());
385
386
		if ($argv[1] == "-e") {
387
			$xcloner_encryption->encrypt_file($argv[2], $argv[2].".enc", $argv[4], 0, 0, false, true);
388
		} elseif ($argv[1] == "-d") {
389
			$xcloner_encryption->decrypt_file($argv[2], $argv[2].".dec", $argv[4], 0, 0, true);
390
		}
391
	}
392
}catch (\Exception $e) {
393
	echo "CAUGHT: ".$e->getMessage();
394
}
395