Passed
Push — master ( b604d5...da5844 )
by Morris
11:44 queued 10s
created

KeyManager   F

Complexity

Total Complexity 87

Size/Duplication

Total Lines 718
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 254
dl 0
loc 718
rs 2
c 0
b 0
f 0
wmc 87

39 Methods

Rating   Name   Duplication   Size   Complexity  
A checkRecoveryPassword() 0 8 2
A getEncryptedFileKey() 0 5 1
A getPrivateKey() 0 8 2
A getPublicShareKeyId() 0 2 1
A getVersion() 0 6 2
A setPrivateKey() 0 5 1
B userHasKeys() 0 23 7
A deletePublicKey() 0 2 1
A backupUserKeys() 0 2 1
A getMasterKeyId() 0 2 1
A getRecoveryKeyId() 0 2 1
A setFileKey() 0 2 1
A deletePrivateKey() 0 2 1
A getPrivateMasterKey() 0 2 1
A setVersion() 0 6 2
A setRecoveryKey() 0 15 2
B validateMasterKey() 0 46 8
A deleteShareKey() 0 5 1
A validateShareKey() 0 21 3
B init() 0 32 6
A recoveryKeyExists() 0 3 1
A getRecoveryKey() 0 2 1
A setSystemPrivateKey() 0 5 1
A getMasterKeyPassword() 0 7 2
A addSystemKeys() 0 15 5
A setShareKey() 0 3 1
A getSystemPrivateKey() 0 2 1
B getFileKey() 0 39 9
A deleteAllFileKeys() 0 2 1
A getPublicKey() 0 7 2
A storeKeyPair() 0 13 2
A deleteUserKeys() 0 3 1
A getPublicMasterKey() 0 2 1
A getPublicShareKey() 0 2 1
B __construct() 0 43 6
A setPublicKey() 0 2 1
A setAllFileKeys() 0 4 2
A getShareKey() 0 3 1
A getPublicKeys() 0 12 3

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Clark Tomlinson <[email protected]>
10
 * @author Lukas Reschke <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 * @author Thomas Müller <[email protected]>
14
 * @author Vincent Petry <[email protected]>
15
 *
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program. If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
32
namespace OCA\Encryption;
33
34
use OC\Encryption\Exceptions\DecryptionFailedException;
35
use OC\Files\View;
36
use OCA\Encryption\Crypto\Crypt;
37
use OCA\Encryption\Crypto\Encryption;
38
use OCA\Encryption\Exceptions\PrivateKeyMissingException;
39
use OCA\Encryption\Exceptions\PublicKeyMissingException;
40
use OCP\Encryption\Keys\IStorage;
41
use OCP\IConfig;
42
use OCP\ILogger;
43
use OCP\IUserSession;
44
use OCP\Lock\ILockingProvider;
45
46
class KeyManager {
47
48
	/**
49
	 * @var Session
50
	 */
51
	protected $session;
52
	/**
53
	 * @var IStorage
54
	 */
55
	private $keyStorage;
56
	/**
57
	 * @var Crypt
58
	 */
59
	private $crypt;
60
	/**
61
	 * @var string
62
	 */
63
	private $recoveryKeyId;
64
	/**
65
	 * @var string
66
	 */
67
	private $publicShareKeyId;
68
	/**
69
	 * @var string
70
	 */
71
	private $masterKeyId;
72
	/**
73
	 * @var string UserID
74
	 */
75
	private $keyId;
76
	/**
77
	 * @var string
78
	 */
79
	private $publicKeyId = 'publicKey';
80
	/**
81
	 * @var string
82
	 */
83
	private $privateKeyId = 'privateKey';
84
85
	/**
86
	 * @var string
87
	 */
88
	private $shareKeyId = 'shareKey';
89
90
	/**
91
	 * @var string
92
	 */
93
	private $fileKeyId = 'fileKey';
94
	/**
95
	 * @var IConfig
96
	 */
97
	private $config;
98
	/**
99
	 * @var ILogger
100
	 */
101
	private $log;
102
	/**
103
	 * @var Util
104
	 */
105
	private $util;
106
107
	/**
108
	 * @var ILockingProvider
109
	 */
110
	private $lockingProvider;
111
112
	/**
113
	 * @param IStorage $keyStorage
114
	 * @param Crypt $crypt
115
	 * @param IConfig $config
116
	 * @param IUserSession $userSession
117
	 * @param Session $session
118
	 * @param ILogger $log
119
	 * @param Util $util
120
	 */
121
	public function __construct(
122
		IStorage $keyStorage,
123
		Crypt $crypt,
124
		IConfig $config,
125
		IUserSession $userSession,
126
		Session $session,
127
		ILogger $log,
128
		Util $util,
129
		ILockingProvider $lockingProvider
130
	) {
131
		$this->util = $util;
132
		$this->session = $session;
133
		$this->keyStorage = $keyStorage;
134
		$this->crypt = $crypt;
135
		$this->config = $config;
136
		$this->log = $log;
137
		$this->lockingProvider = $lockingProvider;
138
139
		$this->recoveryKeyId = $this->config->getAppValue('encryption',
140
			'recoveryKeyId');
141
		if (empty($this->recoveryKeyId)) {
142
			$this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8);
143
			$this->config->setAppValue('encryption',
144
				'recoveryKeyId',
145
				$this->recoveryKeyId);
146
		}
147
148
		$this->publicShareKeyId = $this->config->getAppValue('encryption',
149
			'publicShareKeyId');
150
		if (empty($this->publicShareKeyId)) {
151
			$this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
152
			$this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
153
		}
154
155
		$this->masterKeyId = $this->config->getAppValue('encryption',
156
			'masterKeyId');
157
		if (empty($this->masterKeyId)) {
158
			$this->masterKeyId = 'master_' . substr(md5(time()), 0, 8);
159
			$this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId);
160
		}
