Adjust reviewed code

This commit is contained in:
brantje 2017-01-11 18:09:10 +01:00
parent 3f4afba34f
commit 5f7661659a
No known key found for this signature in database
GPG key ID: 5FF1D117F918687F
8 changed files with 127 additions and 53 deletions

View file

@ -51,6 +51,15 @@ Untested databases:
For more screenshots: [Click here](http://imgur.com/a/giKVt)
## Encryption (server side)
All passwords are encrypted client side AND server side.
This means that if you move to another server you have to backup the following from config.php
- `passwordsalt`
- `secret`
## Code reviews
If you have any improvements regarding our code.
Please do the following

View file

@ -240,6 +240,11 @@ class CredentialController extends ApiController {
$storedCredential->setSharedKey('');
$credential['shared_key'] = '';
}
if(!isset($credential['shared_key'])){
$credential['shared_key'] = $storedCredential->getSharedKey();
}
if (!$skip_revision) {
$this->credentialRevisionService->createRevision($storedCredential, $storedCredential->getUserId(), $credential_id, $this->userId);
}

View file

@ -49,7 +49,7 @@ class CredentialRevision extends Entity implements \JsonSerializable {
protected $credentialId;
protected $userId;
protected $created;
public $credentialData;
protected $credentialData;
protected $editedBy;

View file

@ -150,6 +150,8 @@ class CredentialService {
$acl = $this->sharingACL->getItemACL($user_id, $credential->getGuid());
if ($acl->hasPermission(SharingACL::READ)) {
return $this->encryptService->decryptCredential($credential);
} else {
throw new DoesNotExistException("Did expect one result but found none when executing");
}
}
}

View file

@ -30,6 +30,21 @@ use Icewind\SMB\Exception\Exception;
use OCA\Passman\Db\Credential;
use OCA\Passman\Db\File;
/**
* A class to handle secure encryption and decryption of arbitrary data
*
* Note that this is not just straight encryption. It also has a few other
* features in it to make the encrypted data far more secure. Note that any
* other implementations used to decrypt data will have to do the same exact
* operations.
*
* Security Benefits:
*
* - Uses Key stretching
* - Hides the Initialization Vector
* - Does HMAC verification of source data
*
*/
class EncryptService {
/**
@ -39,7 +54,7 @@ class EncryptService {
*
* @var array
*/
protected $supportedAlgos = array(
const SUPPORTED_ALGORITHMS = array(
'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),
@ -47,37 +62,16 @@ class EncryptService {
'cast5' => array('name' => 'CAST5', 'keySize' => 16, 'blockSize' => 8),
);
/**
* Supported encryption modes
*
* @var array
*/
protected $supportedModes = array(
'cbc' => 'CBC',
);
const OP_ENCRYPT = 'encrypt';
const OP_DECRYPT = 'decrypt';
// The fields of a credential which are encrypted
public $encrypted_credential_fields = array(
'description', 'username', 'password', 'files', 'custom_fields', 'otp', 'email', 'tags', 'url'
);
// Contains the server key
private $server_key;
/**
* A class to handle secure encryption and decryption of arbitrary data
*
* Note that this is not just straight encryption. It also has a few other
* features in it to make the encrypted data far more secure. Note that any
* other implementations used to decrypt data will have to do the same exact
* operations.
*
* Security Benefits:
*
* - Uses Key stretching
* - Hides the Initialization Vector
* - Does HMAC verification of source data
*
*/
/**
* @var string $cipher The openssl cipher to use for this instance
@ -95,9 +89,11 @@ class EncryptService {
* @param SettingsService $settings
*/
public function __construct(SettingsService $settings) {
$this->cipher = $settings->getAppSetting('server_side_encryption');
$this->rounds = (int)100;
$this->server_key = \OC::$server->getConfig()->getSystemValue('passwordsalt', '');
$this->cipher = $settings->getAppSetting('server_side_encryption', 'aes-256-cbc');
$password_salt = \OC::$server->getConfig()->getSystemValue('passwordsalt', '');
$secret = \OC::$server->getConfig()->getSystemValue('secret', '');
$this->server_key = $password_salt . $secret;
$this->rounds = $settings->getAppSetting('rounds_pbkdf2_stretching', 100);
}
/**
@ -121,7 +117,7 @@ class EncryptService {
* @return int Value is in bytes
*/
public function getKeySize() {
return $this->supportedAlgos[$this->cipher]['keySize'];
return EncryptService::SUPPORTED_ALGORITHMS[$this->cipher]['keySize'];
}
/**
@ -155,7 +151,7 @@ class EncryptService {
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
if (!EncryptService::hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
if (!$this->hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
return false;
}
@ -174,9 +170,13 @@ class EncryptService {
* @returns string The encrypted data
*/
public function encrypt($data, $key) {
$salt = openssl_random_pseudo_bytes(128);
list ($cipherKey, $macKey, $iv) = EncryptService::getKeys($salt, $key);
$data = EncryptService::pad($data);
if (function_exists('random_bytes')) {
$salt = random_bytes(128);
} else {
$salt = openssl_random_pseudo_bytes(128);
}
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
$data = $this->pad($data);
$enc = openssl_encrypt($data, $this->cipher, $cipherKey, true, $iv);
$mac = hash_hmac('sha512', $enc, $macKey, true);
$data = bin2hex($salt . $enc . $mac);
@ -197,7 +197,7 @@ class EncryptService {
$keySize = openssl_cipher_iv_length($this->cipher);
$length = 2 * $keySize + $ivSize;
$key = EncryptService::pbkdf2('sha512', $key, $salt, $this->rounds, $length);
$key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);
$cipherKey = substr($key, 0, $keySize);
$macKey = substr($key, $keySize, $keySize);
@ -206,7 +206,11 @@ class EncryptService {
}
protected function hash_equals($a, $b) {
$key = openssl_random_pseudo_bytes(128);
if (function_exists('random_bytes')) {
$key = random_bytes(128);
} else {
$key = openssl_random_pseudo_bytes(128);
}
return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key);
}
@ -279,7 +283,7 @@ class EncryptService {
* @return Credential|array
*/
public function decryptCredential($credential) {
return $this->handleCredential($credential, 'decrypt');
return $this->handleCredential($credential, EncryptService::OP_DECRYPT);
}
/**
@ -290,12 +294,11 @@ class EncryptService {
* @throws \Exception
*/
public function encryptCredential($credential) {
return $this->handleCredential($credential, 'encrypt');
return $this->handleCredential($credential, EncryptService::OP_ENCRYPT);
}
private function extractKeysFromCredential($credential){
private function extractKeysFromCredential($credential) {
$userKey = '';
$userSuppliedKey = '';
if ($credential instanceof Credential) {
@ -317,12 +320,10 @@ class EncryptService {
* @return Credential|array
* @throws \Exception
*/
private function handleCredential($credential, $op) {
$service_function = ($op === 'encrypt') ? 'encrypt' : 'decrypt';
private function handleCredential($credential, $service_function) {
list($userKey, $userSuppliedKey) = $this->extractKeysFromCredential($credential);
$key = EncryptService::makeKey($userKey, $this->server_key, $userSuppliedKey);
$key = $this->makeKey($userKey, $this->server_key, $userSuppliedKey);
foreach ($this->encrypted_credential_fields as $field) {
if ($credential instanceof Credential) {
$field = str_replace(' ', '', str_replace('_', ' ', ucwords($field, '_')));
@ -346,7 +347,7 @@ class EncryptService {
*/
public function encryptFile($file) {
return $this->handleFile($file, 'encrypt');
return $this->handleFile($file, EncryptService::OP_ENCRYPT);
}
/**
@ -357,7 +358,7 @@ class EncryptService {
*/
public function decryptFile($file) {
return $this->handleFile($file, 'decrypt');
return $this->handleFile($file, EncryptService::OP_DECRYPT);
}
/**
@ -367,12 +368,11 @@ class EncryptService {
* @return File|array
* @throws \Exception
*/
private function handleFile($file, $op) {
$service_function = ($op === 'encrypt') ? 'encrypt' : 'decrypt';
private function handleFile($file, $service_function) {
$userKey = '';
$userSuppliedKey = '';
if ($file instanceof File) {
$userSuppliedKey = $file->getSize();
$userSuppliedKey = $file->getFilename();
$userKey = md5($file->getMimetype());
}
@ -381,7 +381,7 @@ class EncryptService {
$userKey = md5($file['mimetype']);
}
$key = EncryptService::makeKey($userKey, $this->server_key, $userSuppliedKey);
$key = $this->makeKey($userKey, $this->server_key, $userSuppliedKey);
if ($file instanceof File) {

View file

@ -55,6 +55,7 @@ class SettingsService {
'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'),
'rounds_pbkdf2_stretching' => $this->config->getAppValue('passman', 'rounds_pbkdf2_stretching', 100),
'settings_loaded' => 1
);
}

View file

@ -41,7 +41,6 @@ class ShareService {
private $revisions;
private $encryptService;
private $server_key;
public function __construct(
SharingACLMapper $sharingACL,
@ -55,7 +54,6 @@ class ShareService {
$this->credential = $credentials;
$this->revisions = $revisions;
$this->encryptService = $encryptService;
$this->server_key = \OC::$server->getConfig()->getSystemValue('passwordsalt', '');
}
/**

View file

@ -0,0 +1,59 @@
<?php
/**
* Nextcloud - passman
*
* @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Passman\Controller;
use OCA\Passman\Service\EncryptService;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use PHPUnit_Framework_TestCase;
use OCA\Passman\Service\SettingsService;
/**
* Class SettingsServiceTest
*
* @package OCA\Passman\Controller
* @coversDefaultClass \OCA\Passman\Service\EncryptService
*/
class EncryptServiceTest extends PHPUnit_Framework_TestCase {
private $service;
private $testKey;
public function setUp() {
$config = $this->getMockBuilder('OCP\IConfig')->getMock();
$userId = 'admin';
$settings_service = new SettingsService($userId, $config, 'passman');
$this->service = new EncryptService($settings_service);
}
/**
* @covers ::testMakeKey
*/
public function testMakeKey() {
$this->testKey = $this->service->makeKey('userKey', 'serverKey', 'userSuppliedKey');
$this->assertTrue($this->testKey === '967efb38599fb81ebc95b280e7c86cda0593e469f6a391caf9e9fee7c3976fd1edcdeefdb6a99e9f0bc47fda4b77fb8309c1955211dccf1dab1aad00c2ad5656');
}
}