Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
psrpinto committed Jan 15, 2017
1 parent 75f1012 commit cb87a6e
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 26 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- `JMS\Payment\CoreBundle\Cryptography\MCryptEncryptionService` has been deprecated and will be removed in 2.0 (`mcrypt` has been deprecated in PHP 7.1 and is removed in PHP 7.2). Refer to http://jmspaymentcorebundle.readthedocs.io/en/latest/mcrypt.html for instructions on how to migrate away from `mcrypt`.

### Added
- Added support for custom crypto providers.
- Added support for custom encryption providers.
- Added support for data encryption with [defuse/php-encryption](https://github.com/defuse/php-encryption).
- Added ability to configure which crypto provider should be used. Current available options are `mcrypt` (not recommended since it will be removed in PHP 7.2) and `defuse_php_encryption`.
- Added ability to configure which encryption provider should be used. Current available options are `mcrypt` (not recommended since it will be removed in PHP 7.2) and `defuse_php_encryption`.

### Removed
- Removed support for PHP 5.3. If you're still using PHP 5.3, please consider upgrading since it reached End Of Life in August 2014. Otherwise, use `1.2.*`.
Expand Down
139 changes: 139 additions & 0 deletions Command/ReencryptDataCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

namespace JMS\Payment\CoreBundle\Command;

use Doctrine\ORM\Tools\Pagination\Paginator;
use JMS\Payment\CoreBundle\Cryptography\DefusePhpEncryptionService;
use JMS\Payment\CoreBundle\Cryptography\MCryptEncryptionService;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class ReencryptDataCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('jms_payment_core:reencrypt-data')
->setDescription('Re-encrypt encrypted database data')
->addArgument(
'src',
InputArgument::REQUIRED,
'The cryptography provider with which data currently in the database was encrypted.
Possible values are mcrypt and defuse_php_encryption'
)
->addArgument(
'src-secret',
InputArgument::REQUIRED,
'The current encryption key'
)
->addArgument(
'dest',
InputArgument::REQUIRED,
'The new cryptography provider to use for encrypting data.
Possible values are mcrypt and defuse_php_encryption'
)
->addArgument(
'dest-secret',
InputArgument::REQUIRED,
'The new encryption key'
)
->addOption(
'src-mcrypt-cipher',
null,
InputOption::VALUE_OPTIONAL,
'The mcrypt cipher for the src provider',
'rijndael-256'
)
->addOption(
'src-mcrypt-mode',
null,
InputOption::VALUE_OPTIONAL,
'The mcrypt mode for the src provider',
'ctr'
)
->addOption(
'dest-mcrypt-cipher',
null,
InputOption::VALUE_OPTIONAL,
'The mcrypt cipher for the dest provider',
'rijndael-256'
)
->addOption(
'dest-mcrypt-mode',
null,
InputOption::VALUE_OPTIONAL,
'The mcrypt mode for the dest provider',
'ctr'
)
->addOption(
'em',
null,
InputOption::VALUE_OPTIONAL,
'The entity manager to use',
'default'
);
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$providers = $this->getProviders($input);

$em = $this->getContainer()->get('doctrine')->getManager($input->getOption('em'));

$query = $em->createQuery('SELECT pi from JMSPaymentCoreBundle:PaymentInstruction pi')
->setFirstResult(0)
->setMaxResults(128);

$paginator = new Paginator($query, $fetchJoinCollection = false);

foreach ($paginator as $pi) {
var_dump($pi->getExtendedData());
}
}

private function getProviders(InputInterface $input)
{
$supportedProviders = array(
'mcrypt' => MCryptEncryptionService::class,
'defuse_php_encryption' => DefusePhpEncryptionService::class,
);

foreach ([$input->getArgument('src'), $input->getArgument('dest')] as $provider) {
if (!array_key_exists($provider, $supportedProviders)) {
throw new \InvalidArgumentException("Unsupported cryptography provider: $provider");
}
}

$providers = array();

foreach (array('src', 'dest') as $providerType) {
foreach (($options = $input->getOptions()) as $key => $value) {
$options[str_replace("$providerType-", '', $key)] = $value;
}

foreach ($supportedProviders as $name => $class) {
if ($name !== $input->getArgument($providerType)) {
continue;
}

switch ($input->getArgument($providerType)) {
case 'mcrypt':
$providers[$providerType] = new MCryptEncryptionService(
$input->getArgument("$providerType-secret"),
$options['mcrypt-cipher'],
$options['mcrypt-mode']
);
break;
case 'defuse_php_encryption':
$providers[$providerType] = new DefusePhpEncryptionService($secret);
break;
}
}
}

return $providers;
}
}
16 changes: 14 additions & 2 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,21 @@ public function getConfigTree()
return $tb
->root('jms_payment_core', 'array')
->children()
->scalarNode('crypto')->defaultValue('mcrypt')->end()
->scalarNode('secret')->isRequired()->cannotBeEmpty()->end()
->scalarNode('secret')
->info('Deprecated')
->end()
->arrayNode('encryption')
->children()
->booleanNode('enabled')->end()
->scalarNode('provider')->defaultValue('mcrypt')->end()
->scalarNode('secret')->isRequired()->cannotBeEmpty()->end()
->end()
->end()
->end()
// ->children()
// ->scalarNode('secret')->isRequired()->cannotBeEmpty()->end()
// ->scalarNode('crypto')->defaultValue('mcrypt')->end()
// ->end()
->end()
->buildTree();
}
Expand Down
7 changes: 7 additions & 0 deletions Resources/doc/data_encryption.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Data Encryption
===============

