summaryrefslogtreecommitdiffstats
path: root/apps/encryption/lib
diff options
context:
space:
mode:
authorBjoern Schiessle <schiessle@owncloud.com>2015-08-07 14:04:17 +0200
committerBjoern Schiessle <schiessle@owncloud.com>2015-08-07 15:21:08 +0200
commit62bc0e5264af50be48dbcbb720b7bd16e8d88df5 (patch)
treead0c95913483a7ed2d866066af4ed3d5c00bab6c /apps/encryption/lib
parent43888bb9bf46928acfe79084377b96133609ef6c (diff)
downloadnextcloud-server-62bc0e5264af50be48dbcbb720b7bd16e8d88df5.tar.gz
nextcloud-server-62bc0e5264af50be48dbcbb720b7bd16e8d88df5.zip
use password hash instead of the plain password to encrypt the private key
Diffstat (limited to 'apps/encryption/lib')
-rw-r--r--apps/encryption/lib/crypto/crypt.php139
-rw-r--r--apps/encryption/lib/keymanager.php9
-rw-r--r--apps/encryption/lib/recovery.php2
3 files changed, 135 insertions, 15 deletions
diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php
index f3cf38fb96c..eef16e51447 100644
--- a/apps/encryption/lib/crypto/crypt.php
+++ b/apps/encryption/lib/crypto/crypt.php
@@ -30,6 +30,7 @@ use OC\Encryption\Exceptions\DecryptionFailedException;
use OC\Encryption\Exceptions\EncryptionFailedException;
use OCA\Encryption\Exceptions\MultiKeyDecryptException;
use OCA\Encryption\Exceptions\MultiKeyEncryptException;
+use OCA\Encryption\Vendor\PBKDF2Fallback;
use OCP\Encryption\Exceptions\GenericEncryptionException;
use OCP\IConfig;
use OCP\ILogger;
@@ -42,6 +43,10 @@ class Crypt {
// default cipher from old ownCloud versions
const LEGACY_CIPHER = 'AES-128-CFB';
+ // default key format, old ownCloud version encrypted the private key directly
+ // with the user password
+ const LEGACY_KEY_FORMAT = 'password';
+
const HEADER_START = 'HBEGIN';
const HEADER_END = 'HEND';
/**
@@ -58,6 +63,11 @@ class Crypt {
private $config;
/**
+ * @var array
+ */
+ private $supportedKeyFormats;
+
+ /**
* @param ILogger $logger
* @param IUserSession $userSession
* @param IConfig $config
@@ -66,6 +76,7 @@ class Crypt {
$this->logger = $logger;
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
$this->config = $config;
+ $this->supportedKeyFormats = ['hash', 'password'];
}
/**
@@ -161,10 +172,23 @@ class Crypt {
/**
* generate header for encrypted file
+ *
+ * @param string $keyFormat (can be 'hash' or 'password')
+ * @return string
+ * @throws \InvalidArgumentException
*/
- public function generateHeader() {
+ public function generateHeader($keyFormat = 'hash') {
+
+ if (in_array($keyFormat, $this->supportedKeyFormats, true) === false) {
+ throw new \InvalidArgumentException('key format "' . $keyFormat . '" is not supported');
+ }
+
$cipher = $this->getCipher();
- $header = self::HEADER_START . ':cipher:' . $cipher . ':' . self::HEADER_END;
+
+ $header = self::HEADER_START
+ . ':cipher:' . $cipher
+ . ':keyFormat:' . $keyFormat
+ . ':' . self::HEADER_END;
return $header;
}
@@ -212,6 +236,25 @@ class Crypt {
}
/**
+ * get key size depending on the cipher
+ *
+ * @param string $cipher supported ('AES-256-CFB' and 'AES-128-CFB')
+ * @return int
+ * @throws \InvalidArgumentException
+ */
+ protected function getKeySize($cipher) {
+ if ($cipher === 'AES-256-CFB') {
+ return 32;
+ } else if ($cipher === 'AES-128-CFB') {
+ return 16;
+ }
+
+ throw new \InvalidArgumentException(
+ 'Wrong cipher defined only AES-128-CFB and AES-256-CFB are supported.'
+ );
+ }
+
+ /**
* get legacy cipher
*
* @return string
@@ -238,6 +281,63 @@ class Crypt {
}
/**
+ * generate password hash used to encrypt the users private key
+ *
+ * @param string $password
+ * @param string $cipher
+ * @return string
+ */
+ protected function generatePasswordHash($password, $cipher) {
+ $instanceId = $this->config->getSystemValue('instanceid');
+ $instanceSecret = $this->config->getSystemValue('secret');
+ $salt = hash('sha256', $instanceId . $instanceSecret, true);
+ $keySize = $this->getKeySize($cipher);
+
+ if (function_exists('hash_pbkdf2')) {
+ $hash = hash_pbkdf2(
+ 'sha256',
+ $password,
+ $salt,
+ 100000,
+ $keySize,
+ true
+ );
+ } else {
+ // fallback to 3rdparty lib for PHP <= 5.4.
+ // FIXME: Can be removed as soon as support for PHP 5.4 was dropped
+ $fallback = new PBKDF2Fallback();
+ $hash = $fallback->pbkdf2(
+ 'sha256',
+ $password,
+ $salt,
+ 100000,
+ $keySize,
+ true
+ );
+ }
+
+ return $hash;
+ }
+
+ /**
+ * encrypt private key
+ *
+ * @param string $privateKey
+ * @param string $password
+ * @return bool|string
+ */
+ public function encryptPrivateKey($privateKey, $password) {
+ $cipher = $this->getCipher();
+ $hash = $this->generatePasswordHash($password, $cipher);
+ $encryptedKey = $this->symmetricEncryptFileContent(
+ $privateKey,
+ $hash
+ );
+
+ return $encryptedKey;
+ }
+
+ /**
* @param string $privateKey
* @param string $password
* @return bool|string
@@ -252,6 +352,16 @@ class Crypt {
$cipher = self::LEGACY_CIPHER;
}
+ if (isset($header['keyFormat'])) {
+ $keyFormat = $header['keyFormat'];
+ } else {
+ $keyFormat = self::LEGACY_KEY_FORMAT;
+ }
+
+ if ($keyFormat === 'hash') {
+ $password = $this->generatePasswordHash($password, $cipher);
+ }
+
// If we found a header we need to remove it from the key we want to decrypt
if (!empty($header)) {
$privateKey = substr($privateKey,
@@ -263,18 +373,29 @@ class Crypt {
$password,
$cipher);
- // Check if this is a valid private key
+ if ($this->isValidPrivateKey($plainKey) === false) {
+ return false;
+ }
+
+ return $plainKey;
+ }
+
+ /**
+ * check if it is a valid private key
+ *
+ * @param $plainKey
+ * @return bool
+ */
+ protected function isValidPrivateKey($plainKey) {
$res = openssl_get_privatekey($plainKey);
if (is_resource($res)) {
$sslInfo = openssl_pkey_get_details($res);
- if (!isset($sslInfo['key'])) {
- return false;
+ if (isset($sslInfo['key'])) {
+ return true;
}
- } else {
- return false;
}
- return $plainKey;
+ return true;
}
/**
@@ -358,7 +479,7 @@ class Crypt {
* @param $data
* @return array
*/
- private function parseHeader($data) {
+ protected function parseHeader($data) {
$result = [];
if (substr($data, 0, strlen(self::HEADER_START)) === self::HEADER_START) {
diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php
index 8c8c1f8fd78..47a8e7d391f 100644
--- a/apps/encryption/lib/keymanager.php
+++ b/apps/encryption/lib/keymanager.php
@@ -146,7 +146,7 @@ class KeyManager {
Encryption::ID);
// Encrypt private key empty passphrase
- $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'], '');
+ $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
$header = $this->crypt->generateHeader();
$this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
}
@@ -203,8 +203,8 @@ class KeyManager {
// Save Public Key
$this->setPublicKey($uid, $keyPair['publicKey']);
- $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'],
- $password);
+ $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password);
+
$header = $this->crypt->generateHeader();
if ($encryptedKey) {
@@ -226,8 +226,7 @@ class KeyManager {
$keyPair['publicKey'],
Encryption::ID);
- $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'],
- $password);
+ $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password);
$header = $this->crypt->generateHeader();
if ($encryptedKey) {
diff --git a/apps/encryption/lib/recovery.php b/apps/encryption/lib/recovery.php
index e31a14fba63..e82ecca9d49 100644
--- a/apps/encryption/lib/recovery.php
+++ b/apps/encryption/lib/recovery.php
@@ -136,7 +136,7 @@ class Recovery {
public function changeRecoveryKeyPassword($newPassword, $oldPassword) {
$recoveryKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId());
$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $oldPassword);
- $encryptedRecoveryKey = $this->crypt->symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword);
+ $encryptedRecoveryKey = $this->crypt->encryptPrivateKey($decryptedRecoveryKey, $newPassword);
$header = $this->crypt->generateHeader();
if ($encryptedRecoveryKey) {
$this->keyManager->setSystemPrivateKey($this->keyManager->getRecoveryKeyId(), $header . $encryptedRecoveryKey);