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 (231e41)
by Liuta
03:38
created

Xcloner_Encryption   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 350
Duplicated Lines 15.43 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 0
dl 54
loc 350
rs 4.5599
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A verify_encrypted_file() 0 8 2
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 9 2
F decrypt_file() 0 117 25
A __construct() 0 13 3
A get_xcloner_path() 0 6 2
F encrypt_file() 0 104 20
A get_backup_encryption_key() 0 7 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 $verification = false;
20
21
	public function __construct(Xcloner $xcloner_container)
22
	{
23
		$this->xcloner_container = $xcloner_container;
0 ignored issues
show
Bug Best Practice introduced by
The property xcloner_container does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
24
		if (method_exists($xcloner_container, 'get_xcloner_settings')) {
25
			$this->xcloner_settings = $xcloner_container->get_xcloner_settings();
26
		} else {
27
			$this->xcloner_settings = "";
28
		}
29
30
		if (method_exists($xcloner_container, 'get_xcloner_logger')) {
31
			$this->logger = $xcloner_container->get_xcloner_logger()->withName("xcloner_encryption");
32
		} else {
33
			$this->logger = "";
34
		}
35
	}
36
37
	/**
38
	 * Returns the backup encryption key
39
	 *
40
	 * @return SECURE_AUTH_SALT|null
0 ignored issues
show
Bug introduced by
The type SECURE_AUTH_SALT was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
41
	 */
42
	public function get_backup_encryption_key()
43
	{
44
		if (is_object($this->xcloner_settings)) {
45
			return $this->xcloner_settings->get_xcloner_encryption_key();
46
		}
47
48
		return "";
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type SECURE_AUTH_SALT|null.
Loading history...
49
50
	}
51
52
	/**
53
	 * Check if provided filename has encrypted suffix
54
	 *
55
	 * @param $filename
56
	 * @return bool
57
	 */
58
	public function is_encrypted_file($filename) {
59
		$fp = fopen($this->get_xcloner_path().$filename, 'r');
60
		$encryption_length = fread($fp, 16);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fread() does only seem to accept resource, 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 ignore-type  annotation

60
		$encryption_length = fread(/** @scrutinizer ignore-type */ $fp, 16);
Loading history...
61
		fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, 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 ignore-type  annotation

61
		fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
62
		if (is_numeric($encryption_length)) {
63
			return true;
64
		}
65
66
		return false;
67
68
	}
69
70
	/**
71
	 * Returns the filename with encrypted suffix
72
	 *
73
	 * @param string $filename
74
	 * @return string
75
	 */
76
	public function get_encrypted_target_backup_file_name($filename) {
77
78
		return str_replace(self::FILE_DECRYPTION_SUFFIX, "", $filename).self::FILE_ENCRYPTION_SUFFIX;
79
	}
80
81
	/**
82
	 * Returns the filename without encrypted suffix
83
	 *
84
	 * @param string $filename
85
	 * @return string
86
	 */
87
	public function get_decrypted_target_backup_file_name($filename) {
88
89
		return str_replace(self::FILE_ENCRYPTION_SUFFIX, "", $filename).self::FILE_DECRYPTION_SUFFIX;
90
	}
91
92
	/**
93
	 * Encrypt the passed file and saves the result in a new file with ".enc" as suffix.
94
	 *
95
	 * @param string $source Path to file that should be encrypted
96
	 * @param string $dest   File name where the encryped file should be written to.
97
	 * @param string $key    The key used for the encryption
98
	 * @param int $start   Start position for reading when doing incremental mode.
99
	 * @param integer $iv   The IV key to use.
100
	 * @param bool $verification   Weather we should we try to verify the decryption.
101
	 * @return string|false  Returns the file name that has been created or FALSE if an error occured
102
	 */
103
	public function encrypt_file($source, $dest = "", $key = "", $start = 0, $iv = 0, $verification = true, $recursive = false)