161
162
		$this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
0 ignored issues
show
Documentation Bug introduced by
It seems like $userSession && $userSes...ser()->getUID() : false can also be of type false. However, the property $keyId is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
163
		$this->log = $log;
164
	}
165
166
	/**
167
	 * check if key pair for public link shares exists, if not we create one
168
	 */
169
	public function validateShareKey() {
170
		$shareKey = $this->getPublicShareKey();
171
		if (empty($shareKey)) {
172
			$this->lockingProvider->acquireLock('encryption-generateSharedKey', ILockingProvider::LOCK_EXCLUSIVE, 'Encryption: shared key generation');
173
			try {
174
				$keyPair = $this->crypt->createKeyPair();
175
176
				// Save public key
177
				$this->keyStorage->setSystemUserKey(
178
					$this->publicShareKeyId . '.' . $this->publicKeyId, $keyPair['publicKey'],
179
					Encryption::ID);
180
181
				// Encrypt private key empty passphrase
182
				$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
183
				$header = $this->crypt->generateHeader();
184
				$this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
0 ignored issues
show
Bug introduced by
Are you sure $encryptedKey of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

184
				$this->setSystemPrivateKey($this->publicShareKeyId, $header . /** @scrutinizer ignore-type */ $encryptedKey);
Loading history...
185
			} catch (\Throwable $e) {
186
				$this->lockingProvider->releaseLock('encryption-generateSharedKey', ILockingProvider::LOCK_EXCLUSIVE);
187
				throw $e;
188
			}
189
			$this->lockingProvider->releaseLock('encryption-generateSharedKey', ILockingProvider::LOCK_EXCLUSIVE);
190
		}
191
	}
192
193
	/**
194
	 * check if a key pair for the master key exists, if not we create one
195
	 */
196
	public function validateMasterKey() {
197
		if ($this->util->isMasterKeyEnabled() === false) {
198
			return;
199
		}
200
201
		$publicMasterKey = $this->getPublicMasterKey();
202
		$privateMasterKey = $this->getPrivateMasterKey();
203
204
		if (empty($publicMasterKey) && empty($privateMasterKey)) {
205
			// There could be a race condition here if two requests would trigger
206
			// the generation the second one would enter the key generation as long
207
			// as the first one didn't write the key to the keystorage yet
208
			$this->lockingProvider->acquireLock('encryption-generateMasterKey', ILockingProvider::LOCK_EXCLUSIVE, 'Encryption: master key generation');
209
			try {
210
				$keyPair = $this->crypt->createKeyPair();
211
212
				// Save public key
213
				$this->keyStorage->setSystemUserKey(
214
					$this->masterKeyId . '.' . $this->publicKeyId, $keyPair['publicKey'],
215
					Encryption::ID);
216
217
				// Encrypt private key with system password
218
				$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId);
219
				$header = $this->crypt->generateHeader();
220
				$this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey);
0 ignored issues
show
Bug introduced by
Are you sure $encryptedKey of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

220
				$this->setSystemPrivateKey($this->masterKeyId, $header . /** @scrutinizer ignore-type */ $encryptedKey);
Loading history...
221
			} catch (\Throwable $e) {
222
				$this->lockingProvider->releaseLock('encryption-generateMasterKey', ILockingProvider::LOCK_EXCLUSIVE);
223
				throw $e;
224
			}
