Skip to content
This repository was archived by the owner on Apr 29, 2019. It is now read-only.

Commit 1131a21

Browse files
authored
Merge pull request #3072 from magento-earl-grey/ENGCOM-2088
[earl] ENGCOM-2088: Sodium Encryption #135
2 parents 716dca6 + 3e16009 commit 1131a21

File tree

25 files changed

+1321
-150
lines changed

25 files changed

+1321
-150
lines changed

app/code/Magento/Config/Model/Config/Structure.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ protected function _createEmptyElement(array $pathParts)
281281
public function getFieldPathsByAttribute($attributeName, $attributeValue)
282282
{
283283
$result = [];
284+
if (empty($this->_data['sections'])) {
285+
return $result;
286+
}
287+
284288
foreach ($this->_data['sections'] as $section) {
285289
if (!isset($section['children'])) {
286290
continue;
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\EncryptionKey\Setup\Patch\Data;
9+
10+
use Magento\Framework\Setup\Patch\DataPatchInterface;
11+
12+
/**
13+
* Migrate encrypted configuration values to the latest cipher
14+
*/
15+
class SodiumChachaPatch implements DataPatchInterface
16+
{
17+
/**
18+
* @var \Magento\Framework\Setup\ModuleDataSetupInterface
19+
*/
20+
private $moduleDataSetup;
21+
22+
/**
23+
* @var \Magento\Config\Model\Config\Structure
24+
*/
25+
private $structure;
26+
27+
/**
28+
* @var \Magento\Framework\Encryption\EncryptorInterface
29+
*/
30+
private $encryptor;
31+
32+
/**
33+
* @var \Magento\Framework\App\State
34+
*/
35+
private $state;
36+
37+
/**
38+
* @param \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup
39+
* @param \Magento\Config\Model\Config\Structure\Proxy $structure
40+
* @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
41+
* @param \Magento\Framework\App\State $state
42+
*/
43+
public function __construct(
44+
\Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup,
45+
\Magento\Config\Model\Config\Structure\Proxy $structure,
46+
\Magento\Framework\Encryption\EncryptorInterface $encryptor,
47+
\Magento\Framework\App\State $state
48+
) {
49+
$this->moduleDataSetup = $moduleDataSetup;
50+
$this->structure = $structure;
51+
$this->encryptor = $encryptor;
52+
$this->state = $state;
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function apply()
59+
{
60+
$this->moduleDataSetup->startSetup();
61+
62+
$this->reEncryptSystemConfigurationValues();
63+
64+
$this->moduleDataSetup->endSetup();
65+
}
66+
67+
/**
68+
* {@inheritdoc}
69+
*/
70+
public static function getDependencies()
71+
{
72+
return [];
73+
}
74+
75+
/**
76+
* {@inheritdoc}
77+
*/
78+
public function getAliases()
79+
{
80+
return [];
81+
}
82+
83+
private function reEncryptSystemConfigurationValues()
84+
{
85+
$structure = $this->structure;
86+
$paths = $this->state->emulateAreaCode(
87+
\Magento\Framework\App\Area::AREA_ADMINHTML,
88+
function () use ($structure) {
89+
return $structure->getFieldPathsByAttribute(
90+
'backend_model',
91+
\Magento\Config\Model\Config\Backend\Encrypted::class
92+
);
93+
}
94+
);
95+
// walk through found data and re-encrypt it
96+
if ($paths) {
97+
$table = $this->moduleDataSetup->getTable('core_config_data');
98+
$values = $this->moduleDataSetup->getConnection()->fetchPairs(
99+
$this->moduleDataSetup->getConnection()
100+
->select()
101+
->from($table, ['config_id', 'value'])
102+
->where('path IN (?)', $paths)
103+
->where('value NOT LIKE ?', '')
104+
);
105+
foreach ($values as $configId => $value) {
106+
$this->moduleDataSetup->getConnection()->update(
107+
$table,
108+
['value' => $this->encryptor->encrypt($this->encryptor->decrypt($value))],
109+
['config_id = ?' => (int)$configId]
110+
);
111+
}
112+
}
113+
}
114+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Sales\Console\Command;
10+
11+
use Symfony\Component\Console\Input\InputInterface;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
use Symfony\Component\Console\Command\Command;
14+
use Magento\Framework\Console\Cli;
15+
16+
/**
17+
* Command for updating encrypted credit card data to the latest cipher
18+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
19+
*/
20+
class EncryptionPaymentDataUpdateCommand extends Command
21+
{
22+
/** Command name */
23+
const NAME = 'encryption:payment-data:update';
24+
25+
/**
26+
* @var \Magento\Sales\Model\ResourceModel\Order\Payment\EncryptionUpdate
27+
*/
28+
private $paymentResource;
29+
30+
/**
31+
* @param \Magento\Sales\Model\ResourceModel\Order\Payment\EncryptionUpdate $paymentResource
32+
*/
33+
public function __construct(
34+
\Magento\Sales\Model\ResourceModel\Order\Payment\EncryptionUpdate $paymentResource
35+
) {
36+
$this->paymentResource = $paymentResource;
37+
parent::__construct();
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
protected function configure()
44+
{
45+
$this->setName(self::NAME)
46+
->setDescription(
47+
'Re-encrypts encrypted credit card data with latest encryption cipher.'
48+
);
49+
parent::configure();
50+
}
51+
52+
/**
53+
* {@inheritdoc}
54+
*/
55+
protected function execute(InputInterface $input, OutputInterface $output)
56+
{
57+
try {
58+
$this->paymentResource->reEncryptCreditCardNumbers();
59+
} catch (\Exception $e) {
60+
$output->writeln('<error>' . $e->getMessage() . '</error>');
61+
return Cli::RETURN_FAILURE;
62+
}
63+
64+
return Cli::RETURN_SUCCESS;
65+
}
66+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Sales\Model\ResourceModel\Order\Payment;
10+
11+
/**
12+
* Resource for updating encrypted credit card data to the latest cipher
13+
*/
14+
class EncryptionUpdate
15+
{
16+
const LEGACY_PATTERN = '^[[:digit:]]+:[^%s]:.*$';
17+
18+
/**
19+
* @var \Magento\Sales\Model\ResourceModel\Order\Payment
20+
*/
21+
private $paymentResource;
22+
23+
/**
24+
* @var \Magento\Framework\Encryption\Encryptor
25+
*/
26+
private $encryptor;
27+
28+
/**
29+
* @param \Magento\Sales\Model\ResourceModel\Order\Payment $paymentResource
30+
* @param \Magento\Framework\Encryption\Encryptor $encryptor
31+
*/
32+
public function __construct(
33+
\Magento\Sales\Model\ResourceModel\Order\Payment $paymentResource,
34+
\Magento\Framework\Encryption\Encryptor $encryptor
35+
) {
36+
$this->paymentResource = $paymentResource;
37+
$this->encryptor = $encryptor;
38+
}
39+
40+
/**
41+
* Fetch encrypted credit card numbers using legacy ciphers and re-encrypt with latest cipher
42+
* @throws \Magento\Framework\Exception\LocalizedException
43+
*/
44+
public function reEncryptCreditCardNumbers()
45+
{
46+
$connection = $this->paymentResource->getConnection();
47+
$table = $this->paymentResource->getMainTable();
48+
$select = $connection->select()->from($table, ['entity_id', 'cc_number_enc'])
49+
->where(
50+
'cc_number_enc REGEXP ?',
51+
sprintf(self::LEGACY_PATTERN, \Magento\Framework\Encryption\Encryptor::CIPHER_LATEST)
52+
)->limit(1000);
53+
54+
while ($attributeValues = $connection->fetchPairs($select)) {
55+
// save new values
56+
foreach ($attributeValues as $valueId => $value) {
57+
$connection->update(
58+
$table,
59+
['cc_number_enc' => $this->encryptor->encrypt($this->encryptor->decrypt($value))],
60+
['entity_id = ?' => (int)$valueId, 'cc_number_enc = ?' => (string)$value]
61+
);
62+
}
63+
}
64+
}
65+
}

app/code/Magento/Sales/etc/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,13 @@
980980
<type name="Magento\Sales\Model\ResourceModel\Order\Handler\Address">
981981
<plugin name="addressUpdate" type="Magento\Sales\Model\Order\Invoice\Plugin\AddressUpdate"/>
982982
</type>
983+
<type name="Magento\Framework\Console\CommandListInterface">
984+
<arguments>
985+
<argument name="commands" xsi:type="array">
986+
<item name="encyption_payment_data_update" xsi:type="object">Magento\Sales\Console\Command\EncryptionPaymentDataUpdateCommand</item>
987+
</argument>
988+
</arguments>
989+
</type>
983990
<type name="Magento\Config\Model\Config\TypePool">
984991
<arguments>
985992
<argument name="sensitive" xsi:type="array">

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"magento/zendframework1": "~1.14.1",
4242
"monolog/monolog": "^1.17",
4343
"oyejorge/less.php": "~1.7.0",
44+
"paragonie/sodium_compat": "^1.6",
4445
"pelago/emogrifier": "^2.0.0",
4546
"php-amqplib/php-amqplib": "~2.7.0",
4647
"phpseclib/mcrypt_compat": "1.0.5",

0 commit comments

Comments
 (0)