Passed
Push — master ( 092ff4...c6d565 )
by Blizzz
13:28 queued 12s
created
apps/encryption/lib/Crypto/Crypt.php 2 patches
Indentation   +653 added lines, -653 removed lines patch added patch discarded remove patch
@@ -56,657 +56,657 @@
 block discarded – undo
56 56
  * @package OCA\Encryption\Crypto
57 57
  */
58 58
 class Crypt {
59
-	public const SUPPORTED_CIPHERS_AND_KEY_SIZE = [
60
-		'AES-256-CTR' => 32,
61
-		'AES-128-CTR' => 16,
62
-		'AES-256-CFB' => 32,
63
-		'AES-128-CFB' => 16,
64
-	];
65
-	// one out of SUPPORTED_CIPHERS_AND_KEY_SIZE
66
-	public const DEFAULT_CIPHER = 'AES-256-CTR';
67
-	// default cipher from old Nextcloud versions
68
-	public const LEGACY_CIPHER = 'AES-128-CFB';
69
-
70
-	public const SUPPORTED_KEY_FORMATS = ['hash', 'password'];
71
-	// one out of SUPPORTED_KEY_FORMATS
72
-	public const DEFAULT_KEY_FORMAT = 'hash';
73
-	// default key format, old Nextcloud version encrypted the private key directly
74
-	// with the user password
75
-	public const LEGACY_KEY_FORMAT = 'password';
76
-
77
-	public const HEADER_START = 'HBEGIN';
78
-	public const HEADER_END = 'HEND';
79
-
80
-	/** @var ILogger */
81
-	private $logger;
82
-
83
-	/** @var string */
84
-	private $user;
85
-
86
-	/** @var IConfig */
87
-	private $config;
88
-
89
-	/** @var IL10N */
90
-	private $l;
91
-
92
-	/** @var bool */
93
-	private $supportLegacy;
94
-
95
-	/**
96
-	 * @param ILogger $logger
97
-	 * @param IUserSession $userSession
98
-	 * @param IConfig $config
99
-	 * @param IL10N $l
100
-	 */
101
-	public function __construct(ILogger $logger, IUserSession $userSession, IConfig $config, IL10N $l) {
102
-		$this->logger = $logger;
103
-		$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : '"no user given"';
104
-		$this->config = $config;
105
-		$this->l = $l;
106
-		$this->supportLegacy = $this->config->getSystemValueBool('encryption.legacy_format_support', false);
107
-	}
108
-
109
-	/**
110
-	 * create new private/public key-pair for user
111
-	 *
112
-	 * @return array|bool
113
-	 */
114
-	public function createKeyPair() {
115
-		$log = $this->logger;
116
-		$res = $this->getOpenSSLPKey();
117
-
118
-		if (!$res) {
119
-			$log->error("Encryption Library couldn't generate users key-pair for {$this->user}",
120
-				['app' => 'encryption']);
121
-
122
-			if (openssl_error_string()) {
123
-				$log->error('Encryption library openssl_pkey_new() fails: ' . openssl_error_string(),
124
-					['app' => 'encryption']);
125
-			}
126
-		} elseif (openssl_pkey_export($res,
127
-			$privateKey,
128
-			null,
129
-			$this->getOpenSSLConfig())) {
130
-			$keyDetails = openssl_pkey_get_details($res);
131
-			$publicKey = $keyDetails['key'];
132
-
133
-			return [
134
-				'publicKey' => $publicKey,
135
-				'privateKey' => $privateKey
136
-			];
137
-		}
138
-		$log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.' . $this->user,
139
-			['app' => 'encryption']);
140
-		if (openssl_error_string()) {
141
-			$log->error('Encryption Library:' . openssl_error_string(),
142
-				['app' => 'encryption']);
143
-		}
144
-
145
-		return false;
146
-	}
147
-
148
-	/**
149
-	 * Generates a new private key
150
-	 *
151
-	 * @return resource
152
-	 */
153
-	public function getOpenSSLPKey() {
154
-		$config = $this->getOpenSSLConfig();
155
-		return openssl_pkey_new($config);
156
-	}
157
-
158
-	/**
159
-	 * get openSSL Config
160
-	 *
161
-	 * @return array
162
-	 */
163
-	private function getOpenSSLConfig() {
164
-		$config = ['private_key_bits' => 4096];
165
-		$config = array_merge(
166
-			$config,
167
-			$this->config->getSystemValue('openssl', [])
168
-		);
169
-		return $config;
170
-	}
171
-
172
-	/**
173
-	 * @param string $plainContent
174
-	 * @param string $passPhrase
175
-	 * @param int $version
176
-	 * @param int $position
177
-	 * @return false|string
178
-	 * @throws EncryptionFailedException
179
-	 */
180
-	public function symmetricEncryptFileContent($plainContent, $passPhrase, $version, $position) {
181
-		if (!$plainContent) {
182
-			$this->logger->error('Encryption Library, symmetrical encryption failed no content given',
183
-				['app' => 'encryption']);
184
-			return false;
185
-		}
186
-
187
-		$iv = $this->generateIv();
188
-
189
-		$encryptedContent = $this->encrypt($plainContent,
190
-			$iv,
191
-			$passPhrase,
192
-			$this->getCipher());
193
-
194
-		// Create a signature based on the key as well as the current version
195
-		$sig = $this->createSignature($encryptedContent, $passPhrase.'_'.$version.'_'.$position);
196
-
197
-		// combine content to encrypt the IV identifier and actual IV
198
-		$catFile = $this->concatIV($encryptedContent, $iv);
199
-		$catFile = $this->concatSig($catFile, $sig);
200
-		return $this->addPadding($catFile);
201
-	}
202
-
203
-	/**
204
-	 * generate header for encrypted file
205
-	 *
206
-	 * @param string $keyFormat see SUPPORTED_KEY_FORMATS
207
-	 * @return string
208
-	 * @throws \InvalidArgumentException
209
-	 */
210
-	public function generateHeader($keyFormat = self::DEFAULT_KEY_FORMAT) {
211
-		if (in_array($keyFormat, self::SUPPORTED_KEY_FORMATS, true) === false) {
212
-			throw new \InvalidArgumentException('key format "' . $keyFormat . '" is not supported');
213
-		}
214
-
215
-		$cipher = $this->getCipher();
216
-
217
-		$header = self::HEADER_START
218
-			. ':cipher:' . $cipher
219
-			. ':keyFormat:' . $keyFormat
220
-			. ':' . self::HEADER_END;
221
-
222
-		return $header;
223
-	}
224
-
225
-	/**
226
-	 * @param string $plainContent
227
-	 * @param string $iv
228
-	 * @param string $passPhrase
229
-	 * @param string $cipher
230
-	 * @return string
231
-	 * @throws EncryptionFailedException
232
-	 */
233
-	private function encrypt($plainContent, $iv, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) {
234
-		$encryptedContent = openssl_encrypt($plainContent,
235
-			$cipher,
236
-			$passPhrase,
237
-			false,
238
-			$iv);
239
-
240
-		if (!$encryptedContent) {
241
-			$error = 'Encryption (symmetric) of content failed';
242
-			$this->logger->error($error . openssl_error_string(),
243
-				['app' => 'encryption']);
244
-			throw new EncryptionFailedException($error);
245
-		}
246
-
247
-		return $encryptedContent;
248
-	}
249
-
250
-	/**
251
-	 * return Cipher either from config.php or the default cipher defined in
252
-	 * this class
253
-	 *
254
-	 * @return string
255
-	 */
256
-	public function getCipher() {
257
-		$cipher = $this->config->getSystemValue('cipher', self::DEFAULT_CIPHER);
258
-		if (!isset(self::SUPPORTED_CIPHERS_AND_KEY_SIZE[$cipher])) {
259
-			$this->logger->warning(
260
-				sprintf(
261
-					'Unsupported cipher (%s) defined in config.php supported. Falling back to %s',
262
-					$cipher,
263
-					self::DEFAULT_CIPHER
264
-				),
265
-				['app' => 'encryption']
266
-			);
267
-			$cipher = self::DEFAULT_CIPHER;
268
-		}
269
-
270
-		return $cipher;
271
-	}
272
-
273
-	/**
274
-	 * get key size depending on the cipher
275
-	 *
276
-	 * @param string $cipher
277
-	 * @return int
278
-	 * @throws \InvalidArgumentException
279
-	 */
280
-	protected function getKeySize($cipher) {
281
-		if (isset(self::SUPPORTED_CIPHERS_AND_KEY_SIZE[$cipher])) {
282
-			return self::SUPPORTED_CIPHERS_AND_KEY_SIZE[$cipher];
283
-		}
284
-
285
-		throw new \InvalidArgumentException(
286
-			sprintf(
287
-					'Unsupported cipher (%s) defined.',
288
-					$cipher
289
-			)
290
-		);
291
-	}
292
-
293
-	/**
294
-	 * get legacy cipher
295
-	 *
296
-	 * @return string
297
-	 */
298
-	public function getLegacyCipher() {
299
-		if (!$this->supportLegacy) {
300
-			throw new ServerNotAvailableException('Legacy cipher is no longer supported!');
301
-		}
302
-
303
-		return self::LEGACY_CIPHER;
304
-	}
305
-
306
-	/**
307
-	 * @param string $encryptedContent
308
-	 * @param string $iv
309
-	 * @return string
310
-	 */
311
-	private function concatIV($encryptedContent, $iv) {
312
-		return $encryptedContent . '00iv00' . $iv;
313
-	}
314
-
315
-	/**
316
-	 * @param string $encryptedContent
317
-	 * @param string $signature
318
-	 * @return string
319
-	 */
320
-	private function concatSig($encryptedContent, $signature) {
321
-		return $encryptedContent . '00sig00' . $signature;
322
-	}
323
-
324
-	/**
325
-	 * Note: This is _NOT_ a padding used for encryption purposes. It is solely
326
-	 * used to achieve the PHP stream size. It has _NOTHING_ to do with the
327
-	 * encrypted content and is not used in any crypto primitive.
328
-	 *
329
-	 * @param string $data
330
-	 * @return string
331
-	 */
332
-	private function addPadding($data) {
333
-		return $data . 'xxx';
334
-	}
335
-
336
-	/**
337
-	 * generate password hash used to encrypt the users private key
338
-	 *
339
-	 * @param string $password
340
-	 * @param string $cipher
341
-	 * @param string $uid only used for user keys
342
-	 * @return string
343
-	 */
344
-	protected function generatePasswordHash($password, $cipher, $uid = '') {
345
-		$instanceId = $this->config->getSystemValue('instanceid');
346
-		$instanceSecret = $this->config->getSystemValue('secret');
347
-		$salt = hash('sha256', $uid . $instanceId . $instanceSecret, true);
348
-		$keySize = $this->getKeySize($cipher);
349
-
350
-		$hash = hash_pbkdf2(
351
-			'sha256',
352
-			$password,
353
-			$salt,
354
-			100000,
355
-			$keySize,
356
-			true
357
-		);
358
-
359
-		return $hash;
360
-	}
361
-
362
-	/**
363
-	 * encrypt private key
364
-	 *
365
-	 * @param string $privateKey
366
-	 * @param string $password
367
-	 * @param string $uid for regular users, empty for system keys
368
-	 * @return false|string
369
-	 */
370
-	public function encryptPrivateKey($privateKey, $password, $uid = '') {
371
-		$cipher = $this->getCipher();
372
-		$hash = $this->generatePasswordHash($password, $cipher, $uid);
373
-		$encryptedKey = $this->symmetricEncryptFileContent(
374
-			$privateKey,
375
-			$hash,
376
-			0,
377
-			0
378
-		);
379
-
380
-		return $encryptedKey;
381
-	}
382
-
383
-	/**
384
-	 * @param string $privateKey
385
-	 * @param string $password
386
-	 * @param string $uid for regular users, empty for system keys
387
-	 * @return false|string
388
-	 */
389
-	public function decryptPrivateKey($privateKey, $password = '', $uid = '') {
390
-		$header = $this->parseHeader($privateKey);
391
-
392
-		if (isset($header['cipher'])) {
393
-			$cipher = $header['cipher'];
394
-		} else {
395
-			$cipher = $this->getLegacyCipher();
396
-		}
397
-
398
-		if (isset($header['keyFormat'])) {
399
-			$keyFormat = $header['keyFormat'];
400
-		} else {
401
-			$keyFormat = self::LEGACY_KEY_FORMAT;
402
-		}
403
-
404
-		if ($keyFormat === self::DEFAULT_KEY_FORMAT) {
405
-			$password = $this->generatePasswordHash($password, $cipher, $uid);
406
-		}
407
-
408
-		// If we found a header we need to remove it from the key we want to decrypt
409
-		if (!empty($header)) {
410
-			$privateKey = substr($privateKey,
411
-				strpos($privateKey,
412
-					self::HEADER_END) + strlen(self::HEADER_END));
413
-		}
414
-
415
-		$plainKey = $this->symmetricDecryptFileContent(
416
-			$privateKey,
417
-			$password,
418
-			$cipher,
419
-			0
420
-		);
421
-
422
-		if ($this->isValidPrivateKey($plainKey) === false) {
423
-			return false;
424
-		}
425
-
426
-		return $plainKey;
427
-	}
428
-
429
-	/**
430
-	 * check if it is a valid private key
431
-	 *
432
-	 * @param string $plainKey
433
-	 * @return bool
434
-	 */
435
-	protected function isValidPrivateKey($plainKey) {
436
-		$res = openssl_get_privatekey($plainKey);
437
-		// TODO: remove resource check one php7.4 is not longer supported
438
-		if (is_resource($res) || (is_object($res) && get_class($res) === 'OpenSSLAsymmetricKey')) {
439
-			$sslInfo = openssl_pkey_get_details($res);
440
-			if (isset($sslInfo['key'])) {
441
-				return true;
442
-			}
443
-		}
444
-
445
-		return false;
446
-	}
447
-
448
-	/**
449
-	 * @param string $keyFileContents
450
-	 * @param string $passPhrase
451
-	 * @param string $cipher
452
-	 * @param int $version
453
-	 * @param int|string $position
454
-	 * @return string
455
-	 * @throws DecryptionFailedException
456
-	 */
457
-	public function symmetricDecryptFileContent($keyFileContents, $passPhrase, $cipher = self::DEFAULT_CIPHER, $version = 0, $position = 0) {
458
-		if ($keyFileContents == '') {
459
-			return '';
460
-		}
461
-
462
-		$catFile = $this->splitMetaData($keyFileContents, $cipher);
463
-
464
-		if ($catFile['signature'] !== false) {
465
-			try {
466
-				// First try the new format
467
-				$this->checkSignature($catFile['encrypted'], $passPhrase . '_' . $version . '_' . $position, $catFile['signature']);
468
-			} catch (GenericEncryptionException $e) {
469
-				// For compatibility with old files check the version without _
470
-				$this->checkSignature($catFile['encrypted'], $passPhrase . $version . $position, $catFile['signature']);
471
-			}
472
-		}
473
-
474
-		return $this->decrypt($catFile['encrypted'],
475
-			$catFile['iv'],
476
-			$passPhrase,
477
-			$cipher);
478
-	}
479
-
480
-	/**
481
-	 * check for valid signature
482
-	 *
483
-	 * @param string $data
484
-	 * @param string $passPhrase
485
-	 * @param string $expectedSignature
486
-	 * @throws GenericEncryptionException
487
-	 */
488
-	private function checkSignature($data, $passPhrase, $expectedSignature) {
489
-		$enforceSignature = !$this->config->getSystemValue('encryption_skip_signature_check', false);
490
-
491
-		$signature = $this->createSignature($data, $passPhrase);
492
-		$isCorrectHash = hash_equals($expectedSignature, $signature);
493
-
494
-		if (!$isCorrectHash && $enforceSignature) {
495
-			throw new GenericEncryptionException('Bad Signature', $this->l->t('Bad Signature'));
496
-		} elseif (!$isCorrectHash && !$enforceSignature) {
497
-			$this->logger->info("Signature check skipped", ['app' => 'encryption']);
498
-		}
499
-	}
500
-
501
-	/**
502
-	 * create signature
503
-	 *
504
-	 * @param string $data
505
-	 * @param string $passPhrase
506
-	 * @return string
507
-	 */
508
-	private function createSignature($data, $passPhrase) {
509
-		$passPhrase = hash('sha512', $passPhrase . 'a', true);
510
-		return hash_hmac('sha256', $data, $passPhrase);
511
-	}
512
-
513
-
514
-	/**
515
-	 * remove padding
516
-	 *
517
-	 * @param string $padded
518
-	 * @param bool $hasSignature did the block contain a signature, in this case we use a different padding
519
-	 * @return string|false
520
-	 */
521
-	private function removePadding($padded, $hasSignature = false) {
522
-		if ($hasSignature === false && substr($padded, -2) === 'xx') {
523
-			return substr($padded, 0, -2);
524
-		} elseif ($hasSignature === true && substr($padded, -3) === 'xxx') {
525
-			return substr($padded, 0, -3);
526
-		}
527
-		return false;
528
-	}
529
-
530
-	/**
531
-	 * split meta data from encrypted file
532
-	 * Note: for now, we assume that the meta data always start with the iv
533
-	 *       followed by the signature, if available
534
-	 *
535
-	 * @param string $catFile
536
-	 * @param string $cipher
537
-	 * @return array
538
-	 */
539
-	private function splitMetaData($catFile, $cipher) {
540
-		if ($this->hasSignature($catFile, $cipher)) {
541
-			$catFile = $this->removePadding($catFile, true);
542
-			$meta = substr($catFile, -93);
543
-			$iv = substr($meta, strlen('00iv00'), 16);
544
-			$sig = substr($meta, 22 + strlen('00sig00'));
545
-			$encrypted = substr($catFile, 0, -93);
546
-		} else {
547
-			$catFile = $this->removePadding($catFile);
548
-			$meta = substr($catFile, -22);
549
-			$iv = substr($meta, -16);
550
-			$sig = false;
551
-			$encrypted = substr($catFile, 0, -22);
552
-		}
553
-
554
-		return [
555
-			'encrypted' => $encrypted,
556
-			'iv' => $iv,
557
-			'signature' => $sig
558
-		];
559
-	}
560
-
561
-	/**
562
-	 * check if encrypted block is signed
563
-	 *
564
-	 * @param string $catFile
565
-	 * @param string $cipher
566
-	 * @return bool
567
-	 * @throws GenericEncryptionException
568
-	 */
569
-	private function hasSignature($catFile, $cipher) {
570
-		$skipSignatureCheck = $this->config->getSystemValue('encryption_skip_signature_check', false);
571
-
572
-		$meta = substr($catFile, -93);
573
-		$signaturePosition = strpos($meta, '00sig00');
574
-
575
-		// If we no longer support the legacy format then everything needs a signature
576
-		if (!$skipSignatureCheck && !$this->supportLegacy && $signaturePosition === false) {
577
-			throw new GenericEncryptionException('Missing Signature', $this->l->t('Missing Signature'));
578
-		}
579
-
580
-		// enforce signature for the new 'CTR' ciphers
581
-		if (!$skipSignatureCheck && $signaturePosition === false && stripos($cipher, 'ctr') !== false) {
582
-			throw new GenericEncryptionException('Missing Signature', $this->l->t('Missing Signature'));
583
-		}
584
-
585
-		return ($signaturePosition !== false);
586
-	}
587
-
588
-
589
-	/**
590
-	 * @param string $encryptedContent
591
-	 * @param string $iv
592
-	 * @param string $passPhrase
593
-	 * @param string $cipher
594
-	 * @return string
595
-	 * @throws DecryptionFailedException
596
-	 */
597
-	private function decrypt($encryptedContent, $iv, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) {
598
-		$plainContent = openssl_decrypt($encryptedContent,
599
-			$cipher,
600
-			$passPhrase,
601
-			false,
602
-			$iv);
603
-
604
-		if ($plainContent) {
605
-			return $plainContent;
606
-		} else {
607
-			throw new DecryptionFailedException('Encryption library: Decryption (symmetric) of content failed: ' . openssl_error_string());
608
-		}
609
-	}
610
-
611
-	/**
612
-	 * @param string $data
613
-	 * @return array
614
-	 */
615
-	protected function parseHeader($data) {
616
-		$result = [];
617
-
618
-		if (substr($data, 0, strlen(self::HEADER_START)) === self::HEADER_START) {
619
-			$endAt = strpos($data, self::HEADER_END);
620
-			$header = substr($data, 0, $endAt + strlen(self::HEADER_END));
621
-
622
-			// +1 not to start with an ':' which would result in empty element at the beginning
623
-			$exploded = explode(':',
624
-				substr($header, strlen(self::HEADER_START) + 1));
625
-
626
-			$element = array_shift($exploded);
627
-
628
-			while ($element !== self::HEADER_END) {
629
-				$result[$element] = array_shift($exploded);
630
-				$element = array_shift($exploded);
631
-			}
632
-		}
633
-
634
-		return $result;
635
-	}
636
-
637
-	/**
638
-	 * generate initialization vector
639
-	 *
640
-	 * @return string
641
-	 * @throws GenericEncryptionException
642
-	 */
643
-	private function generateIv() {
644
-		return random_bytes(16);
645
-	}
646
-
647
-	/**
648
-	 * Generate a cryptographically secure pseudo-random 256-bit ASCII key, used
649
-	 * as file key
650
-	 *
651
-	 * @return string
652
-	 * @throws \Exception
653
-	 */
654
-	public function generateFileKey() {
655
-		return random_bytes(32);
656
-	}
657
-
658
-	/**
659
-	 * @param $encKeyFile
660
-	 * @param $shareKey
661
-	 * @param $privateKey
662
-	 * @return string
663
-	 * @throws MultiKeyDecryptException
664
-	 */
665
-	public function multiKeyDecrypt($encKeyFile, $shareKey, $privateKey) {
666
-		if (!$encKeyFile) {
667
-			throw new MultiKeyDecryptException('Cannot multikey decrypt empty plain content');
668
-		}
669
-
670
-		if (openssl_open($encKeyFile, $plainContent, $shareKey, $privateKey, 'RC4')) {
671
-			return $plainContent;
672
-		} else {
673
-			throw new MultiKeyDecryptException('multikeydecrypt with share key failed:' . openssl_error_string());
674
-		}
675
-	}
676
-
677
-	/**
678
-	 * @param string $plainContent
679
-	 * @param array $keyFiles
680
-	 * @return array
681
-	 * @throws MultiKeyEncryptException
682
-	 */
683
-	public function multiKeyEncrypt($plainContent, array $keyFiles) {
684
-		// openssl_seal returns false without errors if plaincontent is empty
685
-		// so trigger our own error
686
-		if (empty($plainContent)) {
687
-			throw new MultiKeyEncryptException('Cannot multikeyencrypt empty plain content');
688
-		}
689
-
690
-		// Set empty vars to be set by openssl by reference
691
-		$sealed = '';
692
-		$shareKeys = [];
693
-		$mappedShareKeys = [];
694
-
695
-		if (openssl_seal($plainContent, $sealed, $shareKeys, $keyFiles, 'RC4')) {
696
-			$i = 0;
697
-
698
-			// Ensure each shareKey is labelled with its corresponding key id
699
-			foreach ($keyFiles as $userId => $publicKey) {
700
-				$mappedShareKeys[$userId] = $shareKeys[$i];
701
-				$i++;
702
-			}
703
-
704
-			return [
705
-				'keys' => $mappedShareKeys,
706
-				'data' => $sealed
707
-			];
708
-		} else {
709
-			throw new MultiKeyEncryptException('multikeyencryption failed ' . openssl_error_string());
710
-		}
711
-	}
59
+    public const SUPPORTED_CIPHERS_AND_KEY_SIZE = [
60
+        'AES-256-CTR' => 32,
61
+        'AES-128-CTR' => 16,
62
+        'AES-256-CFB' => 32,
63
+        'AES-128-CFB' => 16,
64
+    ];
65
+    // one out of SUPPORTED_CIPHERS_AND_KEY_SIZE
66
+    public const DEFAULT_CIPHER = 'AES-256-CTR';
67
+    // default cipher from old Nextcloud versions
68
+    public const LEGACY_CIPHER = 'AES-128-CFB';
69
+
70
+    public const SUPPORTED_KEY_FORMATS = ['hash', 'password'];
71
+    // one out of SUPPORTED_KEY_FORMATS
72
+    public const DEFAULT_KEY_FORMAT = 'hash';
73
+    // default key format, old Nextcloud version encrypted the private key directly
74
+    // with the user password
75
+    public const LEGACY_KEY_FORMAT = 'password';
76
+
77
+    public const HEADER_START = 'HBEGIN';
78
+    public const HEADER_END = 'HEND';
79
+
80
+    /** @var ILogger */
81
+    private $logger;
82
+
83
+    /** @var string */
84
+    private $user;
85
+
86
+    /** @var IConfig */
87
+    private $config;
88
+
89
+    /** @var IL10N */
90
+    private $l;
91
+
92
+    /** @var bool */
93
+    private $supportLegacy;
94
+
95
+    /**
96
+     * @param ILogger $logger
97
+     * @param IUserSession $userSession
98
+     * @param IConfig $config
99
+     * @param IL10N $l
100
+     */
101
+    public function __construct(ILogger $logger, IUserSession $userSession, IConfig $config, IL10N $l) {
102
+        $this->logger = $logger;
103
+        $this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : '"no user given"';
104
+        $this->config = $config;
105
+        $this->l = $l;
106
+        $this->supportLegacy = $this->config->getSystemValueBool('encryption.legacy_format_support', false);
107
+    }
108
+
109
+    /**
110
+     * create new private/public key-pair for user
111
+     *
112
+     * @return array|bool
113
+     */
114
+    public function createKeyPair() {
115
+        $log = $this->logger;
116
+        $res = $this->getOpenSSLPKey();
117
+
118
+        if (!$res) {
119
+            $log->error("Encryption Library couldn't generate users key-pair for {$this->user}",
120
+                ['app' => 'encryption']);
121
+
122
+            if (openssl_error_string()) {
123
+                $log->error('Encryption library openssl_pkey_new() fails: ' . openssl_error_string(),
124
+                    ['app' => 'encryption']);
125
+            }
126
+        } elseif (openssl_pkey_export($res,
127
+            $privateKey,
128
+            null,
129
+            $this->getOpenSSLConfig())) {
130
+            $keyDetails = openssl_pkey_get_details($res);
131
+            $publicKey = $keyDetails['key'];
132
+
133
+            return [
134
+                'publicKey' => $publicKey,
135
+                'privateKey' => $privateKey
136
+            ];
137
+        }
138
+        $log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.' . $this->user,
139
+            ['app' => 'encryption']);
140
+        if (openssl_error_string()) {
141
+            $log->error('Encryption Library:' . openssl_error_string(),
142
+                ['app' => 'encryption']);
143
+        }
144
+
145
+        return false;
146
+    }
147
+
148
+    /**
149
+     * Generates a new private key
150
+     *
151
+     * @return resource
152
+     */
153
+    public function getOpenSSLPKey() {
154
+        $config = $this->getOpenSSLConfig();
155
+        return openssl_pkey_new($config);
156
+    }
157
+
158
+    /**
159
+     * get openSSL Config
160
+     *
161
+     * @return array
162
+     */
163
+    private function getOpenSSLConfig() {
164
+        $config = ['private_key_bits' => 4096];
165
+        $config = array_merge(
166
+            $config,
167
+            $this->config->getSystemValue('openssl', [])
168
+        );
169
+        return $config;
170
+    }
171
+
172
+    /**
173
+     * @param string $plainContent
174
+     * @param string $passPhrase
175
+     * @param int $version
176
+     * @param int $position
177
+     * @return false|string
178
+     * @throws EncryptionFailedException
179
+     */
180
+    public function symmetricEncryptFileContent($plainContent, $passPhrase, $version, $position) {
181
+        if (!$plainContent) {
182
+            $this->logger->error('Encryption Library, symmetrical encryption failed no content given',
183
+                ['app' => 'encryption']);
184
+            return false;
185
+        }
186
+
187
+        $iv = $this->generateIv();
188
+
189
+        $encryptedContent = $this->encrypt($plainContent,
190
+            $iv,
191
+            $passPhrase,
192
+            $this->getCipher());
193
+
194
+        // Create a signature based on the key as well as the current version
195
+        $sig = $this->createSignature($encryptedContent, $passPhrase.'_'.$version.'_'.$position);
196
+
197
+        // combine content to encrypt the IV identifier and actual IV
198
+        $catFile = $this->concatIV($encryptedContent, $iv);
199
+        $catFile = $this->concatSig($catFile, $sig);
200
+        return $this->addPadding($catFile);
201
+    }
202
+
203
+    /**
204
+     * generate header for encrypted file
205
+     *
206
+     * @param string $keyFormat see SUPPORTED_KEY_FORMATS
207
+     * @return string
208
+     * @throws \InvalidArgumentException
209
+     */
210
+    public function generateHeader($keyFormat = self::DEFAULT_KEY_FORMAT) {
211
+        if (in_array($keyFormat, self::SUPPORTED_KEY_FORMATS, true) === false) {
212
+            throw new \InvalidArgumentException('key format "' . $keyFormat . '" is not supported');
213
+        }
214
+
215
+        $cipher = $this->getCipher();
216
+
217
+        $header = self::HEADER_START
218
+            . ':cipher:' . $cipher
219
+            . ':keyFormat:' . $keyFormat
220
+            . ':' . self::HEADER_END;
221
+
222
+        return $header;
223
+    }
224
+
225
+    /**
226
+     * @param string $plainContent
227
+     * @param string $iv
228
+     * @param string $passPhrase
229
+     * @param string $cipher
230
+     * @return string
231
+     * @throws EncryptionFailedException
232
+     */
233
+    private function encrypt($plainContent, $iv, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) {
234
+        $encryptedContent = openssl_encrypt($plainContent,
235
+            $cipher,
236
+            $passPhrase,
237
+            false,
238
+            $iv);
239
+
240
+        if (!$encryptedContent) {
241
+            $error = 'Encryption (symmetric) of content failed';
242
+            $this->logger->error($error . openssl_error_string(),
243
+                ['app' => 'encryption']);
244
+            throw new EncryptionFailedException($error);
245
+        }
246
+
247
+        return $encryptedContent;
248
+    }
249
+
250
+    /**
251
+     * return Cipher either from config.php or the default cipher defined in
252
+     * this class
253
+     *
254
+     * @return string
255
+     */
256
+    public function getCipher() {
257
+        $cipher = $this->config->getSystemValue('cipher', self::DEFAULT_CIPHER);
258
+        if (!isset(self::SUPPORTED_CIPHERS_AND_KEY_SIZE[$cipher])) {
259
+            $this->logger->warning(
260
+                sprintf(
261
+                    'Unsupported cipher (%s) defined in config.php supported. Falling back to %s',
262
+                    $cipher,
263
+                    self::DEFAULT_CIPHER
264
+                ),
265
+                ['app' => 'encryption']
266
+            );
267
+            $cipher = self::DEFAULT_CIPHER;
268
+        }
269
+
270
+        return $cipher;
271
+    }
272
+
273
+    /**
274
+     * get key size depending on the cipher
275
+     *
276
+     * @param string $cipher
277
+     * @return int
278
+     * @throws \InvalidArgumentException
279
+     */
280
+    protected function getKeySize($cipher) {
281
+        if (isset(self::SUPPORTED_CIPHERS_AND_KEY_SIZE[$cipher])) {
282
+            return self::SUPPORTED_CIPHERS_AND_KEY_SIZE[$cipher];
283
+        }
284
+
285
+        throw new \InvalidArgumentException(
286
+            sprintf(
287
+                    'Unsupported cipher (%s) defined.',
288
+                    $cipher
289
+            )
290
+        );
291
+    }
292
+
293
+    /**
294
+     * get legacy cipher
295
+     *
296
+     * @return string
297
+     */
298
+    public function getLegacyCipher() {
299
+        if (!$this->supportLegacy) {
300
+            throw new ServerNotAvailableException('Legacy cipher is no longer supported!');
301
+        }
302
+
303
+        return self::LEGACY_CIPHER;
304
+    }
305
+
306
+    /**
307
+     * @param string $encryptedContent
308
+     * @param string $iv
309
+     * @return string
310
+     */
311
+    private function concatIV($encryptedContent, $iv) {
312
+        return $encryptedContent . '00iv00' . $iv;
313
+    }
314
+
315
+    /**
316
+     * @param string $encryptedContent
317
+     * @param string $signature
318
+     * @return string
319
+     */
320
+    private function concatSig($encryptedContent, $signature) {
321
+        return $encryptedContent . '00sig00' . $signature;
322
+    }
323
+
324
+    /**
325
+     * Note: This is _NOT_ a padding used for encryption purposes. It is solely
326
+     * used to achieve the PHP stream size. It has _NOTHING_ to do with the
327
+     * encrypted content and is not used in any crypto primitive.
328
+     *
329
+     * @param string $data
330
+     * @return string
331
+     */
332
+    private function addPadding($data) {
333
+        return $data . 'xxx';
334
+    }
335
+
336
+    /**
337
+     * generate password hash used to encrypt the users private key
338
+     *
339
+     * @param string $password
340
+     * @param string $cipher
341
+     * @param string $uid only used for user keys
342
+     * @return string
343
+     */
344
+    protected function generatePasswordHash($password, $cipher, $uid = '') {
345
+        $instanceId = $this->config->getSystemValue('instanceid');
346
+        $instanceSecret = $this->config->getSystemValue('secret');
347
+        $salt = hash('sha256', $uid . $instanceId . $instanceSecret, true);
348
+        $keySize = $this->getKeySize($cipher);
349
+
350
+        $hash = hash_pbkdf2(
351
+            'sha256',
352
+            $password,
353
+            $salt,
354
+            100000,
355
+            $keySize,
356
+            true
357
+        );
358
+
359
+        return $hash;
360
+    }
361
+
362
+    /**
363
+     * encrypt private key
364
+     *
365
+     * @param string $privateKey
366
+     * @param string $password
367
+     * @param string $uid for regular users, empty for system keys
368
+     * @return false|string
369
+     */
370
+    public function encryptPrivateKey($privateKey, $password, $uid = '') {
371
+        $cipher = $this->getCipher();
372
+        $hash = $this->generatePasswordHash($password, $cipher, $uid);
373
+        $encryptedKey = $this->symmetricEncryptFileContent(
374
+            $privateKey,
375
+            $hash,
376
+            0,
377
+            0
378
+        );
379
+
380
+        return $encryptedKey;
381
+    }
382
+
383
+    /**
384
+     * @param string $privateKey
385
+     * @param string $password
386
+     * @param string $uid for regular users, empty for system keys
387
+     * @return false|string
388
+     */
389
+    public function decryptPrivateKey($privateKey, $password = '', $uid = '') {
390
+        $header = $this->parseHeader($privateKey);
391
+
392
+        if (isset($header['cipher'])) {
393
+            $cipher = $header['cipher'];
394
+        } else {
395
+            $cipher = $this->getLegacyCipher();
396
+        }
397
+
398
+        if (isset($header['keyFormat'])) {
399
+            $keyFormat = $header['keyFormat'];
400
+        } else {
401
+            $keyFormat = self::LEGACY_KEY_FORMAT;
402
+        }
403
+
404
+        if ($keyFormat === self::DEFAULT_KEY_FORMAT) {
405
+            $password = $this->generatePasswordHash($password, $cipher, $uid);
406
+        }
407
+
408
+        // If we found a header we need to remove it from the key we want to decrypt
409
+        if (!empty($header)) {
410
+            $privateKey = substr($privateKey,
411
+                strpos($privateKey,
412
+                    self::HEADER_END) + strlen(self::HEADER_END));
413
+        }
414
+
415
+        $plainKey = $this->symmetricDecryptFileContent(
416
+            $privateKey,
417
+            $password,
418
+            $cipher,
419
+            0
420
+        );
421
+
422
+        if ($this->isValidPrivateKey($plainKey) === false) {
423
+            return false;
424
+        }
425
+
426
+        return $plainKey;
427
+    }
428
+
429
+    /**
430
+     * check if it is a valid private key
431
+     *
432
+     * @param string $plainKey
433
+     * @return bool
434
+     */
435
+    protected function isValidPrivateKey($plainKey) {
436
+        $res = openssl_get_privatekey($plainKey);
437
+        // TODO: remove resource check one php7.4 is not longer supported
438
+        if (is_resource($res) || (is_object($res) && get_class($res) === 'OpenSSLAsymmetricKey')) {
439
+            $sslInfo = openssl_pkey_get_details($res);
440
+            if (isset($sslInfo['key'])) {
441
+                return true;
442
+            }
443
+        }
444
+
445
+        return false;
446
+    }
447
+
448
+    /**
449
+     * @param string $keyFileContents
450
+     * @param string $passPhrase
451
+     * @param string $cipher
452
+     * @param int $version
453
+     * @param int|string $position
454
+     * @return string
455
+     * @throws DecryptionFailedException
456
+     */
457
+    public function symmetricDecryptFileContent($keyFileContents, $passPhrase, $cipher = self::DEFAULT_CIPHER, $version = 0, $position = 0) {
458
+        if ($keyFileContents == '') {
459
+            return '';
460
+        }
461
+
462
+        $catFile = $this->splitMetaData($keyFileContents, $cipher);
463
+
464
+        if ($catFile['signature'] !== false) {
465
+            try {
466
+                // First try the new format
467
+                $this->checkSignature($catFile['encrypted'], $passPhrase . '_' . $version . '_' . $position, $catFile['signature']);
468
+            } catch (GenericEncryptionException $e) {
469
+                // For compatibility with old files check the version without _
470
+                $this->checkSignature($catFile['encrypted'], $passPhrase . $version . $position, $catFile['signature']);
471
+            }
472
+        }
473
+
474
+        return $this->decrypt($catFile['encrypted'],
475
+            $catFile['iv'],
476
+            $passPhrase,
477
+            $cipher);
478
+    }
479
+
480
+    /**
481
+     * check for valid signature
482
+     *
483
+     * @param string $data
484
+     * @param string $passPhrase
485
+     * @param string $expectedSignature
486
+     * @throws GenericEncryptionException
487
+     */
488
+    private function checkSignature($data, $passPhrase, $expectedSignature) {
489
+        $enforceSignature = !$this->config->getSystemValue('encryption_skip_signature_check', false);
490
+
491
+        $signature = $this->createSignature($data, $passPhrase);
492
+        $isCorrectHash = hash_equals($expectedSignature, $signature);
493
+
494
+        if (!$isCorrectHash && $enforceSignature) {
495
+            throw new GenericEncryptionException('Bad Signature', $this->l->t('Bad Signature'));
496
+        } elseif (!$isCorrectHash && !$enforceSignature) {
497
+            $this->logger->info("Signature check skipped", ['app' => 'encryption']);
498
+        }
499
+    }
500
+
501
+    /**
502
+     * create signature
503
+     *
504
+     * @param string $data
505
+     * @param string $passPhrase
506
+     * @return string
507
+     */
508
+    private function createSignature($data, $passPhrase) {
509
+        $passPhrase = hash('sha512', $passPhrase . 'a', true);
510
+        return hash_hmac('sha256', $data, $passPhrase);
511
+    }
512
+
513
+
514
+    /**
515
+     * remove padding
516
+     *
517
+     * @param string $padded
518
+     * @param bool $hasSignature did the block contain a signature, in this case we use a different padding
519
+     * @return string|false
520
+     */
521
+    private function removePadding($padded, $hasSignature = false) {
522
+        if ($hasSignature === false && substr($padded, -2) === 'xx') {
523
+            return substr($padded, 0, -2);
524
+        } elseif ($hasSignature === true && substr($padded, -3) === 'xxx') {
525
+            return substr($padded, 0, -3);
526
+        }
527
+        return false;
528
+    }
529
+
530
+    /**
531
+     * split meta data from encrypted file
532
+     * Note: for now, we assume that the meta data always start with the iv
533
+     *       followed by the signature, if available
534
+     *
535
+     * @param string $catFile
536
+     * @param string $cipher
537
+     * @return array
538
+     */
539
+    private function splitMetaData($catFile, $cipher) {
540
+        if ($this->hasSignature($catFile, $cipher)) {
541
+            $catFile = $this->removePadding($catFile, true);
542
+            $meta = substr($catFile, -93);
543
+            $iv = substr($meta, strlen('00iv00'), 16);
544
+            $sig = substr($meta, 22 + strlen('00sig00'));
545
+            $encrypted = substr($catFile, 0, -93);
546
+        } else {
547
+            $catFile = $this->removePadding($catFile);
548
+            $meta = substr($catFile, -22);
549
+            $iv = substr($meta, -16);
550
+            $sig = false;
551
+            $encrypted = substr($catFile, 0, -22);
552
+        }
553
+
554
+        return [
555
+            'encrypted' => $encrypted,
556
+            'iv' => $iv,
557
+            'signature' => $sig
558
+        ];
559
+    }
560
+
561
+    /**
562
+     * check if encrypted block is signed
563
+     *
564
+     * @param string $catFile
565
+     * @param string $cipher
566
+     * @return bool
567
+     * @throws GenericEncryptionException
568
+     */
569
+    private function hasSignature($catFile, $cipher) {
570
+        $skipSignatureCheck = $this->config->getSystemValue('encryption_skip_signature_check', false);
571
+
572
+        $meta = substr($catFile, -93);
573
+        $signaturePosition = strpos($meta, '00sig00');
574
+
575
+        // If we no longer support the legacy format then everything needs a signature
576
+        if (!$skipSignatureCheck && !$this->supportLegacy && $signaturePosition === false) {
577
+            throw new GenericEncryptionException('Missing Signature', $this->l->t('Missing Signature'));
578
+        }
579
+
580
+        // enforce signature for the new 'CTR' ciphers
581
+        if (!$skipSignatureCheck && $signaturePosition === false && stripos($cipher, 'ctr') !== false) {
582
+            throw new GenericEncryptionException('Missing Signature', $this->l->t('Missing Signature'));
583
+        }
584
+
585
+        return ($signaturePosition !== false);
586
+    }
587
+
588
+
589
+    /**
590
+     * @param string $encryptedContent
591
+     * @param string $iv
592
+     * @param string $passPhrase
593
+     * @param string $cipher
594
+     * @return string
595
+     * @throws DecryptionFailedException
596
+     */
597
+    private function decrypt($encryptedContent, $iv, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) {
598
+        $plainContent = openssl_decrypt($encryptedContent,
599
+            $cipher,
600
+            $passPhrase,
601
+            false,
602
+            $iv);
603
+
604
+        if ($plainContent) {
605
+            return $plainContent;
606
+        } else {
607
+            throw new DecryptionFailedException('Encryption library: Decryption (symmetric) of content failed: ' . openssl_error_string());
608
+        }
609
+    }
610
+
611
+    /**
612
+     * @param string $data
613
+     * @return array
614
+     */
615
+    protected function parseHeader($data) {
616
+        $result = [];
617
+
618
+        if (substr($data, 0, strlen(self::HEADER_START)) === self::HEADER_START) {
619
+            $endAt = strpos($data, self::HEADER_END);
620
+            $header = substr($data, 0, $endAt + strlen(self::HEADER_END));
621
+
622
+            // +1 not to start with an ':' which would result in empty element at the beginning
623
+            $exploded = explode(':',
624
+                substr($header, strlen(self::HEADER_START) + 1));
625
+
626
+            $element = array_shift($exploded);
627
+
628
+            while ($element !== self::HEADER_END) {
629
+                $result[$element] = array_shift($exploded);
630
+                $element = array_shift($exploded);
631
+            }
632
+        }
633
+
634
+        return $result;
635
+    }
636
+
637
+    /**
638
+     * generate initialization vector
639
+     *
640
+     * @return string
641
+     * @throws GenericEncryptionException
642
+     */
643
+    private function generateIv() {
644
+        return random_bytes(16);
645
+    }
646
+
647
+    /**
648
+     * Generate a cryptographically secure pseudo-random 256-bit ASCII key, used
649
+     * as file key
650
+     *
651
+     * @return string
652
+     * @throws \Exception
653
+     */
654
+    public function generateFileKey() {
655
+        return random_bytes(32);
656
+    }
657
+
658
+    /**
659
+     * @param $encKeyFile
660
+     * @param $shareKey
661
+     * @param $privateKey
662
+     * @return string
663
+     * @throws MultiKeyDecryptException
664
+     */
665
+    public function multiKeyDecrypt($encKeyFile, $shareKey, $privateKey) {
666
+        if (!$encKeyFile) {
667
+            throw new MultiKeyDecryptException('Cannot multikey decrypt empty plain content');
668
+        }
669
+
670
+        if (openssl_open($encKeyFile, $plainContent, $shareKey, $privateKey, 'RC4')) {
671
+            return $plainContent;
672
+        } else {
673
+            throw new MultiKeyDecryptException('multikeydecrypt with share key failed:' . openssl_error_string());
674
+        }
675
+    }
676
+
677
+    /**
678
+     * @param string $plainContent
679
+     * @param array $keyFiles
680
+     * @return array
681
+     * @throws MultiKeyEncryptException
682
+     */
683
+    public function multiKeyEncrypt($plainContent, array $keyFiles) {
684
+        // openssl_seal returns false without errors if plaincontent is empty
685
+        // so trigger our own error
686
+        if (empty($plainContent)) {
687
+            throw new MultiKeyEncryptException('Cannot multikeyencrypt empty plain content');
688
+        }
689
+
690
+        // Set empty vars to be set by openssl by reference
691
+        $sealed = '';
692
+        $shareKeys = [];
693
+        $mappedShareKeys = [];
694
+
695
+        if (openssl_seal($plainContent, $sealed, $shareKeys, $keyFiles, 'RC4')) {
696
+            $i = 0;
697
+
698
+            // Ensure each shareKey is labelled with its corresponding key id
699
+            foreach ($keyFiles as $userId => $publicKey) {
700
+                $mappedShareKeys[$userId] = $shareKeys[$i];
701
+                $i++;
702
+            }
703
+
704
+            return [
705
+                'keys' => $mappedShareKeys,
706
+                'data' => $sealed
707
+            ];
708
+        } else {
709
+            throw new MultiKeyEncryptException('multikeyencryption failed ' . openssl_error_string());
710
+        }
711
+    }
712 712
 }
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
 				['app' => 'encryption']);
