From cb87a6eb6e8a3a9aee9920b03672bb8d8b21bdd0 Mon Sep 17 00:00:00 2001 From: Paulo Rodrigues Pinto Date: Sun, 15 Jan 2017 15:55:41 +0000 Subject: [PATCH] WIP --- CHANGELOG.md | 4 +- Command/ReencryptDataCommand.php | 139 ++++++++++++++++++ DependencyInjection/Configuration.php | 16 +- Resources/doc/data_encryption.rst | 7 + Resources/doc/index.rst | 1 + Resources/doc/mcrypt.rst | 0 Resources/doc/setup.rst | 37 ++--- .../Command/ReencryptDataCommandTest.php | 128 ++++++++++++++++ Tests/Functional/config/config.yml | 3 + 9 files changed, 309 insertions(+), 26 deletions(-) create mode 100644 Command/ReencryptDataCommand.php create mode 100644 Resources/doc/data_encryption.rst create mode 100644 Resources/doc/mcrypt.rst create mode 100644 Tests/Functional/Command/ReencryptDataCommandTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 56b15719..7f20bd8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.*`. diff --git a/Command/ReencryptDataCommand.php b/Command/ReencryptDataCommand.php new file mode 100644 index 00000000..2312dc1e --- /dev/null +++ b/Command/ReencryptDataCommand.php @@ -0,0 +1,139 @@ +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; + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 6afcbb2f..efdd9cf2 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -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(); } diff --git a/Resources/doc/data_encryption.rst b/Resources/doc/data_encryption.rst new file mode 100644 index 00000000..c0fa067b --- /dev/null +++ b/Resources/doc/data_encryption.rst @@ -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 diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst index b97b4ea6..d9acd5f8 100644 --- a/Resources/doc/index.rst +++ b/Resources/doc/index.rst @@ -33,6 +33,7 @@ License setup payment_form events + data_encryption plugins model backends diff --git a/Resources/doc/mcrypt.rst b/Resources/doc/mcrypt.rst new file mode 100644 index 00000000..e69de29b diff --git a/Resources/doc/setup.rst b/Resources/doc/setup.rst index 037ec03d..64a004d4 100644 --- a/Resources/doc/setup.rst +++ b/Resources/doc/setup.rst @@ -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.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: @@ -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 @@ -88,7 +82,6 @@ Or, if you're using migrations: .. code-block :: yaml # app/config/config.yml - doctrine: orm: mappings: diff --git a/Tests/Functional/Command/ReencryptDataCommandTest.php b/Tests/Functional/Command/ReencryptDataCommandTest.php new file mode 100644 index 00000000..5161129f --- /dev/null +++ b/Tests/Functional/Command/ReencryptDataCommandTest.php @@ -0,0 +1,128 @@ +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(); + } +} diff --git a/Tests/Functional/config/config.yml b/Tests/Functional/config/config.yml index 12e9e710..5a34979f 100644 --- a/Tests/Functional/config/config.yml +++ b/Tests/Functional/config/config.yml @@ -3,6 +3,9 @@ imports: - { resource: framework.php } jms_payment_core: + # encryption: + # provider: defuse_php_encryption + # secret: def00000812bba10524777c97f1155877f0c91a4fac2c9f3d71a39d0df7214eab90d492faa58d2db667c5003c3c2228e3f19ad493ae86c74079a600a1ed51cd65e21f28e crypto: defuse_php_encryption secret: def00000812bba10524777c97f1155877f0c91a4fac2c9f3d71a39d0df7214eab90d492faa58d2db667c5003c3c2228e3f19ad493ae86c74079a600a1ed51cd65e21f28e