225
			$this->lockingProvider->releaseLock('encryption-generateMasterKey', ILockingProvider::LOCK_EXCLUSIVE);
226
		} elseif (empty($publicMasterKey)) {
227
			$this->log->error('A private master key is available but the public key could not be found. This should never happen.');
228
			return;
229
		} elseif (empty($privateMasterKey)) {
230
			$this->log->error('A public master key is available but the private key could not be found. This should never happen.');
231
			return;
232
		}
233
234
		if (!$this->session->isPrivateKeySet()) {
235
			$masterKey = $this->getSystemPrivateKey($this->masterKeyId);
236
			$decryptedMasterKey = $this->crypt->decryptPrivateKey($masterKey, $this->getMasterKeyPassword(), $this->masterKeyId);
237
			$this->session->setPrivateKey($decryptedMasterKey);
238
		}
239
240
		// after the encryption key is available we are ready to go
241
		$this->session->setStatus(Session::INIT_SUCCESSFUL);
242
	}
243
244
	/**
245
	 * @return bool
246
	 */
247
	public function recoveryKeyExists() {
248
		$key = $this->getRecoveryKey();
249
		return !empty($key);
250
	}
251
252
	/**
253
	 * get recovery key
254
	 *
255
	 * @return string
256
	 */
257
	public function getRecoveryKey() {
258
		return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.' . $this->publicKeyId, Encryption::ID);
259
	}
260
261
	/**
262
	 * get recovery key ID
263
	 *
264
	 * @return string
265
	 */
266
	public function getRecoveryKeyId() {
267
		return $this->recoveryKeyId;
268
	}
269
270
	/**
271
	 * @param string $password
272
	 * @return bool
273
	 */
274
	public function checkRecoveryPassword($password) {
275
		$recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.' . $this->privateKeyId, Encryption::ID);
276
		$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
277
278
		if ($decryptedRecoveryKey) {
279
			return true;
280
		}
281
		return false;
282
	}
283
284
	/**
285
	 * @param string $uid
286
	 * @param string $password
287
	 * @param array $keyPair
288
	 * @return bool
289
	 */
290
	public function storeKeyPair($uid, $password, $keyPair) {
291
		// Save Public Key
292
		$this->setPublicKey($uid, $keyPair['publicKey']);
293
294
		$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password, $uid);
295
296
		$header = $this->crypt->generateHeader();
297
298
		if ($encryptedKey) {
299
			$this->setPrivateKey($uid, $header . $encryptedKey);
300
			return true;
301
		}
302
		return false;
303
	}
304
305
	/**
306
	 * @param string $password
307
	 * @param array $keyPair
308
	 * @return bool
309
	 */
310
	public function setRecoveryKey($password, $keyPair) {
311
		// Save Public Key
312
		$this->keyStorage->setSystemUserKey($this->getRecoveryKeyId().
313
			'.' . $this->publicKeyId,
314
			$keyPair['publicKey'],
315
			Encryption::ID);
316
317
		$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password);
318
		$header = $this->crypt->generateHeader();
319
320
		if ($encryptedKey) {
321
			$this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey);
322
			return true;
323
		}
324
		return false;
325
	}
326
327
	/**
328
	 * @param $userId
329
	 * @param $key
330
	 * @return bool
331
	 */
332
	public function setPublicKey($userId, $key) {
333
		return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key, Encryption::ID);
334
	}
335
336
	/**
337
	 * @param $userId
338
	 * @param string $key
339
	 * @return bool
340
	 */