121 121
 
122 122
 			if (openssl_error_string()) {
123
-				$log->error('Encryption library openssl_pkey_new() fails: ' . openssl_error_string(),
123
+				$log->error('Encryption library openssl_pkey_new() fails: '.openssl_error_string(),
124 124
 					['app' => 'encryption']);
125 125
 			}
126 126
 		} elseif (openssl_pkey_export($res,
@@ -135,10 +135,10 @@  discard block
 block discarded – undo
135 135
 				'privateKey' => $privateKey
136 136
 			];
137 137
 		}
138
-		$log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.' . $this->user,
138
+		$log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.'.$this->user,
139 139
 			['app' => 'encryption']);
140 140
 		if (openssl_error_string()) {
141
-			$log->error('Encryption Library:' . openssl_error_string(),
141
+			$log->error('Encryption Library:'.openssl_error_string(),
142 142
 				['app' => 'encryption']);
143 143
 		}
144 144
 
@@ -209,15 +209,15 @@  discard block
 block discarded – undo
209 209
 	 */
210 210
 	public function generateHeader($keyFormat = self::DEFAULT_KEY_FORMAT) {
211 211
 		if (in_array($keyFormat, self::SUPPORTED_KEY_FORMATS, true) === false) {
212
-			throw new \InvalidArgumentException('key format "' . $keyFormat . '" is not supported');
212
+			throw new \InvalidArgumentException('key format "'.$keyFormat.'" is not supported');
213 213
 		}
214 214
 
215 215
 		$cipher = $this->getCipher();
216 216
 
217 217
 		$header = self::HEADER_START
218
-			. ':cipher:' . $cipher
219
-			. ':keyFormat:' . $keyFormat
220
-			. ':' . self::HEADER_END;
218
+			. ':cipher:'.$cipher
219
+			. ':keyFormat:'.$keyFormat
220
+			. ':'.self::HEADER_END;
221 221
 
222 222
 		return $header;
223 223
 	}
@@ -239,7 +239,7 @@  discard block
 block discarded – undo
239 239
 
240 240
 		if (!$encryptedContent) {
241 241
 			$error = 'Encryption (symmetric) of content failed';
242
-			$this->logger->error($error . openssl_error_string(),
242
+			$this->logger->error($error.openssl_error_string(),
243 243
 				['app' => 'encryption']);
244 244
 			throw new EncryptionFailedException($error);
245 245
 		}
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
 	 * @return string
310 310
 	 */
311 311
 	private function concatIV($encryptedContent, $iv) {
312
-		return $encryptedContent . '00iv00' . $iv;
312
+		return $encryptedContent.'00iv00'.$iv;
313 313
 	}
314 314
 
315 315
 	/**
@@ -318,7 +318,7 @@  discard block
 block discarded – undo
318 318
 	 * @return string
319 319
 	 */
320 320
 	private function concatSig($encryptedContent, $signature) {
321
-		return $encryptedContent . '00sig00' . $signature;
321
+		return $encryptedContent.'00sig00'.$signature;
322 322
 	}
323 323
 
324 324
 	/**
@@ -330,7 +330,7 @@  discard block
 block discarded – undo
330 330
 	 * @return string
331 331
 	 */
332 332
 	private function addPadding($data) {
333
-		return $data . 'xxx';
333
+		return $data.'xxx';
334 334
 	}
335 335
 
336 336
 	/**
@@ -344,7 +344,7 @@  discard block
 block discarded – undo
344 344
 	protected function generatePasswordHash($password, $cipher, $uid = '') {
345 345
 		$instanceId = $this->config->getSystemValue('instanceid');
346 346
 		$instanceSecret = $this->config->getSystemValue('secret');
347
-		$salt = hash('sha256', $uid . $instanceId . $instanceSecret, true);
347
+		$salt = hash('sha256', $uid.$instanceId.$instanceSecret, true);
348 348
 		$keySize = $this->getKeySize($cipher);
349 349
 
350 350
 		$hash = hash_pbkdf2(
@@ -464,10 +464,10 @@  discard block
 block discarded – undo
464 464
 		if ($catFile['signature'] !== false) {
465 465
 			try {
466 466
 				// First try the new format
467
-				$this->checkSignature($catFile['encrypted'], $passPhrase . '_' . $version . '_' . $position, $catFile['signature']);
467
+				$this->checkSignature($catFile['encrypted'], $passPhrase.'_'.$version.'_'.$position, $catFile['signature']);
468 468
 			} catch (GenericEncryptionException $e) {
469 469
 				// For compatibility with old files check the version without _
470
-				$this->checkSignature($catFile['encrypted'], $passPhrase . $version . $position, $catFile['signature']);
470
+				$this->checkSignature($catFile['encrypted'], $passPhrase.$version.$position, $catFile['signature']);
471 471
 			}
472 472
 		}
473 473
 
@@ -506,7 +506,7 @@  discard block
 block discarded – undo
506 506
 	 * @return string
507 507
 	 */
508 508
 	private function createSignature($data, $passPhrase) {
509
-		$passPhrase = hash('sha512', $passPhrase . 'a', true);
509
+		$passPhrase = hash('sha512', $passPhrase.'a', true);
510 510
 		return hash_hmac('sha256', $data, $passPhrase);
511 511
 	}
512 512
 
@@ -604,7 +604,7 @@  discard block
 block discarded – undo
604 604
 		if ($plainContent) {
605 605
 			return $plainContent;
606 606
 		} else {
607
-			throw new DecryptionFailedException('Encryption library: Decryption (symmetric) of content failed: ' . openssl_error_string());
607
+			throw new DecryptionFailedException('Encryption library: Decryption (symmetric) of content failed: '.openssl_error_string());
608 608
 		}
609 609
 	}
610 610
 
@@ -670,7 +670,7 @@  discard block
 block discarded – undo
670 670
 		if (openssl_open($encKeyFile, $plainContent, $shareKey, $privateKey, 'RC4')) {
671 671
 			return $plainContent;
672 672
 		} else {
673
-			throw new MultiKeyDecryptException('multikeydecrypt with share key failed:' . openssl_error_string());
673
+			throw new MultiKeyDecryptException('multikeydecrypt with share key failed:'.openssl_error_string());
674 674
 		}
675 675
 	}
676 676
 
@@ -706,7 +706,7 @@  discard block
 block discarded – undo
706 706
 				'data' => $sealed
707 707
 			];
708 708
 		} else {
709
-			throw new MultiKeyEncryptException('multikeyencryption failed ' . openssl_error_string());
709
+			throw new MultiKeyEncryptException('multikeyencryption failed '.openssl_error_string());
710 710
 		}
711 711
 	}
712 712
 }
Please login to merge, or discard this patch.