Skip to content

Commit

Permalink
Use Openssl rather than MCrypt to encrypt scoped keys
Browse files Browse the repository at this point in the history
MCrypt is deprecated as of PHP 7.1.
  • Loading branch information
stof committed Jul 29, 2017
1 parent 238d37b commit c8bc751
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 14 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
language: php
php:
- "7.1"
- "7.0"
- "5.6"
before_script:
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
},
"require": {
"php": ">=5.6.0",
"ext-mcrypt": "*",
"ext-openssl": "*",
"guzzlehttp/guzzle": "~6.0",
"guzzlehttp/guzzle-services": "~1.0"
"guzzlehttp/guzzle-services": "~1.0",
"paragonie/random_compat": "^1.4 || ^2.0"
},
"require-dev": {
"phpunit/phpunit": "5.3.*"
Expand Down
42 changes: 30 additions & 12 deletions src/Client/KeenIOClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,10 @@ public function createScopedKey($filters, $allowedOperations, $source = MCRYPT_D
}

$apiKey = pack('H*', $masterKey);
$blockSize = 16;

$opensslOptions = \OPENSSL_RAW_DATA;

$optionsJson = json_encode($options);

/**
* Use the old block size and hex string input if using a legacy master key.
Expand All @@ -322,15 +325,18 @@ public function createScopedKey($filters, $allowedOperations, $source = MCRYPT_D

if (strlen($masterKey) == 32) {
$apiKey = $masterKey;
$blockSize = 32;

// Openssl's built-in PKCS7 padding won't use the 32 bytes block size, so apply it in userland and use OPENSSL zero padding (no-op as already padded)
$opensslOptions |= \OPENSSL_ZERO_PADDING;
$optionsJson = $this->padString($optionsJson, 32);
}

$optionsJson = $this->padString(json_encode($options), $blockSize);
$cipher = 'AES-256-CBC';

$ivLength = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivLength, $source);
$ivLength = openssl_cipher_iv_length($cipher);
$iv = random_bytes($ivLength);

$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $apiKey, $optionsJson, MCRYPT_MODE_CBC, $iv);
$encrypted = openssl_encrypt($optionsJson, $cipher, $apiKey, $opensslOptions, $iv);

$ivHex = bin2hex($iv);
$encryptedHex = bin2hex($encrypted);
Expand Down Expand Up @@ -371,25 +377,37 @@ public function decryptScopedKey($scopedKey)

$apiKey = pack('H*', $masterKey);

$opensslOptions = \OPENSSL_RAW_DATA;
$paddedManually = false;

// Use the old hex string input if using a legacy master key
if (strlen($masterKey) == 32) {
$apiKey = $masterKey;
// Openssl's built-in PKCS7 padding won't use the 32 bytes block size, so apply it in userland and use OPENSSL zero padding (no-op as already padded)
$opensslOptions |= \OPENSSL_ZERO_PADDING;
$paddedManually = true;
}

$ivLength = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC) * 2;
$cipher = 'AES-256-CBC';

$ivLength = openssl_cipher_iv_length($cipher) * 2;
$ivHex = substr($scopedKey, 0, $ivLength);

$encryptedHex = substr($scopedKey, $ivLength);

$resultPadded = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$apiKey,
$result = openssl_decrypt(
pack('H*', $encryptedHex),
MCRYPT_MODE_CBC,
$cipher,
$apiKey,
$opensslOptions,
pack('H*', $ivHex)
);

return json_decode($this->unpadString($resultPadded), true);
if ($paddedManually) {
$result = $this->unpadString($result);
}

return json_decode($result, true);
}

/**
Expand Down
38 changes: 38 additions & 0 deletions tests/Tests/Client/KeenIOClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,25 @@ public function testCreateLegacyScopedKey()
$this->assertEquals($expected, $result);
}

/**
* Tests the decryption of a Scoped Key
*/
public function testDecryptLegacyScopedKey()
{
$client = KeenIOClient::factory(array(
'masterKey' => 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
));

$filter = array('property_name' => 'id', 'operator' => 'eq', 'property_value' => '123');
$filters = array($filter);
$allowed_operations = array('read');

$result = $client->decryptScopedKey('696a2c05b060e6ed344f7e570c101e0909ff97c338dfe0f1e15c0e5c1ec0621dfcd5577f3ae58596558eed2a43c82d3a062fbf6455810f5c859695c766caddd283398e577b24db014fad896a6f0447a2aad9dad43cef5fa040e8f6d366085423804633ef3b21535b31d11eec24631f83c18e83703247f40136aeba779a840e80013e0969a8cf203295f47da1d70bfeb3');
$expected = array('filters' => $filters, 'allowed_operations' => $allowed_operations);

$this->assertEquals($expected, $result);
}

/**
* Tests the creation of a Scoped Key
*/
Expand All @@ -218,6 +237,25 @@ public function testCreateScopedKey()
$this->assertEquals($expected, $result);
}

/**
* Tests the decryption of a Scoped Key
*/
public function testDecryptScopedKey()
{
$client = KeenIOClient::factory(array(
'masterKey' => 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
));

$filter = array('property_name' => 'id', 'operator' => 'eq', 'property_value' => '123');
$filters = array($filter);
$allowed_operations = array('read');

$result = $client->decryptScopedKey('903441674b0d433ddab759bba82502ec469e00d25c373e35c4d685488bc7779a5abd7d90a03a4cb744ee6a82fa8935804348a5b2351f6527cd5fd6a0613cea5ec4e848f5093e41a53d570cf01066b1f3c3e9b03d4ce0929ff3e6a06e1850fb9e09b65415ac754bbefe9db4b1fcba7d71a9f5f9d9c05cbeffb2a33ef5f4bac131');
$expected = array('filters' => $filters, 'allowed_operations' => $allowed_operations);

$this->assertEquals($expected, $result);
}

/**
* @dataProvider providerServiceCommands
*/
Expand Down

0 comments on commit c8bc751

Please sign in to comment.