104
	{
105
		if (is_object($this->logger)) {
106
			$this->logger->info(sprintf('Encrypting file %s at position %d IV %s', $source, $start, base64_encode($iv)));
107
		}
108
109
		//$key = substr(sha1($key, true), 0, 16);
110
		if (!$key) {
111
			$key = self::get_backup_encryption_key();
0 ignored issues
show
Bug Best Practice introduced by
The method Xcloner_Encryption::get_backup_encryption_key() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

111
			/** @scrutinizer ignore-call */ 
112
   $key = self::get_backup_encryption_key();
Loading history...
112
		}
113
		$key_digest = openssl_digest($key, "md5", true);
114
115
		$keep_local = 1;
116
		if (!$dest) {
117
			$dest = $this->get_encrypted_target_backup_file_name($source);
118
			$keep_local = 0;
119
		}
120
121
		if (!$iv || !$start) {
122
			//$iv = openssl_random_pseudo_bytes(16);
123
			$iv = str_pad(self::FILE_ENCRYPTION_BLOCKS, 16, "0000000000000000", STR_PAD_LEFT);
124
		}
125
126
		if (!$start) {
127
			$fpOut = fopen($this->get_xcloner_path().$dest, 'w');
128
		} else {
129
			$fpOut = fopen($this->get_xcloner_path().$dest, 'a');
130
		}
131
132
		if ($fpOut) {
0 ignored issues
show
introduced by
$fpOut is of type false|resource, thus it always evaluated to false.
Loading history...
133
134
			// Put the initialization vector to the beginning of the file
135
			if (!$start) {
136
				fwrite($fpOut, $iv);
137
			}
138
139
			if (file_exists($this->get_xcloner_path().$source) &&
140
				$fpIn = fopen($this->get_xcloner_path().$source, 'rb')) {
141
142
				fseek($fpIn, (int)$start);
143
144
				if (!feof($fpIn)) {
145
146
					$plaintext = fread($fpIn, 16 * self::FILE_ENCRYPTION_BLOCKS);
147
					$ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', $key_digest, OPENSSL_RAW_DATA, $iv);
148
149
					// Use the first 16 bytes of the ciphertext as the next initialization vector
150
					$iv = substr($ciphertext, 0, 16);
151
					//$iv = openssl_random_pseudo_bytes(16);
152
153
					fwrite($fpOut, $ciphertext);
154
155
					$start = ftell($fpIn);
156
157
					fclose($fpOut);
158
159
					if (!feof($fpIn)) {
160
						fclose($fpIn);
161
						//echo "\n NEW:".$key.md5($iv);
162
						//self::encryptFile($source, $dest, $key, $start, $iv);
163
						if ($recursive) {
164
							$this->encrypt_file($source, $dest, $key, $start, ($iv), $verification, $recursive);
165
						} else {
166
167
							if (($iv) != base64_decode(base64_encode($iv)))
168
							{
169
								throw new \Exception('Could not encode IV for transport');
170
							}
171
172
							return array(
173
								"start" => $start,
174
								"iv" => base64_encode($iv),
175
								"target_file" => $dest,
176
								"finished" => 0
177
							);
178
						}
179
					}
180
181
				}
182
			} else {
183
				if (is_object($this->logger)) {
184
					$this->logger->error('Unable to read source file for encryption.');
185
				}
186
				throw new \Exception("Unable to read source file for encryption.");
187
			}
188
		} else {
189
			if (is_object($this->logger)) {
190
				$this->logger->error('Unable to write destination file for encryption.');
191
			}
192
			throw new \Exception("Unable to write destination file for encryption.");
193
		}
194
195
		if ($verification) {
196
			$this->verify_encrypted_file($dest);
197
		}
198
199
		//we replace the original backup with the encrypted one
200
		if (!$keep_local && copy($this->get_xcloner_path().$dest,
201
			 $this->get_xcloner_path().$source)) {
202
			unlink($this->get_xcloner_path().$dest);
203
		}
204
205
206
		return array("target_file" => $dest, "finished" => 1);
207
	}
208
209
	/**
210
	 * @param string $file
211
	 */
212
	public function verify_encrypted_file($file) {
213
		if (is_object($this->logger)) {
214
			$this->logger->info(sprintf('Verifying encrypted file %s', $file));
215
		}
216
217
		$this->verification = true;
218
		$this->decrypt_file($file);
219
		$this->verification = false;
220
	}
