Skip to content

Commit

Permalink
Merge pull request #164 from Automattic/vip/deprecated-mcrypto
Browse files Browse the repository at this point in the history
  • Loading branch information
GaryJones committed Feb 11, 2024
2 parents 27a9280 + 9a3434d commit 179d2a6
Show file tree
Hide file tree
Showing 13 changed files with 548 additions and 21 deletions.
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -25,7 +25,7 @@
"phpunit/phpunit": "^4 || ^5 || ^6 || ^7",
"squizlabs/php_codesniffer": "^3.5",
"wp-coding-standards/wpcs": "^2.3.0",
"yoast/phpunit-polyfills": "^0.2.0"
"yoast/wp-test-utils": "^0.2.2"
},
"scripts": {
"cbf": [
Expand Down
47 changes: 47 additions & 0 deletions includes/class-syndication-encryption.php
@@ -0,0 +1,47 @@
<?php

/**
* Class Syndication_Encryption
*/
class Syndication_Encryption {

/**
* Stores the current Syndication_Encryptor, used for the encryption/decryption operations.
*
* @var Syndication_Encryptor
*/
private $encryptor;

/**
* Syndication_Encryption constructor.
*
* @param Syndication_Encryptor $encryptor Encryptor to be used.
*/
public function __construct( Syndication_Encryptor $encryptor ) {
$this->encryptor = $encryptor;
}

/**
* Given $data, encrypt it using a Syndication_Encryptor and return the encrypted string.
*
* @param string|array|object $data the data to be encrypted.
*
* @return false|string
*/
public function encrypt( $data ) {
return $this->encryptor->encrypt( $data );
}

/**
* Decrypts an encrypted $data using a Syndication_Encryptor, and returns the decrypted object.
*
* @param string $data The encrypted data.
* @param bool $associative If true, returns as an associative array. Otherwise returns as a class.
*
* @return false|array|object
*/
public function decrypt( $data, $associative = true ) {
return $this->encryptor->decrypt( $data, $associative );
}

}
36 changes: 36 additions & 0 deletions includes/class-syndication-encryptor-mcrypt.php
@@ -0,0 +1,36 @@
<?php

/**
* Class Syndication_Encryptor_MCrypt
*/
class Syndication_Encryptor_MCrypt implements Syndication_Encryptor {

/**
* @inheritDoc
*/
public function encrypt( $data ) {
$data = serialize( $data ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
// phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.mcrypt_encryptDeprecatedRemoved,PHPCompatibility.Extensions.RemovedExtensions.mcryptDeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_rijndael_256DeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_mode_cbcDeprecatedRemoved
return base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_256, md5( PUSH_SYNDICATE_KEY ), $data, MCRYPT_MODE_CBC, md5( md5( PUSH_SYNDICATE_KEY ) ) ) );
}

/**
* @inheritDoc
*/
public function decrypt( $data, $associative = true ) {
// phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.mcrypt_decryptDeprecatedRemoved,PHPCompatibility.Extensions.RemovedExtensions.mcryptDeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_rijndael_256DeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_mode_cbcDeprecatedRemoved
$data = rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_256, md5( PUSH_SYNDICATE_KEY ), base64_decode( $data ), MCRYPT_MODE_CBC, md5( md5( PUSH_SYNDICATE_KEY ) ) ), "\0" );
if ( ! $data ) {
return false;
}
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize,WordPress.PHP.NoSilencedErrors.Discouraged
return @unserialize( $data );
}

/**
* @inheritDoc
*/
public function get_cipher() {
return MCRYPT_RIJNDAEL_256; // phpcs:ignore PHPCompatibility.Constants.RemovedConstants.mcrypt_rijndael_256DeprecatedRemoved
}
}
63 changes: 63 additions & 0 deletions includes/class-syndication-encryptor-openssl.php
@@ -0,0 +1,63 @@
<?php

