Completed
Pull Request — master (#5174)
by Björn
17:50
created

KeyManager   F

Complexity

Total Complexity 81

Size/Duplication

Total Lines 684
Duplicated Lines 10.38 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
dl 71
loc 684
rs 2.7214
c 0
b 0
f 0
wmc 81
lcom 1
cbo 13

38 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 14 43 6
A validateShareKey() 13 16 2
B validateMasterKey() 13 30 4
A recoveryKeyExists() 0 4 1
A getRecoveryKey() 0 3 1
A getRecoveryKeyId() 0 3 1
A checkRecoveryPassword() 0 9 2
A storeKeyPair() 0 14 2
A setRecoveryKey() 0 16 2
A setPublicKey() 0 3 1
A setPrivateKey() 0 6 1
A setFileKey() 0 3 1
A setAllFileKeys() 0 6 2
A setShareKey() 0 4 1
B init() 7 34 6
A getPrivateKey() 9 9 2
D getFileKey() 7 40 9
A getVersion() 0 7 2
A setVersion() 0 8 2
A getEncryptedFileKey() 0 6 1
A deleteShareKey() 0 6 1
A getShareKey() 0 4 1
C userHasKeys() 0 25 7
A getPublicKey() 8 8 2
A getPublicShareKeyId() 0 3 1
A getPublicShareKey() 0 3 1
A backupUserKeys() 0 3 1
A deleteUserKeys() 0 4 1
A deletePublicKey() 0 3 1
A deletePrivateKey() 0 3 1
A deleteAllFileKeys() 0 3 1
A getPublicKeys() 0 14 3
A getSystemPrivateKey() 0 3 1
A setSystemPrivateKey() 0 6 1
B addSystemKeys() 0 17 5
A getMasterKeyPassword() 0 8 2
A getMasterKeyId() 0 3 1
A getPublicMasterKey() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complex Class

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

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

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

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

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Björn Schießle <[email protected]>
6
 * @author Clark Tomlinson <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 * @author Vincent Petry <[email protected]>
10
 *
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
namespace OCA\Encryption;
27
28
use OC\Encryption\Exceptions\DecryptionFailedException;
29
use OC\Files\View;
30
use OCA\Encryption\Crypto\Encryption;
31
use OCA\Encryption\Exceptions\PrivateKeyMissingException;
32
use OCA\Encryption\Exceptions\PublicKeyMissingException;
33
use OCA\Encryption\Crypto\Crypt;
34
use OCP\Encryption\Keys\IStorage;
35
use OCP\IConfig;
36
use OCP\ILogger;
37
use OCP\IUserSession;
38
39
class KeyManager {
40
41
	/**
42
	 * @var Session
43
	 */
44
	protected $session;
45
	/**
46
	 * @var IStorage
47
	 */
48
	private $keyStorage;
49
	/**
50
	 * @var Crypt
51
	 */
52
	private $crypt;
53
	/**
54
	 * @var string
55
	 */
56
	private $recoveryKeyId;
57
	/**
58
	 * @var string
59
	 */
60
	private $publicShareKeyId;
61
	/**
62
	 * @var string
63
	 */
64
	private $masterKeyId;
65
	/**
66
	 * @var string UserID
67
	 */
68
	private $keyId;
69
	/**
70
	 * @var string
71
	 */
72
	private $publicKeyId = 'publicKey';
73
	/**
74
	 * @var string
75
	 */
76
	private $privateKeyId = 'privateKey';
77
78
	/**
79
	 * @var string
80
	 */
81
	private $shareKeyId = 'shareKey';
82
83
	/**
84
	 * @var string
85
	 */
86
	private $fileKeyId = 'fileKey';
87
	/**
88
	 * @var IConfig
89
	 */
90
	private $config;
91
	/**
92
	 * @var ILogger
93
	 */
94
	private $log;
95
	/**
96
	 * @var Util
97
	 */
98
	private $util;
99
100
	/**
101
	 * @param IStorage $keyStorage
102
	 * @param Crypt $crypt
103
	 * @param IConfig $config
104
	 * @param IUserSession $userSession
105
	 * @param Session $session
106
	 * @param ILogger $log
107
	 * @param Util $util
108
	 */
109
	public function __construct(
110
		IStorage $keyStorage,
111
		Crypt $crypt,
112
		IConfig $config,
113
		IUserSession $userSession,
114
		Session $session,
115
		ILogger $log,
116
		Util $util
117
	) {
118
119
		$this->util = $util;
120
		$this->session = $session;
121
		$this->keyStorage = $keyStorage;
122
		$this->crypt = $crypt;
123
		$this->config = $config;
124
		$this->log = $log;
125
126
		$this->recoveryKeyId = $this->config->getAppValue('encryption',
127
			'recoveryKeyId');
128 View Code Duplication
		if (empty($this->recoveryKeyId)) {
129
			$this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8);
130
			$this->config->setAppValue('encryption',
131
				'recoveryKeyId',
132
				$this->recoveryKeyId);
133
		}
134
135
		$this->publicShareKeyId = $this->config->getAppValue('encryption',
136
			'publicShareKeyId');
137 View Code Duplication
		if (empty($this->publicShareKeyId)) {
138
			$this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
139
			$this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
140
		}
141
142
		$this->masterKeyId = $this->config->getAppValue('encryption',
143
			'masterKeyId');
144 View Code Duplication
		if (empty($this->masterKeyId)) {
145
			$this->masterKeyId = 'master_' . substr(md5(time()), 0, 8);
146
			$this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId);
147
		}
148
149
		$this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
150
		$this->log = $log;
151
	}
