diff --git a/4dev/tests/Check/CoreLibsCheckPasswordTest.php b/4dev/tests/Security/CoreLibsSecurityPasswordTest.php similarity index 82% rename from 4dev/tests/Check/CoreLibsCheckPasswordTest.php rename to 4dev/tests/Security/CoreLibsSecurityPasswordTest.php index ce6a749d..b085992e 100644 --- a/4dev/tests/Check/CoreLibsCheckPasswordTest.php +++ b/4dev/tests/Security/CoreLibsSecurityPasswordTest.php @@ -7,9 +7,9 @@ namespace tests; use PHPUnit\Framework\TestCase; /** - * Test class for Check\Password - * @coversDefaultClass \CoreLibs\Check\Password - * @testdox \CoreLibs\Check\Password method tests + * Test class for Security\Password + * @coversDefaultClass \CoreLibs\Security\Password + * @testdox \CoreLibs\Security\Password method tests */ final class CoreLibsCheckPasswordTest extends TestCase { @@ -46,7 +46,7 @@ final class CoreLibsCheckPasswordTest extends TestCase { $this->assertEquals( $expected, - \CoreLibs\Check\Password::passwordVerify($input, \CoreLibs\Check\Password::passwordSet($input_hash)) + \CoreLibs\Security\Password::passwordVerify($input, \CoreLibs\Security\Password::passwordSet($input_hash)) ); } @@ -65,7 +65,7 @@ final class CoreLibsCheckPasswordTest extends TestCase { $this->assertEquals( $expected, - \CoreLibs\Check\Password::passwordRehashCheck($input) + \CoreLibs\Security\Password::passwordRehashCheck($input) ); } } diff --git a/4dev/tests/Security/CoreLibsSecuritySymmetricEncryption.php b/4dev/tests/Security/CoreLibsSecuritySymmetricEncryption.php new file mode 100644 index 00000000..9e2773da --- /dev/null +++ b/4dev/tests/Security/CoreLibsSecuritySymmetricEncryption.php @@ -0,0 +1,172 @@ + [ + 'input' => 'I am a secret', + 'expected' => 'I am a secret', + ], + ]; + } + + /** + * test encrypt/decrypt produce correct output + * + * @covers ::generateRandomKey + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerEncryptDecryptSuccess + * @testdox encrypt/decrypt $input must be $expected [$_dataName] + * + * @param string $input + * @param string $expected + * @return void + */ + public function testEncryptDecryptSuccess(string $input, string $expected): void + { + $key = CreateKey::generateRandomKey(); + $encrypted = SymmetricEncryption::encrypt($input, $key); + $decrypted = SymmetricEncryption::decrypt($encrypted, $key); + + $this->assertEquals( + $expected, + $decrypted + ); + } + + /** + * Undocumented function + * + * @return array + */ + public function providerEncryptFailed(): array + { + return [ + 'wrong decryption key' => [ + 'input' => 'I am a secret', + 'excpetion_message' => 'Invalid Key' + ], + ]; + } + + /** + * Test decryption with wrong key + * + * @covers ::generateRandomKey + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerEncryptFailed + * @testdox decrypt with wrong key $input throws $exception_message [$_dataName] + * + * @param string $input + * @param string $exception_message + * @return void + */ + public function testEncryptFailed(string $input, string $exception_message): void + { + $key = CreateKey::generateRandomKey(); + $encrypted = SymmetricEncryption::encrypt($input, $key); + $wrong_key = CreateKey::generateRandomKey(); + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decrypt($encrypted, $wrong_key); + } + + /** + * Undocumented function + * + * @return array + */ + public function providerWrongKey(): array + { + return [ + 'not hex key' => [ + 'key' => 'not_a_hex_key', + 'exception_message' => 'Invalid hex key' + ], + 'too short hex key' => [ + 'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b', + 'excpetion_message' => 'Key is not the correct size (must be ' + . 'SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long).' + ], + ]; + } + + /** + * test invalid key provided to decrypt or encrypt + * + * @covers ::encrypt + * @covers ::decrypt + * @dataProvider providerWrongKey + * @testdox wrong key $key throws $exception_message [$_dataName] + * + * @param string $key + * @param string $exception_message + * @return void + */ + 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); + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decrypt($encrypted, $key); + } + + /** + * Undocumented function + * + * @return array + */ + public function providerWrongCiphertext(): array + { + return [ + 'too short ciphertext' => [ + 'input' => 'short', + 'exception_message' => 'Invalid ciphertext (too short)' + ], + ]; + } + + /** + * Undocumented function + * + * @covers ::decrypt + * @dataProvider providerWrongCiphertext + * @testdox too short ciphertext $input throws $exception_message [$_dataName] + * + * @param string $input + * @param string $exception_message + * @return void + */ + public function testWrongCiphertext(string $input, string $exception_message): void + { + $key = CreateKey::generateRandomKey(); + $this->expectExceptionMessage($exception_message); + SymmetricEncryption::decrypt($input, $key); + } +} + +// __END__ diff --git a/phpstan.neon b/phpstan.neon index 5b7b4982..15ed9728 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -39,9 +39,9 @@ parameters: - www/vendor # ignore errores with ignoreErrors: - - # in the class_test tree we allow deprecated calls - message: "#^Call to deprecated method #" - path: %currentWorkingDirectory%/www/admin/class_test.*.php + # - # in the class_test tree we allow deprecated calls + # message: "#^Call to deprecated method #" + # path: %currentWorkingDirectory%/www/admin/class_test.*.php # - '#Expression in empty\(\) is always falsy.#' # - # message: '#Reflection error: [a-zA-Z0-9\\_]+ not found.#' diff --git a/www/admin/class_test.db.single.php b/www/admin/class_test.db.single.php index c70edb4c..778e2730 100644 --- a/www/admin/class_test.db.single.php +++ b/www/admin/class_test.db.single.php @@ -21,10 +21,7 @@ $ECHO_ALL = true; $LOG_FILE_ID = 'classTest-db-single'; ob_end_flush(); -use CoreLibs\Debug\Support as DgS; -use CoreLibs\DB\IO as DbIo; use CoreLibs\Debug\Support; -use CoreLibs\Convert\SetVarType; $log = new CoreLibs\Debug\Logging([ 'log_folder' => BASE . LOG, diff --git a/www/admin/class_test.encryption.php b/www/admin/class_test.encryption.php new file mode 100644 index 00000000..75185cf4 --- /dev/null +++ b/www/admin/class_test.encryption.php @@ -0,0 +1,111 @@ + BASE . LOG, + 'file_id' => $LOG_FILE_ID, + // add file date + 'print_file_date' => true, + // set debug and print flags + 'debug_all' => $DEBUG_ALL, + 'echo_all' => $ECHO_ALL ?? false, + 'print_all' => $PRINT_ALL, +]); + + +// define a list of from to color sets for conversion test + +$PAGE_NAME = 'TEST CLASS: ENCRYPTION'; +print ""; +print "" . $PAGE_NAME . ""; +print ""; +print '
Class Test Master
'; +print '