/**
* Class Syndication_Encryptor_OpenSSL
*/
class Syndication_Encryptor_OpenSSL implements Syndication_Encryptor {

/**
* The cipher to be used for encryption.
*
* @var string
*/
private $cipher = 'aes-256-cbc';

/**
* @inheritDoc
*/
public function encrypt( $data ) {
$data = wp_json_encode( $data );
$cipher = $this->get_cipher();

if ( ! $cipher ) {
return $data;
}

$encrypted_data = openssl_encrypt( $data, $cipher['cipher'], $cipher['key'], 0, $cipher['iv'] );
return base64_encode( $encrypted_data ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
}

/**
* @inheritDoc
*/
public function decrypt( $data, $associative = true ) {
$cipher = $this->get_cipher();

if ( ! $cipher ) {
return $data;
}

$data = openssl_decrypt( base64_decode( $data ), $cipher['cipher'], $cipher['key'], 0, $cipher['iv'] ); //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode

if ( ! $data ) {
return false;
}

return json_decode( $data, $associative );
}

/**
* @inheritDoc
*/
public function get_cipher() {
if ( in_array( $this->cipher, openssl_get_cipher_methods(), true ) ) {
return array(
'cipher' => $this->cipher,
'iv' => substr( md5( md5( PUSH_SYNDICATE_KEY ) ), 0, 16 ),
'key' => md5( PUSH_SYNDICATE_KEY ),
);
}

return false; // @TODO: return another default cipher? return exception?
}
}
2 changes: 1 addition & 1 deletion includes/class-wp-push-syndication-server.php
Expand Up @@ -1225,7 +1225,7 @@ public function cron_add_pull_time_interval( $schedules ) {

// Adds the custom time interval to the existing schedules.
$schedules['syn_pull_time_interval'] = array(
'interval' => intval( $this->push_syndicate_settings['pull_time_interval'] ),
'interval' => isset( $this->push_syndicate_settings ) ? intval( $this->push_syndicate_settings['pull_time_interval'] ) : 0,
'display' => __( 'Pull Time Interval', 'push-syndication' )
);

Expand Down
33 changes: 33 additions & 0 deletions includes/interface-syndication-encryptor.php
@@ -0,0 +1,33 @@
<?php

/**
* Interface Syndication_Encryptor
*/
interface Syndication_Encryptor {

/**
* Encrypts data.
*
* @param string|array $data Data to be encrypted.
*
* @return string
*/
public function encrypt( $data );

/**
* Decrypts data
*
* @param string $data Data to be decrypted.
* @param bool $associative If true, returns as an associative array. Otherwise returns as a class.
*
* @return mixed
*/
public function decrypt( $data, $associative = true );

/**
* Returns the cipher being used. It can be a string, or a array with the cipher, key and iv.
*
* @return string|array
*/
public function get_cipher();
}
34 changes: 21 additions & 13 deletions includes/push-syndicate-encryption.php
@@ -1,18 +1,26 @@
<?php

/**
* Encrypts data.
*
* @param string $data The data to encrypt.
*
* @return false|string
*/
function push_syndicate_encrypt( $data ) {

$data = serialize( $data );
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5(PUSH_SYNDICATE_KEY), $data, MCRYPT_MODE_CBC, md5(md5(PUSH_SYNDICATE_KEY))));

global $push_syndication_encryption; // @todo: move from global to WP_Push_Syndication_Server attribute
return $push_syndication_encryption->encrypt( $data );
}

function push_syndicate_decrypt( $data ) {

$data = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5(PUSH_SYNDICATE_KEY), base64_decode($data), MCRYPT_MODE_CBC, md5(md5(PUSH_SYNDICATE_KEY))), "\0");
if ( !$data )
return false;

return @unserialize( $data );

}
/**
* Decrypts data.
*
* @param string $data The encrypted data to decrypt.
* @param bool $associative If true, returns as an associative array. Otherwise returns as a class.
*
* @return array|false|object
*/
function push_syndicate_decrypt( $data, $associative = true ) {
global $push_syndication_encryption; // @todo: move from global to WP_Push_Syndication_Server attribute
return $push_syndication_encryption->decrypt( $data, $associative );
}
31 changes: 25 additions & 6 deletions push-syndication.php
Expand Up @@ -11,21 +11,23 @@