152
153
	/**
154
	 * check if key pair for public link shares exists, if not we create one
155
	 */
156
	public function validateShareKey() {
157
		$shareKey = $this->getPublicShareKey();
158 View Code Duplication
		if (empty($shareKey)) {
159
			$keyPair = $this->crypt->createKeyPair();
160
161
			// Save public key
162
			$this->keyStorage->setSystemUserKey(
163
				$this->publicShareKeyId . '.publicKey', $keyPair['publicKey'],
164
				Encryption::ID);
165
166
			// Encrypt private key empty passphrase
167
			$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
168
			$header = $this->crypt->generateHeader();
169
			$this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
170
		}
171
	}
172
173
	/**
174
	 * check if a key pair for the master key exists, if not we create one
175
	 */
176
	public function validateMasterKey() {
177
178
		if ($this->util->isMasterKeyEnabled() === false) {
179
			return;
180
		}
181
182
		$publicMasterKey = $this->getPublicMasterKey();
183 View Code Duplication
		if (empty($publicMasterKey)) {
184
			$keyPair = $this->crypt->createKeyPair();
185
186
			// Save public key
187
			$this->keyStorage->setSystemUserKey(
188
				$this->masterKeyId . '.publicKey', $keyPair['publicKey'],
189
				Encryption::ID);
190
191
			// Encrypt private key with system password
192
			$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId);
193
			$header = $this->crypt->generateHeader();
194
			$this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey);
195
		}
196
197
		if (!$this->session->isPrivateKeySet()) {
198
			$masterKey = $this->getSystemPrivateKey($this->masterKeyId);
199
			$decryptedMasterKey = $this->crypt->decryptPrivateKey($masterKey, $this->getMasterKeyPassword(), $this->masterKeyId);
200
			$this->session->setPrivateKey($decryptedMasterKey);
0 ignored issues
show
Security Bug introduced by
It seems like $decryptedMasterKey defined by $this->crypt->decryptPri...(), $this->masterKeyId) on line 199 can also be of type false; however, OCA\Encryption\Session::setPrivateKey() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
201
		}
202
203
		// after the encryption key is available we are ready to go
204
		$this->session->setStatus(Session::INIT_SUCCESSFUL);
205
	}
206
207
	/**
208
	 * @return bool
209
	 */
210
	public function recoveryKeyExists() {
211
		$key = $this->getRecoveryKey();
212
		return (!empty($key));
213
	}
214
215
	/**
216
	 * get recovery key
217
	 *
218
	 * @return string
219
	 */
220
	public function getRecoveryKey() {
221
		return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID);
222
	}
223
224
	/**
225
	 * get recovery key ID
226
	 *
227
	 * @return string
228
	 */
229
	public function getRecoveryKeyId() {
230
		return $this->recoveryKeyId;
231
	}
232
233
	/**
234
	 * @param string $password
235
	 * @return bool
236
	 */
237
	public function checkRecoveryPassword($password) {
238
		$recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID);
239
		$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
240
241
		if ($decryptedRecoveryKey) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $decryptedRecoveryKey of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
242
			return true;
243
		}
244
		return false;
245
	}
246
247
	/**
248
	 * @param string $uid
249
	 * @param string $password
250
	 * @param string $keyPair
251
	 * @return bool
252
	 */
253
	public function storeKeyPair($uid, $password, $keyPair) {
254
		// Save Public Key
255
		$this->setPublicKey($uid, $keyPair['publicKey']);
256
257
		$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password, $uid);