341
	public function setPrivateKey($userId, $key) {
342
		return $this->keyStorage->setUserKey($userId,
343
			$this->privateKeyId,
344
			$key,
345
			Encryption::ID);
346
	}
347
348
	/**
349
	 * write file key to key storage
350
	 *
351
	 * @param string $path
352
	 * @param string $key
353
	 * @return boolean
354
	 */
355
	public function setFileKey($path, $key) {
356
		return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key, Encryption::ID);
357
	}
358
359
	/**
360
	 * set all file keys (the file key and the corresponding share keys)
361
	 *
362
	 * @param string $path
363
	 * @param array $keys
364
	 */
365
	public function setAllFileKeys($path, $keys) {
366
		$this->setFileKey($path, $keys['data']);
367
		foreach ($keys['keys'] as $uid => $keyFile) {
368
			$this->setShareKey($path, $uid, $keyFile);
369
		}
370
	}
371
372
	/**
373
	 * write share key to the key storage
374
	 *
375
	 * @param string $path
376
	 * @param string $uid
377
	 * @param string $key
378
	 * @return boolean
379
	 */
380
	public function setShareKey($path, $uid, $key) {
381
		$keyId = $uid . '.' . $this->shareKeyId;
382
		return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID);
383
	}
384
385
	/**
386
	 * Decrypt private key and store it
387
	 *
388
	 * @param string $uid user id
389
	 * @param string $passPhrase users password
390
	 * @return boolean
391
	 */
392
	public function init($uid, $passPhrase) {
393
		$this->session->setStatus(Session::INIT_EXECUTED);
394
395
		try {
396
			if ($this->util->isMasterKeyEnabled()) {
397
				$uid = $this->getMasterKeyId();
398
				$passPhrase = $this->getMasterKeyPassword();
399
				$privateKey = $this->getSystemPrivateKey($uid);
400
			} else {
401
				$privateKey = $this->getPrivateKey($uid);
402
			}
403
			$privateKey = $this->crypt->decryptPrivateKey($privateKey, $passPhrase, $uid);
404
		} catch (PrivateKeyMissingException $e) {
405
			return false;
406
		} catch (DecryptionFailedException $e) {
407
			return false;
408
		} catch (\Exception $e) {
409
			$this->log->logException($e, [
410
				'message' => 'Could not decrypt the private key from user "' . $uid . '"" during login. Assume password change on the user back-end.',
411
				'level' => ILogger::WARN,
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::WARN has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

411
				'level' => /** @scrutinizer ignore-deprecated */ ILogger::WARN,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
412
				'app' => 'encryption',
413
			]);
414
			return false;
415
		}
416
417
		if ($privateKey) {
418
			$this->session->setPrivateKey($privateKey);
419
			$this->session->setStatus(Session::INIT_SUCCESSFUL);
420
			return true;
421
		}
422
423
		return false;
424
	}
425
426
	/**
427
	 * @param $userId
428
	 * @return string
429
	 * @throws PrivateKeyMissingException
430
	 */
431
	public function getPrivateKey($userId) {
432
		$privateKey = $this->keyStorage->getUserKey($userId,
433
			$this->privateKeyId, Encryption::ID);
434
435
		if (strlen($privateKey) !== 0) {
436
			return $privateKey;
437
		}
438
		throw new PrivateKeyMissingException($userId);
439
	}
440
441
	/**
442
	 * @param string $path
443
	 * @param $uid
444
	 * @return string
445
	 */
446
	public function getFileKey($path, $uid) {
447
		if ($uid === '') {
448
			$uid = null;
449
		}
450
		$publicAccess = is_null($uid);
451
		$encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID);
452
453
		if (empty($encryptedFileKey)) {
454
			return '';
455
		}
456
457
		if ($this->util->isMasterKeyEnabled()) {
458
			$uid = $this->getMasterKeyId();
459
			$shareKey = $this->getShareKey($path, $uid);
460
			if ($publicAccess) {
461
				$privateKey = $this->getSystemPrivateKey($uid);
462
				$privateKey = $this->crypt->decryptPrivateKey($privateKey, $this->getMasterKeyPassword(), $uid);
463
			} else {
464
				// when logged in, the master key is already decrypted in the session
465
				$privateKey = $this->session->getPrivateKey();
466
			}
467
		} elseif ($publicAccess) {
468
			// use public share key for public links
469
			$uid = $this->getPublicShareKeyId();
470
			$shareKey = $this->getShareKey($path, $uid);
471
			$privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.' . $this->privateKeyId, Encryption::ID);
472
			$privateKey = $this->crypt->decryptPrivateKey($privateKey);
473
		} else {
474
			$shareKey = $this->getShareKey($path, $uid);
475
			$privateKey = $this->session->getPrivateKey();
476
		}
