diff --git a/libraries/joomla/crypt/cipher/crypto.php b/libraries/joomla/crypt/cipher/crypto.php new file mode 100644 index 0000000000000..18011c11edaa0 --- /dev/null +++ b/libraries/joomla/crypt/cipher/crypto.php @@ -0,0 +1,125 @@ +type != 'crypto') + { + throw new InvalidArgumentException('Invalid key of type: ' . $key->type . '. Expected crypto.'); + } + + // Decrypt the data. + try + { + return Crypto::Decrypt($data, $key->public); + } + catch (InvalidCiphertextException $ex) + { + throw new RuntimeException('DANGER! DANGER! The ciphertext has been tampered with!', $ex->getCode(), $ex); + } + catch (CryptoTestFailedException $ex) + { + throw new RuntimeException('Cannot safely perform decryption', $ex->getCode(), $ex); + } + catch (CannotPerformOperationException $ex) + { + throw new RuntimeException('Cannot safely perform decryption', $ex->getCode(), $ex); + } + } + + /** + * Method to encrypt a data string. + * + * @param string $data The data string to encrypt. + * @param JCryptKey $key The key object to use for encryption. + * + * @return string The encrypted data string. + * + * @since 3.5 + * @throws RuntimeException + */ + public function encrypt($data, JCryptKey $key) + { + // Validate key. + if ($key->type != 'crypto') + { + throw new InvalidArgumentException('Invalid key of type: ' . $key->type . '. Expected crypto.'); + } + + // Encrypt the data. + try + { + return Crypto::Encrypt($data, $key->public); + } + catch (CryptoTestFailedException $ex) + { + throw new RuntimeException('Cannot safely perform encryption', $ex->getCode(), $ex); + } + catch (CannotPerformOperationException $ex) + { + throw new RuntimeException('Cannot safely perform encryption', $ex->getCode(), $ex); + } + } + + /** + * Method to generate a new encryption key object. + * + * @param array $options Key generation options. + * + * @return JCryptKey + * + * @since 3.5 + * @throws RuntimeException + */ + public function generateKey(array $options = array()) + { + // Create the new encryption key object. + $key = new JCryptKey('crypto'); + + // Generate the encryption key. + try + { + $key->public = Crypto::CreateNewRandomKey(); + } + catch (CryptoTestFailedException $ex) + { + throw new RuntimeException('Cannot safely create a key', $ex->getCode(), $ex); + } + catch (CannotPerformOperationException $ex) + { + throw new RuntimeException('Cannot safely create a key', $ex->getCode(), $ex); + } + + // Explicitly flag the private as unused in this cipher. + $key->private = 'unused'; + + return $key; + } +} diff --git a/tests/unit/suites/libraries/joomla/crypt/cipher/JCryptCipherCryptoTest.php b/tests/unit/suites/libraries/joomla/crypt/cipher/JCryptCipherCryptoTest.php new file mode 100644 index 0000000000000..8db6ba2c1a986 --- /dev/null +++ b/tests/unit/suites/libraries/joomla/crypt/cipher/JCryptCipherCryptoTest.php @@ -0,0 +1,99 @@ +{DJzOHMCv_<#yKuN/G`/Us{GkgicWG$M|HW;kI0BVZ^|FY/"Obt53?PNaWwhmRtH;lWkWE4vlG5CIFA!abu&F=Xo#Qw}gAp3;GL\'k])%D}C+W&ne6_F$3P5'), + array('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' . + 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor ' . + 'in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt ' . + 'in culpa qui officia deserunt mollit anim id est laborum.'), + array('لا أحد يحب الألم بذاته، يسعى ورائه أو يبتغيه، ببساطة لأنه الألم...'), + array('Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства'), + array('The quick brown fox jumps over the lazy dog.') + ); + } + + /** + * @testdox Validates data is encrypted and decrypted correctly + * + * @param string $data The decrypted data to validate + * + * @covers JCryptCipherCrypto::decrypt + * @covers JCryptCipherCrypto::encrypt + * @dataProvider dataStrings + */ + public function testDataEncryptionAndDecryption($data) + { + $cipher = new JCryptCipherCrypto; + $key = $cipher->generateKey(); + + $encrypted = $cipher->encrypt($data, $key); + + // Assert that the encrypted value is not the same as the clear text value. + $this->assertNotSame($data, $encrypted); + + $decrypted = $cipher->decrypt($encrypted, $key); + + // Assert the decrypted string is the same value we started with + $this->assertSame($data, $decrypted); + } + + /** + * @testdox Validates keys are correctly generated + * + * @covers JCryptCipherCrypto::generateKey + */ + public function testGenerateKey() + { + $cipher = new JCryptCipherCrypto; + $key = $cipher->generateKey(); + + // Assert that the key is the correct type. + $this->assertInstanceOf('JCryptKey', $key); + + // Assert the private key is our expected value. + $this->assertSame('unused', $key->private); + + // Assert the public key is the expected length + $this->assertSame(Crypto::KEY_BYTE_SIZE, JCrypt::safeStrlen($key->public)); + + // Assert the key is of the correct type. + $this->assertAttributeEquals('crypto', 'type', $key); + } +}