258
259
		$header = $this->crypt->generateHeader();
260
261
		if ($encryptedKey) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encryptedKey of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
262
			$this->setPrivateKey($uid, $header . $encryptedKey);
263
			return true;
264
		}
265
		return false;
266
	}
267
268
	/**
269
	 * @param string $password
270
	 * @param array $keyPair
271
	 * @return bool
272
	 */
273
	public function setRecoveryKey($password, $keyPair) {
274
		// Save Public Key
275
		$this->keyStorage->setSystemUserKey($this->getRecoveryKeyId().
276
			'.publicKey',
277
			$keyPair['publicKey'],
278
			Encryption::ID);
279
280
		$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password);
281
		$header = $this->crypt->generateHeader();
282
283
		if ($encryptedKey) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encryptedKey of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
284
			$this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey);
285
			return true;
286
		}
287
		return false;
288
	}
289
290
	/**
291
	 * @param $userId
292
	 * @param $key
293
	 * @return bool
294
	 */
295
	public function setPublicKey($userId, $key) {
296
		return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key, Encryption::ID);
297
	}
298
299
	/**
300
	 * @param $userId
301
	 * @param string $key
302
	 * @return bool
303
	 */
304
	public function setPrivateKey($userId, $key) {
305
		return $this->keyStorage->setUserKey($userId,
306
			$this->privateKeyId,
307
			$key,
308
			Encryption::ID);
309
	}
310
311
	/**
312
	 * write file key to key storage
313
	 *
314
	 * @param string $path
315
	 * @param string $key
316
	 * @return boolean
317
	 */
318
	public function setFileKey($path, $key) {
319
		return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key, Encryption::ID);
320
	}
321
322
	/**
323
	 * set all file keys (the file key and the corresponding share keys)
324
	 *
325
	 * @param string $path
326
	 * @param array $keys
327
	 */
328
	public function setAllFileKeys($path, $keys) {
329
		$this->setFileKey($path, $keys['data']);
330
		foreach ($keys['keys'] as $uid => $keyFile) {
331
			$this->setShareKey($path, $uid, $keyFile);
332
		}
333
	}
334
335
	/**
336
	 * write share key to the key storage
337
	 *
338
	 * @param string $path
339
	 * @param string $uid
340
	 * @param string $key
341
	 * @return boolean
342
	 */
343
	public function setShareKey($path, $uid, $key) {
344
		$keyId = $uid . '.' . $this->shareKeyId;
345
		return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID);
346
	}
347
348
	/**
349
	 * Decrypt private key and store it
350
	 *
351
	 * @param string $uid user id
352
	 * @param string $passPhrase users password
353
	 * @return boolean
354
	 */
355
	public function init($uid, $passPhrase) {
356
357
		$this->session->setStatus(Session::INIT_EXECUTED);
358
359
		try {
360 View Code Duplication
			if($this->util->isMasterKeyEnabled()) {
361
				$uid = $this->getMasterKeyId();
362
				$passPhrase = $this->getMasterKeyPassword();
363
				$privateKey = $this->getSystemPrivateKey($uid);
364
			} else {
365
				$privateKey = $this->getPrivateKey($uid);
366
			}
367
			$privateKey = $this->crypt->decryptPrivateKey($privateKey, $passPhrase, $uid);
368
		} catch (PrivateKeyMissingException $e) {
369
			return false;
370
		} catch (DecryptionFailedException $e) {
371
			return false;
372
		} catch (\Exception $e) {
373
			$this->log->warning(
374
				'Could not decrypt the private key from user "' . $uid . '"" during login. ' .
375
				'Assume password change on the user back-end. Error message: '
376
				. $e->getMessage()
377
			);
378
			return false;
379
		}
380
381
		if ($privateKey) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $privateKey of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
382
			$this->session->setPrivateKey($privateKey);
383
			$this->session->setStatus(Session::INIT_SUCCESSFUL);
384
			return true;
385
		}
386
387
		return false;
388
	}
389
390
	/**
391
	 * @param $userId
392
	 * @return string
393
	 * @throws PrivateKeyMissingException
394
	 */
395 View Code Duplication
	public function getPrivateKey($userId) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
396
		$privateKey = $this->keyStorage->getUserKey($userId,
397
			$this->privateKeyId, Encryption::ID);
398
399
		if (strlen($privateKey) !== 0) {
400
			return $privateKey;
401
		}
402
		throw new PrivateKeyMissingException($userId);
403
	}
404
405
	/**
406
	 * @param string $path
407
	 * @param $uid
408
	 * @return string
409
	 */