- What is encrypted
- Usage (form)
- Crypto providers (including custom, Defuse vs mcrypt and BC)
- Migrating from mcrypt to defuse
1 change: 1 addition & 0 deletions Resources/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ License
setup
payment_form
events
data_encryption
plugins
model
backends
Expand Down
Empty file added Resources/doc/mcrypt.rst
Empty file.
37 changes: 15 additions & 22 deletions Resources/doc/setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,33 @@ And register the bundle in your ``AppKernel.php``:
Configuration
-------------
The configuration is as simple as setting a random secret which will be used for encrypting data, in case this is requested.
The configuration is as simple as setting a random secret which will be used for encrypting data. For more information on how this bundle uses encryption see :doc:`data_encryption`.

You can generate the secret with the following command:
You can generate a secret with the following command:

.. code-block :: bash
# Feel free to increase the length of the generated string by
# passing a larger number to openssl_random_pseudo_bytes
php -r 'echo bin2hex(openssl_random_pseudo_bytes(16))."\n";'
php -r '
require "vendor/autoload.php";
echo (\Defuse\Crypto\Key::createNewRandomKey())->saveToAsciiSafeString()."\n";
'
And then use it in your configuration:

.. configuration-block ::
.. code-block :: yaml
# app/config/config.yml
jms_payment_core:
secret: yoursecret
.. code-block :: xml
.. code-block :: yaml
<!-- app/config/config.xml -->
<jms-payment-core secret="yoursecret" />
# app/config/config.yml
jms_payment_core:
crypto: defuse_php_encryption
secret: output_of_above_command
.. note ::
.. warning ::
If you change the secret, all data encrypted with the old secret will become unreadable.
If you change the ``secret`` or the ``crypto`` provider, all encrypted data will become unreadable.
Create database tables
----------------------
This bundle requires a few database tables to function. You can create these tables as follows.
This bundle requires a few database tables, which you can create as follows.

If you're not using database migrations:

Expand All @@ -71,14 +66,13 @@ Or, if you're using migrations:
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate
.. warning ::
.. note ::
It's assumed you have entity auto mapping enabled, which is usually the case. If you don't, you need to either enable it:
.. code-block :: yaml
# app/config/config.yml
doctrine:
orm:
auto_mapping: true
Expand All @@ -88,7 +82,6 @@ Or, if you're using migrations:
.. code-block :: yaml
# app/config/config.yml
doctrine:
orm:
mappings:
Expand Down
128 changes: 128 additions & 0 deletions Tests/Functional/Command/ReencryptDataCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace JMS\Payment\CoreBundle\Tests\Command;

use JMS\Payment\CoreBundle\Command\ReencryptDataCommand;
use JMS\Payment\CoreBundle\Tests\Functional\BaseTestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;

class ReencryptDataCommandTest extends BaseTestCase
{
public function setUp()
{
self::bootKernel();

$application = new Application(self::$kernel);
$application->add(new ReencryptDataCommand());

$this->command = $application->find('jms_payment_core:reencrypt-data');

parent::setUp();
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage foo
*/
public function testUnsupportedSourceProvider()
{
$this->execute(array(
'src' => 'foo',
'src-secret' => 'foo-secret',
'dest' => 'defuse_php_encryption',
'dest-secret' => 'bar-secret',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage bar
*/
public function testUnsupportedDestProvider()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'dest' => 'bar',
'dest-secret' => 'bar-secret',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The cipher "foo" is not supported.
*/
public function testMcryptSrcCipher()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'--src-mcrypt-cipher' => 'foo',
'dest' => 'mcrypt',
'dest-secret' => 'bar-secret',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The cipher "bar" is not supported.
*/
public function testMcryptDestCipher()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'dest' => 'mcrypt',
'dest-secret' => 'bar-secret',
'--dest-mcrypt-cipher' => 'bar',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The mode "foo" is not supported.
*/
public function testMcryptSrcMode()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'--src-mcrypt-mode' => 'foo',
'dest' => 'mcrypt',
'dest-secret' => 'bar-secret',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The mode "bar" is not supported.
*/
public function testMcryptDestMode()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'dest' => 'mcrypt',
'dest-secret' => 'bar-secret',
'--dest-mcrypt-mode' => 'bar',
));
}

private function execute(array $input)
{
$commandTester = new CommandTester($this->command);

$commandTester->execute(array_merge(array(
'command' => $this->command->getName(),
), $input));

return $commandTester->getDisplay();
}
}
Loading

0 comments on commit cb87a6e

Please sign in to comment.