define( 'SYNDICATION_VERSION', 2.0 );

if ( ! defined( 'PUSH_SYNDICATE_KEY' ) )
if ( ! defined( 'PUSH_SYNDICATE_KEY' ) ) {
define( 'PUSH_SYNDICATE_KEY', 'PUSH_SYNDICATE_KEY' );
}

/**
* Load syndication logger
*/
require_once( dirname( __FILE__ ) . '/includes/class-syndication-logger.php' );
require_once dirname( __FILE__ ) . '/includes/class-syndication-logger.php';
Syndication_Logger::init();

require_once( dirname( __FILE__ ) . '/includes/class-wp-push-syndication-server.php' );
require_once dirname( __FILE__ ) . '/includes/class-wp-push-syndication-server.php';

if ( defined( 'WP_CLI' ) && WP_CLI )
require_once( dirname( __FILE__ ) . '/includes/class-wp-cli.php' );
if ( defined( 'WP_CLI' ) && WP_CLI ) {
require_once dirname( __FILE__ ) . '/includes/class-wp-cli.php';
}

$GLOBALS['push_syndication_server'] = new WP_Push_Syndication_Server;
$GLOBALS['push_syndication_server'] = new WP_Push_Syndication_Server();

// Create the event counter.
require __DIR__ . '/includes/class-syndication-event-counter.php';
Expand All @@ -38,3 +40,20 @@
// Create the site auto retry functionality
require __DIR__ . '/includes/class-syndication-site-auto-retry.php';
new Failed_Syndication_Auto_Retry();

// Load encryption classes.
require_once dirname( __FILE__ ) . '/includes/class-syndication-encryption.php';
require_once dirname( __FILE__ ) . '/includes/interface-syndication-encryptor.php';
require_once dirname( __FILE__ ) . '/includes/class-syndication-encryptor-mcrypt.php';
require_once dirname( __FILE__ ) . '/includes/class-syndication-encryptor-openssl.php';

// On PHP 7.1 mcrypt is available, but will throw a deprecated error if its used. Therefore, checking for the
// PHP version, instead of checking for mcrypt is a better approach.
if ( ! defined( 'PHP_VERSION_ID' ) || PHP_VERSION_ID < 70100 ) {
$syndication_encryption = new Syndication_Encryption( new Syndication_Encryptor_MCrypt() );
} else {
$syndication_encryption = new Syndication_Encryption( new Syndication_Encryptor_OpenSSL() );
}

// @TODO: instead of saving this as a global, have it as an attribute of WP_Push_Syndication_Server
$GLOBALS['push_syndication_encryption'] = $syndication_encryption;
14 changes: 14 additions & 0 deletions tests/bootstrap.php
@@ -1,9 +1,12 @@
<?php
use Yoast\WPTestUtils\WPIntegration;

$_tests_dir = getenv( 'WP_TESTS_DIR' );
if ( ! $_tests_dir ) {
$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib';
putenv( 'WP_TESTS_DIR=' . $_tests_dir );
}

if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) {
echo "Could not find $_tests_dir/includes/functions.php, have you run bin/install-wp-tests.sh ?" . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
exit( 1 );
Expand All @@ -18,3 +21,14 @@ function _manually_load_plugin() {

require $_tests_dir . '/includes/bootstrap.php';

/*
* Load WordPress, which will load the Composer autoload file, and load the MockObject autoloader after that.
*/
require_once dirname( __DIR__ ) . '/vendor/yoast/wp-test-utils/src/WPIntegration/bootstrap-functions.php';
WPIntegration\bootstrap_it();

/*
* Load tests dependencies
*/

require_once dirname( __FILE__ ) . '/class-encryptor-test-case.php';

0 comments on commit 179d2a6

Please sign in to comment.