410
	public function getFileKey($path, $uid) {
411
		if ($uid === '') {
412
			$uid = null;
413
		}
414
		$publicAccess = is_null($uid);
415
		$encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID);
416
417
		if (empty($encryptedFileKey)) {
418
			return '';
419
		}
420
421
		if ($this->util->isMasterKeyEnabled()) {
422
			$uid = $this->getMasterKeyId();
423
			$shareKey = $this->getShareKey($path, $uid);
424 View Code Duplication
			if ($publicAccess) {
425
				$privateKey = $this->getSystemPrivateKey($uid);
426
				$privateKey = $this->crypt->decryptPrivateKey($privateKey, $this->getMasterKeyPassword(), $uid);
427
			} else {
428
				// when logged in, the master key is already decrypted in the session
429
				$privateKey = $this->session->getPrivateKey();
430
			}
431
		} else if ($publicAccess) {
432
			// use public share key for public links
433
			$uid = $this->getPublicShareKeyId();
434
			$shareKey = $this->getShareKey($path, $uid);
435
			$privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID);
436
			$privateKey = $this->crypt->decryptPrivateKey($privateKey);
437
		} else {
438
			$shareKey = $this->getShareKey($path, $uid);
439
			$privateKey = $this->session->getPrivateKey();
440
		}
441
442
		if ($encryptedFileKey && $shareKey && $privateKey) {
443
			return $this->crypt->multiKeyDecrypt($encryptedFileKey,
444
				$shareKey,
445
				$privateKey);
446
		}
447
448
		return '';
449
	}
450
451
	/**
452
	 * Get the current version of a file
453
	 *
454
	 * @param string $path
455
	 * @param View $view
456
	 * @return int
457
	 */
458
	public function getVersion($path, View $view) {
459
		$fileInfo = $view->getFileInfo($path);
460
		if($fileInfo === false) {
461
			return 0;
462
		}
463
		return $fileInfo->getEncryptedVersion();
464
	}
465
466
	/**
467
	 * Set the current version of a file
468
	 *
469
	 * @param string $path
470
	 * @param int $version
471
	 * @param View $view
472
	 */
473
	public function setVersion($path, $version, View $view) {
474
		$fileInfo= $view->getFileInfo($path);
475
476
		if($fileInfo !== false) {
477
			$cache = $fileInfo->getStorage()->getCache();
478
			$cache->update($fileInfo->getId(), ['encrypted' => $version, 'encryptedVersion' => $version]);
479
		}
480
	}
481
482
	/**
483
	 * get the encrypted file key
484
	 *
485
	 * @param string $path
486
	 * @return string
487
	 */
488
	public function getEncryptedFileKey($path) {
489
		$encryptedFileKey = $this->keyStorage->getFileKey($path,
490
			$this->fileKeyId, Encryption::ID);
491
492
		return $encryptedFileKey;
493
	}
494
495
	/**
496
	 * delete share key
497
	 *
498
	 * @param string $path
499
	 * @param string $keyId
500
	 * @return boolean
501
	 */
502
	public function deleteShareKey($path, $keyId) {
503
		return $this->keyStorage->deleteFileKey(
504
			$path,
505
			$keyId . '.' . $this->shareKeyId,
506
			Encryption::ID);
507
	}
508
509
510
	/**
511
	 * @param $path
512
	 * @param $uid
513
	 * @return mixed
514
	 */
515
	public function getShareKey($path, $uid) {
516
		$keyId = $uid . '.' . $this->shareKeyId;
517
		return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID);
518
	}
519
520
	/**
521
	 * check if user has a private and a public key
522
	 *
523
	 * @param string $userId
524
	 * @return bool
525
	 * @throws PrivateKeyMissingException
526
	 * @throws PublicKeyMissingException
527
	 */
528
	public function userHasKeys($userId) {
529
		$privateKey = $publicKey = true;
530
		$exception = null;
531
532
		try {
533
			$this->getPrivateKey($userId);
534
		} catch (PrivateKeyMissingException $e) {
535
			$privateKey = false;
536
			$exception = $e;
537
		}
538
		try {
539
			$this->getPublicKey($userId);
540
		} catch (PublicKeyMissingException $e) {
541
			$publicKey = false;
542
			$exception = $e;
543
		}
544
545
		if ($privateKey && $publicKey) {
546
			return true;
547
		} elseif (!$privateKey && !$publicKey) {
548
			return false;
549
		} else {
550
			throw $exception;
551
		}
552
	}