477
478
		if ($encryptedFileKey && $shareKey && $privateKey) {
479
			return $this->crypt->multiKeyDecrypt($encryptedFileKey,
480
				$shareKey,
481
				$privateKey);
482
		}
483
484
		return '';
485
	}
486
487
	/**
488
	 * Get the current version of a file
489
	 *
490
	 * @param string $path
491
	 * @param View $view
492
	 * @return int
493
	 */
494
	public function getVersion($path, View $view) {
495
		$fileInfo = $view->getFileInfo($path);
496
		if ($fileInfo === false) {
497
			return 0;
498
		}
499
		return $fileInfo->getEncryptedVersion();
500
	}
501
502
	/**
503
	 * Set the current version of a file
504
	 *
505
	 * @param string $path
506
	 * @param int $version
507
	 * @param View $view
508
	 */
509
	public function setVersion($path, $version, View $view) {
510
		$fileInfo= $view->getFileInfo($path);
511
512
		if ($fileInfo !== false) {
513
			$cache = $fileInfo->getStorage()->getCache();
514
			$cache->update($fileInfo->getId(), ['encrypted' => $version, 'encryptedVersion' => $version]);
515
		}
516
	}
517
518
	/**
519
	 * get the encrypted file key
520
	 *
521
	 * @param string $path
522
	 * @return string
523
	 */
524
	public function getEncryptedFileKey($path) {
525
		$encryptedFileKey = $this->keyStorage->getFileKey($path,
526
			$this->fileKeyId, Encryption::ID);
527
528
		return $encryptedFileKey;
529
	}
530
531
	/**
532
	 * delete share key
533
	 *
534
	 * @param string $path
535
	 * @param string $keyId
536
	 * @return boolean
537
	 */
538
	public function deleteShareKey($path, $keyId) {
539
		return $this->keyStorage->deleteFileKey(
540
			$path,
541
			$keyId . '.' . $this->shareKeyId,
542
			Encryption::ID);
543
	}
544
545
546
	/**
547
	 * @param $path
548
	 * @param $uid
549
	 * @return mixed
550
	 */
551
	public function getShareKey($path, $uid) {
552
		$keyId = $uid . '.' . $this->shareKeyId;
553
		return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID);
554
	}
555
556
	/**
557
	 * check if user has a private and a public key
558
	 *
559
	 * @param string $userId
560
	 * @return bool
561
	 * @throws PrivateKeyMissingException
562
	 * @throws PublicKeyMissingException
563
	 */
564
	public function userHasKeys($userId) {
565
		$privateKey = $publicKey = true;
566
		$exception = null;
567
568
		try {
569
			$this->getPrivateKey($userId);
570
		} catch (PrivateKeyMissingException $e) {
571
			$privateKey = false;
572
			$exception = $e;
573
		}
574
		try {
575
			$this->getPublicKey($userId);
576
		} catch (PublicKeyMissingException $e) {
577
			$publicKey = false;
578
			$exception = $e;
579
		}
580
581
		if ($privateKey && $publicKey) {
582
			return true;
583
		} elseif (!$privateKey && !$publicKey) {
584
			return false;
585
		} else {
586
			throw $exception;
587
		}
588
	}
589
590
	/**
591
	 * @param $userId
592
	 * @return mixed
593
	 * @throws PublicKeyMissingException
594
	 */
595
	public function getPublicKey($userId) {
596
		$publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId, Encryption::ID);
597
598
		if (strlen($publicKey) !== 0) {
599
			return $publicKey;
600
		}
601
		throw new PublicKeyMissingException($userId);
602
	}
603
604
	public function getPublicShareKeyId() {
605
		return $this->publicShareKeyId;
606
	}
607
608
	/**
609
	 * get public key for public link shares
610
	 *
611
	 * @return string
612
	 */
