Skip to content

Commit

Permalink
Merge da75908 into d76fbc6
Browse files Browse the repository at this point in the history
  • Loading branch information
yceruto committed Sep 10, 2019
2 parents d76fbc6 + da75908 commit 4961a33
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 34 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ php:
- 5.5
- 5.6
- 7.0
- 7.1
- hhvm

install:
Expand All @@ -17,4 +18,4 @@ script:
- php vendor/bin/phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml

after_script:
- php vendor/bin/coveralls
- php vendor/bin/coveralls
47 changes: 14 additions & 33 deletions src/Crypto.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,10 @@
use Rafrsr\Crypto\Encryptor\MCryptEncryptor;
use Rafrsr\Crypto\Exception\AlgorithmNotSupportedException;

/**
* Class Crypto
*/
class Crypto
{
/**
* @var EncryptorInterface
*/
protected $encryptor;

/**
* BaseEncryptor constructor.
*/
public function __construct(EncryptorInterface $encryptor)
{
$this->encryptor = $encryptor;
Expand All @@ -38,41 +29,38 @@ public function __construct(EncryptorInterface $encryptor)
* Create a Crypto instance using a build in encryptor with given encryptor name
* for now support all most used MCRYPT_* algorithms
*
* @param string $secretKey Secret key used for encryption/decryption
* @param string $encryptor one of MCRYPT_* constants or class or instance implementing EncryptorInterface
* @param string $secretKey Secret key used for encryption/decryption
* @param string|EncryptorInterface $encryptor One of MCRYPT_* constants or class or instance implementing EncryptorInterface
*
* @return Crypto
* @throws AlgorithmNotSupportedException
*/
public static function build($secretKey, $encryptor = MCRYPT_RIJNDAEL_256)
public static function build($secretKey, $encryptor)
{
if (null === $encryptor && \defined('MCRYPT_RIJNDAEL_256')) {
$encryptor = MCRYPT_RIJNDAEL_256;
}

if (is_string($encryptor)) {
$algorithms = mcrypt_list_algorithms();
if (in_array($encryptor, $algorithms)) {
if (class_exists($encryptor)) {
$encryptor = new $encryptor($secretKey);
} elseif (\function_exists('mcrypt_list_algorithms') && \in_array($encryptor, mcrypt_list_algorithms(), true)) {
$encryptor = new MCryptEncryptor($secretKey, $encryptor);
} elseif (class_exists($encryptor)) {
$encryptor = new $encryptor;
}
}

return new Crypto($encryptor);
}

/**
* @inheritDoc
*/
public function encrypt($data)
{
if (!$this->isEncrypted($data) && $data !== null && $data !== '') {
return base64_encode("<Crypto>" . $this->encryptor->encrypt($data));
if (null !== $data && '' !== $data && !$this->isEncrypted($data)) {
return base64_encode('<Crypto>'.$this->encryptor->encrypt($data));
}

return $data;
}

/**
* @inheritDoc
*/
public function decrypt($data)
{
if ($this->isEncrypted($data)) {
Expand All @@ -85,15 +73,8 @@ public function decrypt($data)
return $data;
}

/**
* @inheritDoc
*/
public function isEncrypted($data)
{
if (!empty($data) && substr(base64_decode($data), 0, 8) == '<Crypto>') {
return true;
}

return false;
return $data && 0 === strpos(base64_decode($data), '<Crypto>');
}
}
}
83 changes: 83 additions & 0 deletions src/Encryptor/SodiumEncryptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

/**
* This file is part of the Crypto package.
*
* (c) RafaelSR <https://github.com/rafrsr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Rafrsr\Crypto\Encryptor;

use Rafrsr\Crypto\EncryptorInterface;

class SodiumEncryptor implements EncryptorInterface
{
private $secretKey;

public function __construct($secretKey)
{
$this->secretKey = $secretKey;
}

/**
* {@inheritdoc}
*/
public function encrypt($value)
{
$nonce = random_bytes(SODIUM_CRYPTO_STREAM_NONCEBYTES);
list($encKey, $authKey) = $this->splitKeys();

$cipherText = sodium_crypto_stream_xor($value, $nonce, $encKey);
sodium_memzero($value);
$mac = sodium_crypto_auth($nonce.$cipherText, $authKey);
sodium_memzero($encKey);
sodium_memzero($authKey);

return sodium_bin2hex($mac.$nonce.$cipherText);
}

/**
* {@inheritdoc}
*/
public function decrypt($value)
{
$raw = sodium_hex2bin($value);
list($encKey, $authKey) = $this->splitKeys();

$mac = mb_substr($raw, 0, SODIUM_CRYPTO_AUTH_BYTES, '8bit');
$nonce = mb_substr($raw, SODIUM_CRYPTO_AUTH_BYTES, SODIUM_CRYPTO_STREAM_NONCEBYTES, '8bit');
$cipherText = mb_substr($raw, SODIUM_CRYPTO_AUTH_BYTES + SODIUM_CRYPTO_STREAM_NONCEBYTES, null, '8bit');

if (sodium_crypto_auth_verify($mac, $nonce.$cipherText, $authKey)) {
sodium_memzero($authKey);
$plaintext = sodium_crypto_stream_xor($cipherText, $nonce, $encKey);
sodium_memzero($encKey);
if ($plaintext !== false) {
return $plaintext;
}
} else {
sodium_memzero($authKey);
sodium_memzero($encKey);
}

throw new \RuntimeException('Decryption failed.');
}

/**
* Just an example. In a real system, you want to use HKDF for
* key-splitting instead of just a keyed BLAKE2b hash.
* @return array(2) [encryption key, authentication key]
*/
private function splitKeys()
{
$key = md5($this->secretKey);
$encKey = sodium_crypto_generichash(sodium_crypto_generichash('encryption', $key), $this->secretKey, SODIUM_CRYPTO_STREAM_KEYBYTES);
$authKey = sodium_crypto_generichash(sodium_crypto_generichash('authentication', $key), $this->secretKey, SODIUM_CRYPTO_AUTH_KEYBYTES);

return [$encKey, $authKey];
}
}
29 changes: 29 additions & 0 deletions tests/Encryptor/SodiumEncryptorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/**
* This file is part of the Crypto package.
*
* (c) RafaelSR <https://github.com/rafrsr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Rafrsr\Crypto\Tests\Encryptor;

use Rafrsr\Crypto\Encryptor\SodiumEncryptor;

class SodiumEncryptorTest extends \PHPUnit_Framework_TestCase
{
public function testEncryption()
{
$encryptor = new SodiumEncryptor('12345678901234567890123456');

$message = 'This is a secret message';
$encrypted = $encryptor->encrypt($message);
$this->assertNotEquals($message, $encrypted);

$decrypted = $encryptor->decrypt($encrypted);
$this->assertEquals($message, $decrypted);
}
}

0 comments on commit 4961a33

Please sign in to comment.