Skip to content

Commit

Permalink
Added a hybrid multi-layered cryptographic protocol implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
TonyKaravasilev committed Dec 23, 2022
1 parent 2935f91 commit 5bf9361
Show file tree
Hide file tree
Showing 9 changed files with 1,312 additions and 10 deletions.
14 changes: 14 additions & 0 deletions check.php
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,20 @@ function dump_an_error()
'red'
);

dump('List of raw OpenSSL errors:', 'red');

$count = 0;

while ($error = openssl_error_string()) {
dump($error, 'red');

$count++;
}

if ($count === 0) {
dump('No raw errors were returned from OpenSSL.', 'red');
}

dump_an_error();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

/**
* Interface for the layered encryption and decryption of data.
*/

namespace CryptoManana\Core\Interfaces\Containers;

use CryptoManana\DataStructures\EncryptionLayer;

/**
* Interface LayeredEncryptionInterface - Interface specification for layered encryption.
*
* @package CryptoManana\Core\Interfaces\Containers
*/
interface LayeredEncryptionInterface
{
/**
* Setter for the encryption layers' configuration.
*
* @param EncryptionLayer[]|array $layers Collection of layers.
*
* @throws \Exception Validation errors.
*/
public function setLayers(array $layers);

/**
* Add a single new layer at the last of the list.
*
* @param EncryptionLayer $layer The layer configuration.
*
* @throws \Exception Validation errors.
*/
public function addLayer(EncryptionLayer $layer);

/**
* Getter for the encryption layers' configuration.
*
* @return EncryptionLayer[]|array Collection of used layers' configuration.
*
* @throws \Exception Validation errors.
*/
public function getLayers();

/**
* Encrypts the given plain data multiple times with different algorithms as layers.
*
* @param string $plainData The plain input string.
* @param string $oneTimePad The optional one-time pad key.
*
* @return string The cipher/encrypted data.
* @throws \Exception Validation errors.
*/
public function layeredEncryptData($plainData, $oneTimePad = '');

/**
* Decrypts the given cipher data multiple times with different algorithms as layers.
*
* @param string $cipherData The encrypted input string.
* @param string $oneTimePad The optional one-time pad key.
*
* @return string The decrypted/plain data.
* @throws \Exception Validation errors.
*/
public function layeredDecryptData($cipherData, $oneTimePad = '');
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public function setPaddingStandard($padding);
/**
* Getter for the final block padding operation property.
*
* @return string The padding standard integer code value.
* @return int The padding standard integer code value.
*/
public function getPaddingStandard();
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public function setPaddingStandard($padding)
/**
* Getter for the final block padding operation property.
*
* @return string The padding standard integer code value.
* @return int The padding standard integer code value.
*/
public function getPaddingStandard()
{
Expand Down
246 changes: 246 additions & 0 deletions src/CryptoManana/CryptographicProtocol/LayeredEncryption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
<?php

/**
* Cryptographic protocol for multiple layered encryption.
*/

namespace CryptoManana\CryptographicProtocol;

use CryptoManana\Core\Abstractions\Containers\AbstractCryptographicProtocol as CryptographicProtocol;
use CryptoManana\Core\Abstractions\MessageEncryption\AbstractBlockCipherAlgorithm as SymmetricBlockCipher;
use CryptoManana\Core\Interfaces\Containers\LayeredEncryptionInterface as LayeredDataProcessing;
use CryptoManana\DataStructures\EncryptionLayer as LayerConfiguration;

/**
* Class LayeredEncryption - The multiple layered encryption protocol object.
*
* @package CryptoManana\CryptographicProtocol
*/
class LayeredEncryption extends CryptographicProtocol implements LayeredDataProcessing
{
/**
* The internal encryption cipher collection property storage.
*
* @var SymmetricBlockCipher[]
*/
protected $ciphers = [];

/**
* Setter for the encryption layers' configuration.
*
* @param LayerConfiguration[]|array $layers Collection of layers.
*
* @return $this The cryptographic protocol object.
* @throws \Exception Validation errors.
*/
public function setLayers(array $layers)
{
if (count($layers) < 2) {
throw new \RuntimeException('This protocol must have at least two layers to operate.');
}

foreach ($layers as $layer) {
if (!$layer instanceof LayerConfiguration) {
throw new \RuntimeException(
'All supplied configuration must be of the `EncryptionLayer` data structure.'
);
}

$this->addLayer($layer);
}

return $this;
}

/**
* Add a single new layer at the last of the list.
*
* @param LayerConfiguration $layer The layer configuration.
*
* @return $this The cryptographic protocol object.
* @throws \Exception Validation errors.
*/
public function addLayer(LayerConfiguration $layer)
{
if (class_exists($layer->cipher) && is_subclass_of($layer->cipher, SymmetricBlockCipher::class)) {
/** @var SymmetricBlockCipher $cipher */
$cipher = new $layer->cipher();

$cipher->setSecretKey($layer->key)
->setInitializationVector($layer->iv)
->setBlockOperationMode($layer->mode)
->setPaddingStandard($layer->padding)
->setCipherFormat($layer->format);

$this->ciphers[] = $cipher;
} else {
throw new \RuntimeException('All supplied ciphers must be existing and for symmetric encryption.');
}

return $this;
}

/**
* Getter for the encryption layers' configuration.
*
* @return LayerConfiguration[]|array Collection of used layers' configuration.
*
* @throws \Exception Validation errors.
*/
public function getLayers()
{
$layers = [];

foreach ($this->ciphers as $cipher) {
$layers [] = new LayerConfiguration(
get_class($cipher),
$cipher->getSecretKey(),
$cipher->getInitializationVector(),
$cipher->getBlockOperationMode(),
$cipher->getPaddingStandard(),
$cipher->getCipherFormat()
);
}

return $layers;
}


/**
* Container constructor.
*
* @param LayerConfiguration[]|array $configuration The layers' configuration.
*
* @throws \Exception Initialization validation.
*/
public function __construct(array $configuration = [])
{
$this->setLayers($configuration);
}

/**
* Container destructor.
*/
public function __destruct()
{
unset($this->ciphers);
}

/**
* Container cloning via deep copy.
*/
public function __clone()
{
$ciphers = [];

foreach ($this->ciphers as $cipher) {
$ciphers [] = clone $cipher;
}

$this->ciphers = $ciphers;
}

/**
* Calculates a XOR of two binary strings.
*
* @param string $stringOne The first binary string.
* @param string $stringTwo The second binary string.
*
* @return string The XOR output of both strings.
*/
protected function xorTwoStrings($stringOne, $stringTwo)
{
/**
* {@internal The encryption standard is 8-bit wise (do not use StringBuilder) and utilizes performance. }}
*/
if (strlen($stringTwo) < strlen($stringOne)) {
$stringTwo = str_pad($stringTwo, strlen($stringOne), "\x0", STR_PAD_RIGHT);
}

$dataLength = strlen($stringOne);
$keyLength = strlen($stringTwo);
$xorOutput = $stringOne;

for ($i = 0; $i < $dataLength; ++$i) {
$xorOutput[$i] = $stringOne[$i] ^ $stringTwo[$i % $keyLength];
}

return $xorOutput;
}

/**
* Internal method for the validation of the one-time pad string.
*
* @param string $oneTimePad The optional one-time pad key.
*
* @throws \Exception Validation errors.
*/
protected function validateOneTimePad($oneTimePad)
{
if (!is_string($oneTimePad)) {
throw new \InvalidArgumentException('The one-time pad key must be a string or a binary string.');
}
}

/**
* Encrypts the given plain data multiple times with different algorithms as layers.
*
* @param string $plainData The plain input string.
* @param string $oneTimePad The optional one-time pad key.
*
* @return string The cipher/encrypted data.
* @throws \Exception Validation errors.
*
* @note The one-time pad key must be the same length as the input date to maximize security.
*/
public function layeredEncryptData($plainData, $oneTimePad = '')
{
$this->validateOneTimePad($oneTimePad);

if (!is_string($plainData)) {
throw new \InvalidArgumentException(
'The data for encryption must be a string or a binary string.'
);
} elseif (!empty(trim($oneTimePad))) {
$plainData = $this->xorTwoStrings($plainData, $oneTimePad);
}

$last = count($this->ciphers) - 1;

for ($i = 0; $i <= $last; $i++) {
$cipher = $this->ciphers[$i];

$plainData = $cipher->encryptData($plainData);
}

return $plainData;
}

/**
* Decrypts the given cipher data multiple times with different algorithms as layers.
*
* @param string $cipherData The encrypted input string.
* @param string $oneTimePad The optional one-time pad key.
*
* @return string The decrypted/plain data.
* @throws \Exception Validation errors.
*/
public function layeredDecryptData($cipherData, $oneTimePad = '')
{
$this->validateOneTimePad($oneTimePad);

$last = count($this->ciphers) - 1;

for ($i = $last; $i >= 0; $i--) {
$cipher = $this->ciphers[$i];

$cipherData = $cipher->decryptData($cipherData);
}

if (!empty(trim($oneTimePad))) {
$cipherData = $this->xorTwoStrings($cipherData, $oneTimePad);
}

return $cipherData;
}
}
Loading

0 comments on commit 5bf9361

Please sign in to comment.