613
	public function getPublicShareKey() {
614
		return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.' . $this->publicKeyId, Encryption::ID);
615
	}
616
617
	/**
618
	 * @param string $purpose
619
	 * @param string $uid
620
	 */
621
	public function backupUserKeys($purpose, $uid) {
622
		$this->keyStorage->backupUserKeys(Encryption::ID, $purpose, $uid);
623
	}
624
625
	/**
626
	 * creat a backup of the users private and public key and then  delete it
627
	 *
628
	 * @param string $uid
629
	 */
630
	public function deleteUserKeys($uid) {
631
		$this->deletePublicKey($uid);
632
		$this->deletePrivateKey($uid);
633
	}
634
635
	/**
636
	 * @param $uid
637
	 * @return bool
638
	 */
639
	public function deletePublicKey($uid) {
640
		return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId, Encryption::ID);
641
	}
642
643
	/**
644
	 * @param string $uid
645
	 * @return bool
646
	 */
647
	private function deletePrivateKey($uid) {
648
		return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId, Encryption::ID);
649
	}
650
651
	/**
652
	 * @param string $path
653
	 * @return bool
654
	 */
655
	public function deleteAllFileKeys($path) {
656
		return $this->keyStorage->deleteAllFileKeys($path);
657
	}
658
659
	/**
660
	 * @param array $userIds
661
	 * @return array
662
	 * @throws PublicKeyMissingException
663
	 */
664
	public function getPublicKeys(array $userIds) {
665
		$keys = [];
666
667
		foreach ($userIds as $userId) {
668
			try {
669
				$keys[$userId] = $this->getPublicKey($userId);
670
			} catch (PublicKeyMissingException $e) {
671
				continue;
672
			}
673
		}
674
675
		return $keys;
676
	}
677
678
	/**
679
	 * @param string $keyId
680
	 * @return string returns openssl key
681
	 */
682
	public function getSystemPrivateKey($keyId) {
683
		return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID);
684
	}
685
686
	/**
687
	 * @param string $keyId
688
	 * @param string $key
689
	 * @return string returns openssl key
690
	 */
691
	public function setSystemPrivateKey($keyId, $key) {
692
		return $this->keyStorage->setSystemUserKey(
693
			$keyId . '.' . $this->privateKeyId,
694
			$key,
695
			Encryption::ID);
696
	}
697
698
	/**
699
	 * add system keys such as the public share key and the recovery key
700
	 *
701
	 * @param array $accessList
702
	 * @param array $publicKeys
703
	 * @param string $uid
704
	 * @return array
705
	 * @throws PublicKeyMissingException
706
	 */
707
	public function addSystemKeys(array $accessList, array $publicKeys, $uid) {
708
		if (!empty($accessList['public'])) {
709
			$publicShareKey = $this->getPublicShareKey();
710
			if (empty($publicShareKey)) {
711
				throw new PublicKeyMissingException($this->getPublicShareKeyId());
712
			}
713
			$publicKeys[$this->getPublicShareKeyId()] = $publicShareKey;
714
		}
715
716
		if ($this->recoveryKeyExists() &&
717
			$this->util->isRecoveryEnabledForUser($uid)) {
718
			$publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey();
719
		}
720
721
		return $publicKeys;
722
	}
723
724
	/**
725
	 * get master key password
726
	 *
727
	 * @return string
728
	 * @throws \Exception
729
	 */
730
	public function getMasterKeyPassword() {
731
		$password = $this->config->getSystemValue('secret');
732
		if (empty($password)) {
733
			throw new \Exception('Can not get secret from Nextcloud instance');
734
		}
735
736
		return $password;
737
	}
738
739
	/**
740
	 * return master key id
741
	 *
742
	 * @return string
743
	 */
744
	public function getMasterKeyId() {
745
		return $this->masterKeyId;
746
	}
747
748
	/**
749
	 * get public master key
750
	 *
751
	 * @return string
752
	 */
753
	public function getPublicMasterKey() {
754
		return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.' . $this->publicKeyId, Encryption::ID);
755
	}
756
757
	/**
758
	 * get public master key
759
	 *
760
	 * @return string
761
	 */
762
	public function getPrivateMasterKey() {
763
		return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.' . $this->privateKeyId, Encryption::ID);
764
	}
765
}
766