221
222
	/**
223
	 * Dencrypt the passed file and saves the result in a new file, removing the
224
	 * last 4 characters from file name.
225
	 *
226
	 * @param string $source Path to file that should be decrypted
227
	 * @param string $dest   File name where the decryped file should be written to.
228
	 * @param string $key    The key used for the decryption (must be the same as for encryption)
229
	 * @param int $start   Start position for reading when doing incremental mode.
230
	 * @param integer $iv   The IV key to use.
231
	 * @return string|false  Returns the file name that has been created or FALSE if an error occured
232
	 */
233
	public function decrypt_file($source, $dest = "", $key = "", $start = 0, $iv = 0, $recursive = false)
234
	{
235
		if (is_object($this->logger)) {
236
			$this->logger->info(sprintf('Decrypting file %s at position %d with IV %s', $source, $start, base64_encode($iv)));
237
		}
238
239
		//$key = substr(sha1($key, true), 0, 16);
240
		if (!$key) {
241
			$key = self::get_backup_encryption_key();
0 ignored issues
show
Bug Best Practice introduced by
The method Xcloner_Encryption::get_backup_encryption_key() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

241
			/** @scrutinizer ignore-call */ 
242
   $key = self::get_backup_encryption_key();
Loading history...
242
		}
243
244
		$key_digest = openssl_digest($key, "md5", true);
245
246
		$keep_local = 1;
247
		if (!$dest) {
248
			$dest = $this->get_decrypted_target_backup_file_name($source);
249
			$keep_local = 0;
250
		}
251
252
		if (!$start) {
253
			if ($this->verification) {
254
				$fpOut = fopen("php://stdout", 'w');
255
			} else {
256
				$fpOut = fopen($this->get_xcloner_path().$dest, 'w');
257
			}
258
		} else {
259
			if ($this->verification) {
260
				$fpOut = fopen("php://stdout", 'a');
261
			} else {
262
				$fpOut = fopen($this->get_xcloner_path().$dest, 'a');
263
			}
264
		}
265
266
		if ($fpOut) {
0 ignored issues
show
introduced by
$fpOut is of type false|resource, thus it always evaluated to false.
Loading history...
267
			if (file_exists($this->get_xcloner_path().$source) &&
268
				$fpIn = fopen($this->get_xcloner_path().$source, 'rb')) {
269
270
				$encryption_length = (int)fread($fpIn, 16);
271
				if (!$encryption_length) {
272
					$encryption_length = self::FILE_ENCRYPTION_BLOCKS;
273
				}
274
275
				fseek($fpIn, (int)$start);
276
277
				// Get the initialzation vector from the beginning of the file
278
				if (!$iv) {
279
					$iv = fread($fpIn, 16);
280
				}
281
282
				if (!feof($fpIn)) {
283
284
					// we have to read one block more for decrypting than for encrypting
285
					$ciphertext = fread($fpIn, 16 * ($encryption_length + 1));
286
					$plaintext = openssl_decrypt($ciphertext, 'AES-128-CBC', $key_digest, OPENSSL_RAW_DATA, $iv);
287
288
					if (!$plaintext) {
289
						unlink($this->get_xcloner_path().$dest);
290
						if (is_object($this->logger)) {
291
							$this->logger->error('Backup decryption failed, please check your provided Encryption Key.');
292
						}
293
						throw new \Exception("Backup decryption failed, please check your provided Encryption Key.");
294
					}
295
296
					// Use the first 16 bytes of the ciphertext as the next initialization vector
297
					$iv = substr($ciphertext, 0, 16);
298
299
					if (!$this->verification) {
300
						fwrite($fpOut, $plaintext);
301
					}
302
303
					$start = ftell($fpIn);
304
305
					fclose($fpOut);
306
307
					if (!feof($fpIn)) {
308
						fclose($fpIn);
309
						if ($this->verification || $recursive) {
310
							$ciphertext = "";
0 ignored issues
show
Unused Code introduced by
The assignment to $ciphertext is dead and can be removed.
Loading history...
311
							$plaintext = "";
0 ignored issues
show
Unused Code introduced by
The assignment to $plaintext is dead and can be removed.
Loading history...
312
							$this->decrypt_file($source, $dest, $key, $start, $iv, $recursive);
313
						} else {
314
							if (($iv) != base64_decode(base64_encode($iv)))
315
							{
316
								throw new \Exception('Could not encode IV for transport');
317
							}
318
319
							return array(
320
								"start" => $start,
321
								"encryption_length" => $encryption_length,
322
								"iv" => base64_encode($iv),
323
								"target_file" => $dest,
324
								"finished" => 0
325
							);
326
					}
327
					}
328
329
				}
330
			} else {
331
				if (is_object($this->logger)) {
332
					$this->logger->error('Unable to read source file for decryption');
333
				}
334
				throw new \Exception("Unable to read source file for decryption");
335
			}
336
		} else {
337
			if (is_object($this->logger)) {
338
				$this->logger->error('Unable to write destination file for decryption');
339
			}
340
			throw new \Exception("Unable to write destination file for decryption");
341
		}
342
343
		//we replace the original backup with the encrypted one
344
		if (!$keep_local && !$this->verification && copy($this->get_xcloner_path().$dest,
345
			$this->get_xcloner_path().$source)) {
346
			unlink($this->get_xcloner_path().$dest);
347
		}
348
349
		return array("target_file" => $dest, "finished" => 1);
350
	}
