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