diff options
author | brantje <brantje@gmail.com> | 2017-01-02 17:25:41 +0300 |
---|---|---|
committer | brantje <brantje@gmail.com> | 2017-01-11 20:09:49 +0300 |
commit | 734496ebcc04dee8da332618a5c405eef89dbb5b (patch) | |
tree | 56cc209a76e1c590a5da2a8c743a1035bd135287 /lib | |
parent | e5c8c5d1f73a087a50f8f9a4a68f49909ef71769 (diff) |
Implement encryption class
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Db/CredentialMapper.php | 6 | ||||
-rw-r--r-- | lib/Db/CredentialRevision.php | 2 | ||||
-rw-r--r-- | lib/Service/CredentialRevisionService.php | 24 | ||||
-rw-r--r-- | lib/Service/CredentialService.php | 77 | ||||
-rw-r--r-- | lib/Service/EncryptService.php | 182 | ||||
-rw-r--r-- | lib/Service/FileService.php | 29 | ||||
-rw-r--r-- | lib/Service/SettingsService.php | 1 | ||||
-rw-r--r-- | lib/Service/ShareService.php | 17 |
8 files changed, 261 insertions, 77 deletions
diff --git a/lib/Db/CredentialMapper.php b/lib/Db/CredentialMapper.php index 5d3443f6..fd1b8deb 100644 --- a/lib/Db/CredentialMapper.php +++ b/lib/Db/CredentialMapper.php @@ -39,7 +39,7 @@ class CredentialMapper extends Mapper { * Obtains the credentials by vault id (not guid) * @throws \OCP\AppFramework\Db\DoesNotExistException if not found * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result - * @return Vault[] + * @return Credential[] */ public function getCredentialsByVaultId($vault_id, $user_id) { $sql = 'SELECT * FROM `*PREFIX*passman_credentials` ' . @@ -166,7 +166,9 @@ class CredentialMapper extends Mapper { $credential->setOtp($raw_credential['otp']); $credential->setHidden($raw_credential['hidden']); $credential->setDeleteTime($raw_credential['delete_time']); - $credential->setSharedKey($raw_credential['shared_key']); + if(isset($raw_credential['shared_key'])) { + $credential->setSharedKey($raw_credential['shared_key']); + } return parent::update($credential); } diff --git a/lib/Db/CredentialRevision.php b/lib/Db/CredentialRevision.php index 2390fb35..8d829e97 100644 --- a/lib/Db/CredentialRevision.php +++ b/lib/Db/CredentialRevision.php @@ -49,7 +49,7 @@ class CredentialRevision extends Entity implements \JsonSerializable { protected $credentialId; protected $userId; protected $created; - protected $credentialData; + public $credentialData; protected $editedBy; diff --git a/lib/Service/CredentialRevisionService.php b/lib/Service/CredentialRevisionService.php index 06113371..76f29c30 100644 --- a/lib/Service/CredentialRevisionService.php +++ b/lib/Service/CredentialRevisionService.php @@ -33,9 +33,13 @@ use OCA\Passman\Db\CredentialRevisionMapper; class CredentialRevisionService { private $credentialRevisionMapper; + private $encryptService; + private $server_key; - public function __construct(CredentialRevisionMapper $credentialRevisionMapper) { + public function __construct(CredentialRevisionMapper $credentialRevisionMapper, EncryptService $encryptService) { $this->credentialRevisionMapper = $credentialRevisionMapper; + $this->encryptService = $encryptService; + $this->server_key = \OC::$server->getConfig()->getSystemValue('passwordsalt', ''); } /** @@ -47,6 +51,7 @@ class CredentialRevisionService { * @return CredentialRevision */ public function createRevision($credential, $userId, $credential_id, $edited_by) { + $credential = $this->encryptService->encryptCredential($credential); return $this->credentialRevisionMapper->create($credential, $userId, $credential_id, $edited_by); } @@ -57,7 +62,13 @@ class CredentialRevisionService { * @return CredentialRevision[] */ public function getRevisions($credential_id, $user_id = null){ - return $this->credentialRevisionMapper->getRevisions($credential_id, $user_id); + $result = $this->credentialRevisionMapper->getRevisions($credential_id, $user_id); + foreach ($result as $index => $revision){ + $c = json_decode(base64_decode($revision->getCredentialData()), true); + $result[$index] = $revision->jsonSerialize(); + $result[$index]['credential_data'] = $this->encryptService->decryptCredential($c); + } + return $result; } /** @@ -67,7 +78,10 @@ class CredentialRevisionService { * @return CredentialRevision */ public function getRevision($credential_id, $user_id = null){ - return $this->credentialRevisionMapper->getRevision($credential_id, $user_id); + $revision = $this->credentialRevisionMapper->getRevision($credential_id, $user_id); + $c = json_decode(base64_decode($revision->getCredentialData()), true); + $revision->setCredentialData($this->encryptService->decryptCredential($c)); + return $revision; } /** @@ -86,6 +100,10 @@ class CredentialRevisionService { * @return CredentialRevision */ public function updateRevision(CredentialRevision $credentialRevision){ + $credential_data = $credentialRevision->getCredentialData(); + $credential_data = json_decode(base64_decode($credential_data), true); + $credential_data = base64_encode(json_encode($this->encryptService->encryptCredential($credential_data))); + $credentialRevision->setCredentialData($credential_data); return $this->credentialRevisionMapper->update($credentialRevision); } }
\ No newline at end of file diff --git a/lib/Service/CredentialService.php b/lib/Service/CredentialService.php index 9590bb0e..b2d6b871 100644 --- a/lib/Service/CredentialService.php +++ b/lib/Service/CredentialService.php @@ -36,116 +36,147 @@ use OCA\Passman\Db\CredentialMapper; class CredentialService { private $credentialMapper; - private $sharingACL; + private $sharingACL; + private $encryptService; + private $server_key; - public function __construct(CredentialMapper $credentialMapper, SharingACLMapper $sharingACL) { + public function __construct(CredentialMapper $credentialMapper, SharingACLMapper $sharingACL, EncryptService $encryptService) { $this->credentialMapper = $credentialMapper; - $this->sharingACL = $sharingACL; + $this->sharingACL = $sharingACL; + $this->encryptService = $encryptService; + $this->server_key = \OC::$server->getConfig()->getSystemValue('passwordsalt', ''); } /** * Create a new credential + * * @param $user_id * @param $item_guid * @return Credential */ public function createCredential($credential) { + $credential = $this->encryptService->encryptCredential($credential); return $this->credentialMapper->create($credential); } /** * Update credential + * * @param $credential array * @return Credential */ public function updateCredential($credential) { + $credential = $this->encryptService->encryptCredential($credential); return $this->credentialMapper->updateCredential($credential); } /** * Update credential + * * @param $credential Credential + * @return Credential */ public function upd(Credential $credential) { - return $this->credentialMapper->upd($credential); + $credential = $this->encryptService->encryptCredential($credential); + return $this->credentialMapper->updateCredential($credential); } /** * Delete credential + * * @param Credential $credential * @return \OCP\AppFramework\Db\Entity */ - public function deleteCredential(Credential $credential){ + public function deleteCredential(Credential $credential) { return $this->credentialMapper->deleteCredential($credential); } /** * Get credentials by vault id + * * @param $vault_id * @param $user_id - * @return \OCA\Passman\Db\Vault[] + * @return \OCA\Passman\Db\Credential[] */ public function getCredentialsByVaultId($vault_id, $user_id) { - return $this->credentialMapper->getCredentialsByVaultId($vault_id, $user_id); + $credentials = $this->credentialMapper->getCredentialsByVaultId($vault_id, $user_id); + foreach ($credentials as $index => $credential) { + $credentials[$index] = $this->encryptService->decryptCredential($credential); + } + return $credentials; } /** * Get a random credential from given vault + * * @param $vault_id * @param $user_id * @return mixed */ public function getRandomCredentialByVaultId($vault_id, $user_id) { $credentials = $this->credentialMapper->getRandomCredentialByVaultId($vault_id, $user_id); + foreach ($credentials as $index => $credential) { + $credentials[$index] = $this->encryptService->decryptCredential($credential); + } return array_pop($credentials); } /** * Get expired credentials. + * * @param $timestamp * @return \OCA\Passman\Db\Credential[] */ public function getExpiredCredentials($timestamp) { - return $this->credentialMapper->getExpiredCredentials($timestamp); + $credentials = $this->credentialMapper->getExpiredCredentials($timestamp); + foreach ($credentials as $index => $credential) { + $credentials[$index] = $this->encryptService->decryptCredential($credential); + } + return $credentials; } /** * Get a single credential. + * * @param $credential_id * @param $user_id * @return Credential * @throws DoesNotExistException */ - public function getCredentialById($credential_id, $user_id){ - $credential = $this->credentialMapper->getCredentialById($credential_id); - if ($credential->getUserId() === $user_id){ - return $credential; - } - else { - $acl = $this->sharingACL->getItemACL($user_id, $credential->getGuid()); - if ($acl->hasPermission(SharingACL::READ)) { - return $credential; + public function getCredentialById($credential_id, $user_id) { + $credential = $this->credentialMapper->getCredentialById($credential_id); + if ($credential->getUserId() === $user_id) { + return $credential; + } else { + $acl = $this->sharingACL->getItemACL($user_id, $credential->getGuid()); + if ($acl->hasPermission(SharingACL::READ)) { + return $this->encryptService->decryptCredential($credential); } throw new DoesNotExistException("Did expect one result but found none when executing"); - } + + } } /** * Get credential label by credential id. + * * @param $credential_id * @return Credential */ - public function getCredentialLabelById($credential_id){ - return $this->credentialMapper->getCredentialLabelById($credential_id); + public function getCredentialLabelById($credential_id) { + $credential = $this->credentialMapper->getCredentialLabelById($credential_id); + return $this->encryptService->decryptCredential($credential); } /** * Get credential by guid + * * @param $credential_guid * @param null $user_id * @return Credential */ - public function getCredentialByGUID($credential_guid, $user_id = null){ - return $this->credentialMapper->getCredentialByGUID($credential_guid, $user_id); - } + public function getCredentialByGUID($credential_guid, $user_id = null) { + $credential = $this->credentialMapper->getCredentialByGUID($credential_guid, $user_id); + return $this->encryptService->decryptCredential($credential); + } }
\ No newline at end of file diff --git a/lib/Service/EncryptService.php b/lib/Service/EncryptService.php index ea33abfa..8a64516e 100644 --- a/lib/Service/EncryptService.php +++ b/lib/Service/EncryptService.php @@ -26,6 +26,10 @@ namespace OCA\Passman\Service; // Class copied from http://stackoverflow.com/questions/5089841/two-way-encryption-i-need-to-store-passwords-that-can-be-retrieved?answertab=votes#tab-top // Upgraded to use openssl +use Icewind\SMB\Exception\Exception; +use OCA\Passman\Db\Credential; +use OCA\Passman\Db\File; + class EncryptService { /** @@ -36,7 +40,7 @@ class EncryptService { * @var array */ protected $supportedAlgos = array( - 'aes-256' => array('name' => 'AES-256', 'keySize' => 32, 'blockSize' => 32), + 'aes-256-cbc' => array('name' => 'AES-256', 'keySize' => 32, 'blockSize' => 32), 'bf' => array('name' => 'BF', 'keySize' => 16, 'blockSize' => 8), 'des' => array('name' => 'DES', 'keySize' => 7, 'blockSize' => 8), 'des-ede3' => array('name' => 'DES-EDE3', 'keySize' => 21, 'blockSize' => 8), // 3 different 56-bit keys @@ -52,6 +56,12 @@ class EncryptService { 'cbc' => 'CBC', ); + public $encrypted_credential_fields = array( + 'description', 'username', 'password', 'files', 'custom_fields', 'otp', 'email', 'tags', 'url' + ); + + + private $server_key; /** * A class to handle secure encryption and decryption of arbitrary data * @@ -68,32 +78,13 @@ class EncryptService { * */ - /** - * Create an encryption key. Based on given parameters - * - * @param string $userKey The user key to use. This should be specific to this user. - * @param string $serverKey The server key - * @param string $userSuppliedKey A key from the credential (eg guid, name or tags) - * @return string - */ - - public static function makeKey($userKey, $serverKey, $userSuppliedKey) { - $key = hash_hmac('sha512', $userKey, $serverKey); - $key = hash_hmac('sha512', $key, $userSuppliedKey); - return $key; - } /** - * @var string $cipher The mcrypt cipher to use for this instance + * @var string $cipher The openssl cipher to use for this instance */ protected $cipher = ''; /** - * @var int $mode The mcrypt cipher mode to use - */ - protected $mode = ''; - - /** * @var int $rounds The number of rounds to feed into PBKDF2 for key generation */ protected $rounds = 100; @@ -101,14 +92,27 @@ class EncryptService { /** * Constructor! * - * @param string $cipher The MCRYPT_* cypher to use for this instance - * @param int $mode The MCRYPT_MODE_* mode to use for this instance - * @param int $rounds The number of PBKDF2 rounds to do on the key + * @param SettingsService $settings */ - public function __construct($cipher, $mode, $rounds = 100) { - $this->cipher = $cipher; - $this->mode = $mode; - $this->rounds = (int)$rounds; + public function __construct(SettingsService $settings) { + $this->cipher = $settings->getAppSetting('server_side_encryption'); + $this->rounds = (int)100; + $this->server_key = \OC::$server->getConfig()->getSystemValue('passwordsalt', ''); + } + + /** + * Create an encryption key. Based on given parameters + * + * @param string $userKey The user key to use. This should be specific to this user. + * @param string $serverKey The server key + * @param string $userSuppliedKey A key from the credential (eg guid, name or tags) + * @return string + */ + + public static function makeKey($userKey, $serverKey, $userSuppliedKey) { + $key = hash_hmac('sha512', $userKey, $serverKey); + $key = hash_hmac('sha512', $key, $userSuppliedKey); + return $key; } /** @@ -155,7 +159,7 @@ class EncryptService { return false; } - $dec = openssl_decrypt($enc, $this->cipher . '-' . $this->mode, $cipherKey, true, $iv); + $dec = openssl_decrypt($enc, $this->cipher, $cipherKey, true, $iv); $data = $this->unpad($dec); return $data; @@ -173,11 +177,8 @@ class EncryptService { $salt = openssl_random_pseudo_bytes(128); list ($cipherKey, $macKey, $iv) = EncryptService::getKeys($salt, $key); $data = EncryptService::pad($data); - $enc = openssl_encrypt($data, $this->cipher . '-' . $this->mode, $cipherKey, true, $iv); - - + $enc = openssl_encrypt($data, $this->cipher, $cipherKey, true, $iv); $mac = hash_hmac('sha512', $enc, $macKey, true); - $data = bin2hex($salt . $enc . $mac); return $data; @@ -192,8 +193,8 @@ class EncryptService { * @returns array An array of keys (a cipher key, a mac key, and a IV) */ protected function getKeys($salt, $key) { - $ivSize = openssl_cipher_iv_length($this->cipher . '-' . $this->mode); - $keySize = openssl_cipher_iv_length($this->cipher . '-' . $this->mode); + $ivSize = openssl_cipher_iv_length($this->cipher); + $keySize = openssl_cipher_iv_length($this->cipher); $length = 2 * $keySize + $ivSize; $key = EncryptService::pbkdf2('sha512', $key, $salt, $this->rounds, $length); @@ -204,7 +205,7 @@ class EncryptService { return array($cipherKey, $macKey, $iv); } - function hash_equals($a, $b) { + protected function hash_equals($a, $b) { $key = openssl_random_pseudo_bytes(128); return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key); } @@ -247,7 +248,7 @@ class EncryptService { protected function pad($data) { $length = $this->getKeySize(); $padAmount = $length - strlen($data) % $length; - if ($padAmount == 0) { + if ($padAmount === 0) { $padAmount = $length; } return $data . str_repeat(chr($padAmount), $padAmount); @@ -269,4 +270,111 @@ class EncryptService { } return substr($data, 0, -1 * $last); } + + + /** + * Encrypt a credential + * + * @param Credential|array $credential the credential to decrypt + * @return Credential|array + */ + public function decryptCredential($credential) { + return $this->handleCredential($credential, 'decrypt'); + } + + /** + * Encrypt a credential + * + * @param Credential|array $credential the credential to encrypt + * @return Credential|array + * @throws \Exception + */ + public function encryptCredential($credential) { + return $this->handleCredential($credential, 'encrypt'); + } + + /** + * Handles the encryption / decryption of a credential + * + * @param Credential|array $credential the credential to encrypt + * @return Credential|array + * @throws \Exception + */ + private function handleCredential($credential, $op) { + $service_function = ($op === 'encrypt') ? 'encrypt' : 'decrypt'; + if ($credential instanceof Credential) { + $userSuppliedKey = $credential->getLabel(); + $sk = $credential->getSharedKey(); + $userKey = (isset($sk)) ? $sk : $credential->getUserId(); + } else { + $userSuppliedKey = $credential['label']; + $userKey = (isset($credential['shared_key'])) ? $credential['shared_key'] : $credential['user_id']; + } + $key = EncryptService::makeKey($userKey, $this->server_key, $userSuppliedKey); + + foreach ($this->encrypted_credential_fields as $field) { + if ($credential instanceof Credential) { + $field = str_replace(' ', '', str_replace('_', ' ', ucwords($field, '_'))); + $set = 'set' . $field; + $get = 'get' . $field; + $credential->{$set}($this->{$service_function}($credential->{$get}(), $key)); + } else { + $credential[$field] = $this->{$service_function}($credential[$field], $key); + } + } + + return $credential; + } + + /** + * Encrypt a file + * + * @param File|array $file + * @return File|array + */ + + public function encryptFile($file) { + return $this->handleFile($file, 'encrypt'); + } + + /** + * Decrypt a file + * + * @param File|array $file + * @return File|array + */ + + public function decryptFile($file) { + return $this->handleFile($file, 'decrypt'); + } + + /** + * Handles the encryption / decryption of a File + * + * @param File|array $file the credential to encrypt + * @return File|array + * @throws \Exception + */ + private function handleFile($file, $op){ + $service_function = ($op === 'encrypt') ? 'encrypt' : 'decrypt'; + if ($file instanceof File) { + $userSuppliedKey = $file->getSize(); + $userKey = md5($file->getMimetype()); + } else { + $userSuppliedKey = $file['size']; + $userKey = md5($file['mimetype']); + } + + $key = EncryptService::makeKey($userKey, $this->server_key, $userSuppliedKey); + + + if ($file instanceof File) { + $file->setFilename($this->{$service_function}($file->getFilename(), $key)); + $file->setFileData($this->{$service_function}($file->getFileData(), $key)); + } else { + $file['filename'] = $this->{$service_function}($file['filename'], $key); + $file['file_data'] = $this->{$service_function}($file['file_data'], $key); + } + return $file; + } }
\ No newline at end of file diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index 63da182a..d6257085 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -23,6 +23,7 @@ namespace OCA\Passman\Service; +use OCA\Passman\Db\File; use OCP\IConfig; use OCP\AppFramework\Db\DoesNotExistException; @@ -32,43 +33,55 @@ use OCA\Passman\Db\FileMapper; class FileService { private $fileMapper; + private $encryptService; + private $server_key; - public function __construct(FileMapper $fileMapper) { + public function __construct(FileMapper $fileMapper, EncryptService $encryptService) { $this->fileMapper = $fileMapper; + $this->encryptService = $encryptService; + $this->server_key = \OC::$server->getConfig()->getSystemValue('passwordsalt', ''); } /** * Get a single file. This function also returns the file content. + * * @param $fileId * @param null $userId * @return \OCA\Passman\Db\File */ public function getFile($fileId, $userId = null) { - return $this->fileMapper->getFile($fileId, $userId); + $file = $this->fileMapper->getFile($fileId, $userId); + return $this->encryptService->decryptFile($file); } /** * Get a single file. This function also returns the file content. + * * @param $file_guid * @param null $userId * @return \OCA\Passman\Db\File */ public function getFileByGuid($file_guid, $userId = null) { - return $this->fileMapper->getFileByGuid($file_guid, $userId); + $file = $this->fileMapper->getFileByGuid($file_guid, $userId); + return $this->encryptService->decryptFile($file); } /** * Upload a new file, + * * @param $file array * @param $userId * @return \OCA\Passman\Db\File */ public function createFile($file, $userId) { - return $this->fileMapper->create($file, $userId); + $file = $this->encryptService->encryptFile($file); + $file = $this->fileMapper->create($file, $userId); + return $this->getFile($file->getId()); } /** * Delete file + * * @param $file_id * @param $userId * @return \OCA\Passman\Db\File @@ -79,11 +92,13 @@ class FileService { /** * Update file - * @param $file_id + * + * @param File $file * @return \OCA\Passman\Db\File */ - public function updateFile($file_id) { - return $this->fileMapper->updateFile($file_id); + public function updateFile($file) { + $file = $this->encryptService->encryptFile($file); + return $this->fileMapper->updateFile($file); } }
\ No newline at end of file diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php index 813a994e..789b6806 100644 --- a/lib/Service/SettingsService.php +++ b/lib/Service/SettingsService.php @@ -54,6 +54,7 @@ class SettingsService { 'check_version' => intval($this->config->getAppValue('passman', 'check_version', 1)), 'https_check' => intval($this->config->getAppValue('passman', 'https_check', 1)), 'disable_contextmenu' => intval($this->config->getAppValue('passman', 'disable_contextmenu', 1)), + 'server_side_encryption' => $this->config->getAppValue('passman', 'server_side_encryption', 'aes-256-cbc'), 'settings_loaded' => 1 ); } diff --git a/lib/Service/ShareService.php b/lib/Service/ShareService.php index 02fe7aa5..81824915 100644 --- a/lib/Service/ShareService.php +++ b/lib/Service/ShareService.php @@ -39,17 +39,22 @@ class ShareService { private $shareRequest; private $credential; private $revisions; + private $encryptService; + private $server_key; public function __construct( SharingACLMapper $sharingACL, ShareRequestMapper $shareRequest, CredentialMapper $credentials, - CredentialRevisionService $revisions + CredentialRevisionService $revisions, + EncryptService $encryptService ) { $this->sharingACL = $sharingACL; $this->shareRequest = $shareRequest; $this->credential = $credentials; $this->revisions = $revisions; + $this->encryptService = $encryptService; + $this->server_key = \OC::$server->getConfig()->getSystemValue('passwordsalt', ''); } /** @@ -140,9 +145,10 @@ class ShareService { foreach ($entries as $entry) { // Check if the user can read the credential, probably unnecesary, but just to be sure if (!$entry->hasPermission(SharingACL::READ)) continue; - $tmp = $entry->jsonSerialize(); - $tmp['credential_data'] = $this->credential->getCredentialById($entry->getItemId())->jsonSerialize(); + $credential = $this->credential->getCredentialById($entry->getItemId()); + $credential = $this->encryptService->decryptCredential($credential); + $tmp['credential_data'] = $credential->jsonSerialize(); if (!$entry->hasPermission(SharingACL::FILES)) unset($tmp['credential_data']['files']); unset($tmp['credential_data']['shared_key']); @@ -168,7 +174,10 @@ class ShareService { if (!$acl->hasPermission(SharingACL::READ)) throw new DoesNotExistException("Item not found or wrong access level"); $tmp = $acl->jsonSerialize(); - $tmp['credential_data'] = $this->credential->getCredentialById($acl->getItemId())->jsonSerialize(); + $credential = $this->credential->getCredentialById($acl->getItemId()); + $credential = $this->encryptService->decryptCredential($credential); + + $tmp['credential_data'] = $credential->jsonSerialize(); if (!$acl->hasPermission(SharingACL::FILES)) unset($tmp['credential_data']['files']); unset($tmp['credential_data']['shared_key']); |