351
352
	public function get_xcloner_path() {
353
		if (is_object($this->xcloner_settings)) {
354
			return $this->xcloner_settings->get_xcloner_store_path().DS;
355
		}
356
357
		return null;
358
	}
359
360
}
361
362
363
try {
364
365
	if (isset($argv[1])) {
366
367
		class Xcloner {
368
			public function __construct()
369
			{
370
			}
371
		}
372
		$xcloner_encryption = new Xcloner_Encryption(new Xcloner());
373
374
		if ($argv[1] == "-e") {
375
			$xcloner_encryption->encrypt_file($argv[2], $argv[2].".enc", $argv[4], '', '', '', true);
0 ignored issues
show
Bug introduced by
'' of type string is incompatible with the type boolean expected by parameter $verification of Xcloner_Encryption::encrypt_file(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

375
			$xcloner_encryption->encrypt_file($argv[2], $argv[2].".enc", $argv[4], '', '', /** @scrutinizer ignore-type */ '', true);
Loading history...
Bug introduced by
'' of type string is incompatible with the type integer expected by parameter $start of Xcloner_Encryption::encrypt_file(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

375
			$xcloner_encryption->encrypt_file($argv[2], $argv[2].".enc", $argv[4], /** @scrutinizer ignore-type */ '', '', '', true);
Loading history...
Bug introduced by
'' of type string is incompatible with the type integer expected by parameter $iv of Xcloner_Encryption::encrypt_file(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

375
			$xcloner_encryption->encrypt_file($argv[2], $argv[2].".enc", $argv[4], '', /** @scrutinizer ignore-type */ '', '', true);
Loading history...
376
		} elseif ($argv[1] == "-d") {
377
			$xcloner_encryption->decrypt_file($argv[2], $argv[2].".dec", $argv[4], '', '', true);
0 ignored issues
show
Bug introduced by
'' of type string is incompatible with the type integer expected by parameter $start of Xcloner_Encryption::decrypt_file(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

377
			$xcloner_encryption->decrypt_file($argv[2], $argv[2].".dec", $argv[4], /** @scrutinizer ignore-type */ '', '', true);
Loading history...
Bug introduced by
'' of type string is incompatible with the type integer expected by parameter $iv of Xcloner_Encryption::decrypt_file(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

377
			$xcloner_encryption->decrypt_file($argv[2], $argv[2].".dec", $argv[4], '', /** @scrutinizer ignore-type */ '', true);
Loading history...
378
		}
379
	}
380
}catch (\Exception $e) {
381
	echo "CAUGHT: ".$e->getMessage();
382
}
383