' . $PAGE_NAME . '

'; + +$key = CreateKey::generateRandomKey(); +print "Secret Key: " . $key . "
"; + +$string = "I a some deep secret"; +$encrypted = SymmetricEncryption::encrypt($string, $key); +$decrypted = SymmetricEncryption::decrypt($encrypted, $key); + +print "Original: " . $string . "
"; +print "Encrypted: " . $encrypted . "
"; +print "Decrytped: " . $decrypted . "
"; + +print "
WRONG CIPHERTEXT
"; +try { + $decrypted = SymmetricEncryption::decrypt('flupper', $key); +} catch (Exception $e) { + print "Error: " . $e->getMessage() . "
"; +} + +print "
SHORT and WRONG KEY
"; +$key = 'wrong_key'; +try { + $encrypted = SymmetricEncryption::encrypt($string, $key); +} catch (Exception $e) { + print "Error: " . $e->getMessage() . "
"; +} + +print "
INVALID HEX KEY
"; +$key = '1cabd5cba9e042f12522f4ff2de5c31d233b'; +try { + $encrypted = SymmetricEncryption::encrypt($string, $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); +$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); +$key = 'wrong_key'; +try { + $decrypted = SymmetricEncryption::decrypt($encrypted, $key); +} catch (Exception $e) { + print "Error: " . $e->getMessage() . "
"; +} + +// error message +print $log->printErrorMsg(); + +print ""; + +// __END__ diff --git a/www/admin/class_test.password.php b/www/admin/class_test.password.php index 73312bb2..3ca8e668 100644 --- a/www/admin/class_test.password.php +++ b/www/admin/class_test.password.php @@ -20,10 +20,10 @@ define('USE_DATABASE', false); // sample config require 'config.php'; // define log file id -$LOG_FILE_ID = 'classTest-pass'; +$LOG_FILE_ID = 'classTest-password'; ob_end_flush(); -use CoreLibs\Check\Password as PwdChk; +use CoreLibs\Security\Password as PwdChk; $log = new CoreLibs\Debug\Logging([ 'log_folder' => BASE . LOG, @@ -35,8 +35,8 @@ $log = new CoreLibs\Debug\Logging([ 'echo_all' => $ECHO_ALL ?? false, 'print_all' => $PRINT_ALL, ]); -$_password = new CoreLibs\Check\Password(); -$password_class = 'CoreLibs\Check\Password'; +$_password = new CoreLibs\Security\Password(); +$password_class = 'CoreLibs\Security\Password'; // define a list of from to color sets for conversion test diff --git a/www/admin/class_test.php b/www/admin/class_test.php index 9c2b453c..e4c020ca 100644 --- a/www/admin/class_test.php +++ b/www/admin/class_test.php @@ -81,6 +81,7 @@ print '
Class Test: MIME
'; print '
Class Test: JSON
'; print '
Class Test: FORM TOKEN
'; print '
Class Test: PASSWORD
'; +print '
Class Test: ENCRYPTION
'; print '
Class Test: MATH
'; print '
Class Test: HTML/ELEMENTS
'; print '
Class Test: EMAIL
'; diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index 92a87028..e657ee19 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -68,7 +68,7 @@ declare(strict_types=1); namespace CoreLibs\ACL; -use CoreLibs\Check\Password; +use CoreLibs\Security\Password; use CoreLibs\Convert\Json; class Login diff --git a/www/lib/CoreLibs/Basic.php b/www/lib/CoreLibs/Basic.php index a9a70c52..7e1882e8 100644 --- a/www/lib/CoreLibs/Basic.php +++ b/www/lib/CoreLibs/Basic.php @@ -1164,7 +1164,7 @@ class Basic public function passwordSet(string $password): string { trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordSet()', E_USER_DEPRECATED); - return \CoreLibs\Check\Password::passwordSet($password); + return \CoreLibs\Security\Password::passwordSet($password); } /** @@ -1177,7 +1177,7 @@ class Basic public function passwordVerify(string $password, string $hash): bool { trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordVerify()', E_USER_DEPRECATED); - return \CoreLibs\Check\Password::passwordVerify($password, $hash); + return \CoreLibs\Security\Password::passwordVerify($password, $hash); } /** @@ -1189,7 +1189,7 @@ class Basic public function passwordRehashCheck(string $hash): bool { trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordRehashCheck()', E_USER_DEPRECATED); - return \CoreLibs\Check\Password::passwordRehashCheck($hash); + return \CoreLibs\Security\Password::passwordRehashCheck($hash); } // *** BETTER PASSWORD OPTIONS END *** diff --git a/www/lib/CoreLibs/Check/Password.php b/www/lib/CoreLibs/Check/Password.php index 1475064c..f1f588b1 100644 --- a/www/lib/CoreLibs/Check/Password.php +++ b/www/lib/CoreLibs/Check/Password.php @@ -1,6 +1,8 @@ table_array[$key]['value']) { // use the better new passwordSet instead of crypt based $this->table_array[$key]['value'] = - \CoreLibs\Check\Password::passwordSet($this->table_array[$key]['value']); + \CoreLibs\Security\Password::passwordSet($this->table_array[$key]['value']); $this->table_array[$key]['HIDDEN_value'] = $this->table_array[$key]['value']; } else { // $this->table_array[$key]['HIDDEN_value'] = diff --git a/www/lib/CoreLibs/Security/CreateKey.php b/www/lib/CoreLibs/Security/CreateKey.php new file mode 100644 index 00000000..add2773e --- /dev/null +++ b/www/lib/CoreLibs/Security/CreateKey.php @@ -0,0 +1,61 @@ + $baseDir . '/lib/CoreLibs/Output/Form/Token.php', 'CoreLibs\\Output\\Image' => $baseDir . '/lib/CoreLibs/Output/Image.php', 'CoreLibs\\Output\\ProgressBar' => $baseDir . '/lib/CoreLibs/Output/ProgressBar.php', + 'CoreLibs\\Security\\CreateKey' => $baseDir . '/lib/CoreLibs/Security/CreateKey.php', + 'CoreLibs\\Security\\Password' => $baseDir . '/lib/CoreLibs/Security/Password.php', + 'CoreLibs\\Security\\SymmetricEncryption' => $baseDir . '/lib/CoreLibs/Security/SymmetricEncryption.php', 'CoreLibs\\Template\\SmartyExtend' => $baseDir . '/lib/CoreLibs/Template/SmartyExtend.php', 'FileUpload\\Core\\qqUploadedFile' => $baseDir . '/lib/FileUpload/Core/qqUploadedFile.php', 'FileUpload\\Core\\qqUploadedFileForm' => $baseDir . '/lib/FileUpload/Core/qqUploadedFileForm.php', diff --git a/www/vendor/composer/autoload_static.php b/www/vendor/composer/autoload_static.php index cc3bb35b..7ff5acf8 100644 --- a/www/vendor/composer/autoload_static.php +++ b/www/vendor/composer/autoload_static.php @@ -115,6 +115,9 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9 'CoreLibs\\Output\\Form\\Token' => __DIR__ . '/../..' . '/lib/CoreLibs/Output/Form/Token.php', 'CoreLibs\\Output\\Image' => __DIR__ . '/../..' . '/lib/CoreLibs/Output/Image.php', 'CoreLibs\\Output\\ProgressBar' => __DIR__ . '/../..' . '/lib/CoreLibs/Output/ProgressBar.php', + 'CoreLibs\\Security\\CreateKey' => __DIR__ . '/../..' . '/lib/CoreLibs/Security/CreateKey.php', + 'CoreLibs\\Security\\Password' => __DIR__ . '/../..' . '/lib/CoreLibs/Security/Password.php', + 'CoreLibs\\Security\\SymmetricEncryption' => __DIR__ . '/../..' . '/lib/CoreLibs/Security/SymmetricEncryption.php', 'CoreLibs\\Template\\SmartyExtend' => __DIR__ . '/../..' . '/lib/CoreLibs/Template/SmartyExtend.php', 'FileUpload\\Core\\qqUploadedFile' => __DIR__ . '/../..' . '/lib/FileUpload/Core/qqUploadedFile.php', 'FileUpload\\Core\\qqUploadedFileForm' => __DIR__ . '/../..' . '/lib/FileUpload/Core/qqUploadedFileForm.php',