553
554
	/**
555
	 * @param $userId
556
	 * @return mixed
557
	 * @throws PublicKeyMissingException
558
	 */
559 View Code Duplication
	public function getPublicKey($userId) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
560
		$publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId, Encryption::ID);
561
562
		if (strlen($publicKey) !== 0) {
563
			return $publicKey;
564
		}
565
		throw new PublicKeyMissingException($userId);
566
	}
567
568
	public function getPublicShareKeyId() {
569
		return $this->publicShareKeyId;
570
	}
571
572
	/**
573
	 * get public key for public link shares
574
	 *
575
	 * @return string
576
	 */
577
	public function getPublicShareKey() {
578
		return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID);
579
	}
580
581
	/**
582
	 * @param string $purpose
583
	 * @param string $uid
584
	 */
585
	public function backupUserKeys($purpose, $uid) {
586
		$this->keyStorage->backupUserKeys(Encryption::ID, $purpose, $uid);
587
	}
588
589
	/**
590
	 * creat a backup of the users private and public key and then  delete it
591
	 *
592
	 * @param string $uid
593
	 */
594
	public function deleteUserKeys($uid) {
595
		$this->deletePublicKey($uid);
596
		$this->deletePrivateKey($uid);
597
	}
598
599
	/**
600
	 * @param $uid
601
	 * @return bool
602
	 */
603
	public function deletePublicKey($uid) {
604
		return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId, Encryption::ID);
605
	}
606
607
	/**
608
	 * @param string $uid
609
	 * @return bool
610
	 */
611
	private function deletePrivateKey($uid) {
612
		return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId, Encryption::ID);
613
	}
614
615
	/**
616
	 * @param string $path
617
	 * @return bool
618
	 */
619
	public function deleteAllFileKeys($path) {
620
		return $this->keyStorage->deleteAllFileKeys($path);
621
	}
622
623
	/**
624
	 * @param array $userIds
625
	 * @return array
626
	 * @throws PublicKeyMissingException
627
	 */
628
	public function getPublicKeys(array $userIds) {
629
		$keys = [];
630
631
		foreach ($userIds as $userId) {
632
			try {
633
				$keys[$userId] = $this->getPublicKey($userId);
634
			} catch (PublicKeyMissingException $e) {
635
				continue;
636
			}
637
		}
638
639
		return $keys;
640
641
	}
642
643
	/**
644
	 * @param string $keyId
645
	 * @return string returns openssl key
646
	 */
647
	public function getSystemPrivateKey($keyId) {
648
		return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID);
649
	}
650
651
	/**
652
	 * @param string $keyId
653
	 * @param string $key
654
	 * @return string returns openssl key
655
	 */
656
	public function setSystemPrivateKey($keyId, $key) {
657
		return $this->keyStorage->setSystemUserKey(
658
			$keyId . '.' . $this->privateKeyId,
659
			$key,
660
			Encryption::ID);
661
	}
662
663
	/**
664
	 * add system keys such as the public share key and the recovery key
665
	 *
666
	 * @param array $accessList
667
	 * @param array $publicKeys
668
	 * @param string $uid
669
	 * @return array
670
	 * @throws PublicKeyMissingException
671
	 */
672
	public function addSystemKeys(array $accessList, array $publicKeys, $uid) {
673
		if (!empty($accessList['public'])) {
674
			$publicShareKey = $this->getPublicShareKey();
675
			if (empty($publicShareKey)) {
676
				throw new PublicKeyMissingException($this->getPublicShareKeyId());
677
			}
678
			$publicKeys[$this->getPublicShareKeyId()] = $publicShareKey;
679
		}
680
681
		if ($this->recoveryKeyExists() &&
682
			$this->util->isRecoveryEnabledForUser($uid)) {
683
684
			$publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey();
685
		}
686
687
		return $publicKeys;
688
	}
689
690
	/**
691
	 * get master key password
692
	 *
693
	 * @return string
694
	 * @throws \Exception
695
	 */
696
	public function getMasterKeyPassword() {
697
		$password = $this->config->getSystemValue('secret');
698
		if (empty($password)){
699
			throw new \Exception('Can not get secret from Nextcloud instance');
700
		}
701
702
		return $password;
703
	}
704
705
	/**
706
	 * return master key id
707
	 *
708
	 * @return string
709
	 */
710
	public function getMasterKeyId() {
711
		return $this->masterKeyId;
712
	}
713
714
	/**
715
	 * get public master key
716
	 *
717
	 * @return string
718
	 */
719
	public function getPublicMasterKey() {
720
		return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID);
721
	}
722
}
723