use OCP\IConfig;
use OCP\ILogger;
use OCP\IUserSession;
+use OCP\Lock\ILockingProvider;
class KeyManager {
*/
private $util;
+ /**
+ * @var ILockingProvider
+ */
+ private $lockingProvider;
+
/**
* @param IStorage $keyStorage
* @param Crypt $crypt
IUserSession $userSession,
Session $session,
ILogger $log,
- Util $util
+ Util $util,
+ ILockingProvider $lockingProvider
) {
$this->util = $util;
$this->session = $session;
$this->crypt = $crypt;
$this->config = $config;
$this->log = $log;
+ $this->lockingProvider = $lockingProvider;
$this->recoveryKeyId = $this->config->getAppValue('encryption',
'recoveryKeyId');
public function validateShareKey() {
$shareKey = $this->getPublicShareKey();
if (empty($shareKey)) {
- $keyPair = $this->crypt->createKeyPair();
-
- // Save public key
- $this->keyStorage->setSystemUserKey(
- $this->publicShareKeyId . '.publicKey', $keyPair['publicKey'],
- Encryption::ID);
-
- // Encrypt private key empty passphrase
- $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
- $header = $this->crypt->generateHeader();
- $this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
+ $this->lockingProvider->acquireLock('encryption-generateSharedKey', ILockingProvider::LOCK_EXCLUSIVE, 'Encryption: shared key generation');
+ try {
+ $keyPair = $this->crypt->createKeyPair();
+
+ // Save public key
+ $this->keyStorage->setSystemUserKey(
+ $this->publicShareKeyId . '.' . $this->publicKeyId, $keyPair['publicKey'],
+ Encryption::ID);
+
+ // Encrypt private key empty passphrase
+ $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
+ $header = $this->crypt->generateHeader();
+ $this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
+ } catch (\Throwable $e) {
+ $this->lockingProvider->releaseLock('encryption-generateSharedKey', ILockingProvider::LOCK_EXCLUSIVE);
+ throw $e;
+ }
+ $this->lockingProvider->releaseLock('encryption-generateSharedKey', ILockingProvider::LOCK_EXCLUSIVE);
}
}
}
$publicMasterKey = $this->getPublicMasterKey();
- if (empty($publicMasterKey)) {
- $keyPair = $this->crypt->createKeyPair();
-
- // Save public key
- $this->keyStorage->setSystemUserKey(
- $this->masterKeyId . '.publicKey', $keyPair['publicKey'],
- Encryption::ID);
-
- // Encrypt private key with system password
- $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId);
- $header = $this->crypt->generateHeader();
- $this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey);
+ $privateMasterKey = $this->getPrivateMasterKey();
+
+ if (empty($publicMasterKey) && empty($privateMasterKey)) {
+ // There could be a race condition here if two requests would trigger
+ // the generation the second one would enter the key generation as long
+ // as the first one didn't write the key to the keystorage yet
+ $this->lockingProvider->acquireLock('encryption-generateMasterKey', ILockingProvider::LOCK_EXCLUSIVE, 'Encryption: master key generation');
+ try {
+ $keyPair = $this->crypt->createKeyPair();
+
+ // Save public key
+ $this->keyStorage->setSystemUserKey(
+ $this->masterKeyId . '.' . $this->publicKeyId, $keyPair['publicKey'],
+ Encryption::ID);
+
+ // Encrypt private key with system password
+ $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId);
+ $header = $this->crypt->generateHeader();
+ $this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey);
+ } catch (\Throwable $e) {
+ $this->lockingProvider->releaseLock('encryption-generateMasterKey', ILockingProvider::LOCK_EXCLUSIVE);
+ throw $e;
+ }
+ $this->lockingProvider->releaseLock('encryption-generateMasterKey', ILockingProvider::LOCK_EXCLUSIVE);
+ } elseif (empty($publicMasterKey)) {
+ $this->log->error('A private master key is available but the public key could not be found. This should never happen.');
+ return;
+ } elseif (empty($privateMasterKey)) {
+ $this->log->error('A private master key is available but the public key could not be found. This should never happen.');
+ return;
}
if (!$this->session->isPrivateKeySet()) {
* @return string
*/
public function getRecoveryKey() {
- return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID);
+ return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.' . $this->publicKeyId, Encryption::ID);
}
/**
* @return bool
*/
public function checkRecoveryPassword($password) {
- $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID);
+ $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.' . $this->privateKeyId, Encryption::ID);
$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
if ($decryptedRecoveryKey) {
/**
* @param string $uid
* @param string $password
- * @param string $keyPair
+ * @param array $keyPair
* @return bool
*/
public function storeKeyPair($uid, $password, $keyPair) {
public function setRecoveryKey($password, $keyPair) {
// Save Public Key
$this->keyStorage->setSystemUserKey($this->getRecoveryKeyId().
- '.publicKey',
+ '.' . $this->publicKeyId,
$keyPair['publicKey'],
Encryption::ID);
// use public share key for public links
$uid = $this->getPublicShareKeyId();
$shareKey = $this->getShareKey($path, $uid);
- $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID);
+ $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.' . $this->privateKeyId, Encryption::ID);
$privateKey = $this->crypt->decryptPrivateKey($privateKey);
} else {
$shareKey = $this->getShareKey($path, $uid);
* @return string
*/
public function getPublicShareKey() {
- return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID);
+ return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.' . $this->publicKeyId, Encryption::ID);
}
/**
* @return string
*/
public function getPublicMasterKey() {
- return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID);
+ return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.' . $this->publicKeyId, Encryption::ID);
+ }
+
+ /**
+ * get public master key
+ *
+ * @return string
+ */
+ public function getPrivateMasterKey() {
+ return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.' . $this->privateKeyId, Encryption::ID);
}
}