diff --git a/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php b/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php
index 33a2f458..d3f502af 100644
--- a/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php
+++ b/4dev/tests/Security/CoreLibsSecuritySymmetricEncryptionTest.php
@@ -46,12 +46,34 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
public function testEncryptDecryptSuccess(string $input, string $expected): void
{
$key = CreateKey::generateRandomKey();
- $encrypted = SymmetricEncryption::encrypt($input, $key);
- $decrypted = SymmetricEncryption::decrypt($encrypted, $key);
+
+ // test class
+ $crypt = new SymmetricEncryption($key);
+ $encrypted = $crypt->encrypt($input);
+ $decrypted = $crypt->decrypt($encrypted);
+ $this->assertEquals(
+ $expected,
+ $decrypted,
+ 'Class call',
+ );
+
+ // test indirect
+ $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
+ $decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted);
+ $this->assertEquals(
+ $expected,
+ $decrypted,
+ 'Class Instance call',
+ );
+
+ // test static
+ $encrypted = SymmetricEncryption::encryptKey($input, $key);
+ $decrypted = SymmetricEncryption::decryptKey($encrypted, $key);
$this->assertEquals(
$expected,
- $decrypted
+ $decrypted,
+ 'Static call',
);
}
@@ -86,10 +108,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
public function testEncryptFailed(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
- $encrypted = SymmetricEncryption::encrypt($input, $key);
$wrong_key = CreateKey::generateRandomKey();
+
+ // wrong key in class call
+ $crypt = new SymmetricEncryption($key);
+ $encrypted = $crypt->encrypt($input);
$this->expectExceptionMessage($exception_message);
- SymmetricEncryption::decrypt($encrypted, $wrong_key);
+ $crypt->setKey($key);
+ $crypt->decrypt($encrypted);
+
+ // class instance
+ $encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
+ $this->expectExceptionMessage($exception_message);
+ SymmetricEncryption::getInstance($wrong_key)->decrypt($encrypted);
+
+ // class static
+ $encrypted = SymmetricEncryption::encryptKey($input, $key);
+ $this->expectExceptionMessage($exception_message);
+ SymmetricEncryption::decryptKey($encrypted, $wrong_key);
}
/**
@@ -107,7 +143,6 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
'too short hex key' => [
'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
'excpetion_message' => 'Key is not the correct size (must be '
- . 'SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long).'
],
];
}
@@ -126,13 +161,33 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
*/
public function testWrongKey(string $key, string $exception_message): void
{
- $this->expectExceptionMessage($exception_message);
- SymmetricEncryption::encrypt('test', $key);
- // we must encrypt valid thing first so we can fail with the wrong kjey
$enc_key = CreateKey::generateRandomKey();
- $encrypted = SymmetricEncryption::encrypt('test', $enc_key);
+
+ // class
+ $crypt = new SymmetricEncryption($key);
$this->expectExceptionMessage($exception_message);
- SymmetricEncryption::decrypt($encrypted, $key);
+ $crypt->encrypt('test');
+ $crypt->setKey($enc_key);
+ $encrypted = $crypt->encrypt('test');
+ $this->expectExceptionMessage($exception_message);
+ $crypt->setKey($key);
+ $crypt->decrypt($encrypted);
+
+ // class instance
+ $this->expectExceptionMessage($exception_message);
+ SymmetricEncryption::getInstance($key)->encrypt('test');
+ // we must encrypt valid thing first so we can fail with the wrong key
+ $encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test');
+ $this->expectExceptionMessage($exception_message);
+ SymmetricEncryption::getInstance($key)->decrypt($encrypted);
+
+ // class static
+ $this->expectExceptionMessage($exception_message);
+ SymmetricEncryption::encryptKey('test', $key);
+ // we must encrypt valid thing first so we can fail with the wrong key
+ $encrypted = SymmetricEncryption::encryptKey('test', $enc_key);
+ $this->expectExceptionMessage($exception_message);
+ SymmetricEncryption::decryptKey($encrypted, $key);
}
/**
@@ -145,7 +200,7 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
return [
'too short ciphertext' => [
'input' => 'short',
- 'exception_message' => 'Invalid ciphertext (too short)'
+ 'exception_message' => 'Decipher message failed: '
],
];
}
@@ -164,8 +219,18 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
public function testWrongCiphertext(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
+ // class
+ $crypt = new SymmetricEncryption($key);
$this->expectExceptionMessage($exception_message);
- SymmetricEncryption::decrypt($input, $key);
+ $crypt->decrypt($input);
+
+ // class instance
+ $this->expectExceptionMessage($exception_message);
+ SymmetricEncryption::getInstance($key)->decrypt($input);
+
+ // class static
+ $this->expectExceptionMessage($exception_message);
+ SymmetricEncryption::decryptKey($input, $key);
}
}
diff --git a/www/admin/class_test.encryption.php b/www/admin/class_test.encryption.php
index c03e5515..77e16e23 100644
--- a/www/admin/class_test.encryption.php
+++ b/www/admin/class_test.encryption.php
@@ -40,16 +40,33 @@ $key = CreateKey::generateRandomKey();
print "Secret Key: " . $key . "
";
$string = "I a some deep secret";
-$encrypted = SymmetricEncryption::encrypt($string, $key);
-$decrypted = SymmetricEncryption::decrypt($encrypted, $key);
+//
+$crypt = new SymmetricEncryption($key);
+$encrypted = $crypt->encrypt($string);
+$decrypted = $crypt->decrypt($encrypted);
+print "[C] Encrypted: " . $encrypted . "
";
+print "[C] Decrytped: " . $decrypted . "
";
+$encrypted = SymmetricEncryption::getInstance($key)->encrypt($string);
+$decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted);
+print "[S] Original: " . $string . "
";
+print "[S] Encrypted: " . $encrypted . "
";
+print "[S] Decrytped: " . $decrypted . "
";
+$encrypted = SymmetricEncryption::encryptKey($string, $key);
+$decrypted = SymmetricEncryption::decryptKey($encrypted, $key);
+print "[SS] Encrypted: " . $encrypted . "
";
+print "[SS] Decrytped: " . $decrypted . "
";
-print "Original: " . $string . "
";
-print "Encrypted: " . $encrypted . "
";
-print "Decrytped: " . $decrypted . "
";
+print "
INIT KEY MISSING
";
+try {
+ $crypt = new SymmetricEncryption();
+ $encrypted = $crypt->decrypt($string);
+} catch (Exception $e) {
+ print("Error: " . $e->getMessage() . "
");
+}
print "
WRONG CIPHERTEXT
";
try {
- $decrypted = SymmetricEncryption::decrypt('flupper', $key);
+ $decrypted = SymmetricEncryption::decryptKey('flupper', $key);
} catch (Exception $e) {
print "Error: " . $e->getMessage() . "
";
}
@@ -57,7 +74,7 @@ try {
print "
SHORT and WRONG KEY
";
$key = 'wrong_key';
try {
- $encrypted = SymmetricEncryption::encrypt($string, $key);
+ $encrypted = SymmetricEncryption::encryptKey($string, $key);
} catch (Exception $e) {
print "Error: " . $e->getMessage() . "
";
}
@@ -65,7 +82,7 @@ try {
print "
INVALID HEX KEY
";
$key = '1cabd5cba9e042f12522f4ff2de5c31d233b';
try {
- $encrypted = SymmetricEncryption::encrypt($string, $key);
+ $encrypted = SymmetricEncryption::encryptKey($string, $key);
} catch (Exception $e) {
print "Error: " . $e->getMessage() . "
";
}
@@ -73,21 +90,10 @@ try {
print "
WRONG KEY TO DECRYPT
";
$key = CreateKey::generateRandomKey();
$string = "I a some deep secret";
-$encrypted = SymmetricEncryption::encrypt($string, $key);
-$key = CreateKey::generateRandomKey();
-try {
- $decrypted = SymmetricEncryption::decrypt($encrypted, $key);
-} catch (Exception $e) {
- print "Error: " . $e->getMessage() . "
";
-}
-
-print "
WRONG KEY TO DECRYPT
";
-$key = CreateKey::generateRandomKey();
-$string = "I a some deep secret";
-$encrypted = SymmetricEncryption::encrypt($string, $key);
+$encrypted = SymmetricEncryption::encryptKey($string, $key);
$key = 'wrong_key';
try {
- $decrypted = SymmetricEncryption::decrypt($encrypted, $key);
+ $decrypted = SymmetricEncryption::decryptKey($encrypted, $key);
} catch (Exception $e) {
print "Error: " . $e->getMessage() . "
";
}
diff --git a/www/lib/CoreLibs/Security/SymmetricEncryption.php b/www/lib/CoreLibs/Security/SymmetricEncryption.php
index 2586e94a..c12f4b3f 100644
--- a/www/lib/CoreLibs/Security/SymmetricEncryption.php
+++ b/www/lib/CoreLibs/Security/SymmetricEncryption.php
@@ -21,58 +21,82 @@ use SodiumException;
class SymmetricEncryption
{
+ /** @var SymmetricEncryption self instance */
+ private static SymmetricEncryption $instance;
+
+ /** @var string bin hex key */
+ private string $key = '';
+
/**
- * Encrypt a message
+ * init class
+ * if key not passed, key must be set with createKey
*
- * @param string $message Message to encrypt
- * @param string $key Encryption key (as hex string)
- * @return string
- * @throws \Exception
- * @throws \RangeException
+ * @param string|null|null $key
*/
- public static function encrypt(string $message, string $key): string
+ public function __construct(
+ string|null $key = null
+ ) {
+ if ($key != null) {
+ $this->setKey($key);
+ }
+ }
+
+ /**
+ * Returns the singleton self object.
+ * For function wrapper use
+ *
+ * @return SymmetricEncryption object
+ */
+ public static function getInstance(string|null $key = null): self
+ {
+ if (empty(self::$instance)) {
+ self::$instance = new self($key);
+ }
+ return self::$instance;
+ }
+
+ /* ************************************************************************
+ * MARK: PRIVATE
+ * *************************************************************************/
+
+ /**
+ * create key and check validity
+ *
+ * @param string $key The key from which the binary key will be created
+ * @return string Binary key string
+ */
+ private function createKey(string $key): string
{
try {
$key = CreateKey::hex2bin($key);
} catch (SodiumException $e) {
- throw new \UnexpectedValueException('Invalid hex key');
+ throw new \UnexpectedValueException('Invalid hex key: ' . $e->getMessage());
}
if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
throw new \RangeException(
'Key is not the correct size (must be '
- . 'SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long).'
+ . SODIUM_CRYPTO_SECRETBOX_KEYBYTES . ' bytes long).'
);
}
- $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
-
- $cipher = base64_encode(
- $nonce
- . sodium_crypto_secretbox(
- $message,
- $nonce,
- $key
- )
- );
- sodium_memzero($message);
- sodium_memzero($key);
- return $cipher;
+ return $key;
}
/**
- * Decrypt a message
+ * Decryption call
*
- * @param string $encrypted Message encrypted with safeEncrypt()
- * @param string $key Encryption key (as hex string)
- * @return string
- * @throws \Exception
+ * @param string $encrypted Text to decrypt
+ * @param ?string $key Mandatory encryption key, will throw exception if empty
+ * @return string Plain text
+ * @throws \RangeException
+ * @throws \UnexpectedValueException
+ * @throws \UnexpectedValueException
*/
- public static function decrypt(string $encrypted, string $key): string
+ private function decryptData(string $encrypted, ?string $key): string
{
- try {
- $key = CreateKey::hex2bin($key);
- } catch (SodiumException $e) {
- throw new \Exception('Invalid hex key');
+ if (empty($key)) {
+ throw new \UnexpectedValueException('Key not set');
}
+ $key = $this->createKey($key);
$decoded = base64_decode($encrypted);
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
@@ -85,7 +109,7 @@ class SymmetricEncryption
$key
);
} catch (SodiumException $e) {
- throw new \UnexpectedValueException('Invalid ciphertext (too short)');
+ throw new \UnexpectedValueException('Decipher message failed: ' . $e->getMessage());
}
if (!is_string($plain)) {
throw new \UnexpectedValueException('Invalid Key');
@@ -94,6 +118,117 @@ class SymmetricEncryption
sodium_memzero($key);
return $plain;
}
+
+ /**
+ * Encrypt a message
+ *
+ * @param string $message Message to encrypt
+ * @param ?string $key Mandatory encryption key, will throw exception if empty
+ * @return string
+ * @throws \Exception
+ * @throws \RangeException
+ */
+ private function encryptData(string $message, ?string $key): string
+ {
+ if (empty($this->key) || $key === null) {
+ throw new \UnexpectedValueException('Key not set');
+ }
+ $key = $this->createKey($key);
+ $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
+ try {
+ $cipher = base64_encode(
+ $nonce
+ . sodium_crypto_secretbox(
+ $message,
+ $nonce,
+ $key,
+ )
+ );
+ } catch (SodiumException $e) {
+ throw new \UnexpectedValueException("Create encrypted message failed: " . $e->getMessage());
+ }
+ sodium_memzero($message);
+ sodium_memzero($key);
+ return $cipher;
+ }
+
+ /* ************************************************************************
+ * MARK: PUBLIC
+ * *************************************************************************/
+
+
+ /**
+ * set a new key for encryption
+ *
+ * @param string $key
+ * @return void
+ */
+ public function setKey(string $key)
+ {
+ if (empty($key)) {
+ throw new \UnexpectedValueException('Key cannot be empty');
+ }
+ $this->key = $key;
+ }
+
+ /**
+ * Decrypt a message
+ * static version
+ *
+ * @param string $encrypted Message encrypted with safeEncrypt()
+ * @param string $key Encryption key (as hex string)
+ * @return string
+ * @throws \Exception
+ * @throws \RangeException
+ * @throws \UnexpectedValueException
+ * @throws \UnexpectedValueException
+ */
+ public static function decryptKey(string $encrypted, string $key): string
+ {
+ return self::getInstance()->decryptData($encrypted, $key);
+ }
+
+ /**
+ * Decrypt a message
+ *
+ * @param string $encrypted Message encrypted with safeEncrypt()
+ * @return string
+ * @throws \RangeException
+ * @throws \UnexpectedValueException
+ * @throws \UnexpectedValueException
+ */
+ public function decrypt(string $encrypted): string
+ {
+ return $this->decryptData($encrypted, $this->key);
+ }
+
+ /**
+ * Encrypt a message
+ * static version
+ *
+ * @param string $message Message to encrypt
+ * @param string $key Encryption key (as hex string)
+ * @return string
+ * @throws \Exception
+ * @throws \RangeException
+ */
+ public static function encryptKey(string $message, string $key): string
+ {
+ return self::getInstance()->encryptData($message, $key);
+ }
+
+ /**
+ * Encrypt a message
+ *
+ * @param string $message Message to encrypt
+ * @return string
+ * @throws \Exception
+ * @throws \RangeException
+ */
+ public function encrypt(string $message): string
+ {
+ return $this->encryptData($message, $this->key);
+ }
}
// __END__