-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a hybrid multi-layered cryptographic protocol implementation.
- Loading branch information
1 parent
2935f91
commit 5bf9361
Showing
9 changed files
with
1,312 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
src/CryptoManana/Core/Interfaces/Containers/LayeredEncryptionInterface.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = ''); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
246 changes: 246 additions & 0 deletions
246
src/CryptoManana/CryptographicProtocol/LayeredEncryption.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.