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/composer.lock b/www/composer.lock index b5f4f8b1..0c2e8a47 100644 --- a/www/composer.lock +++ b/www/composer.lock @@ -12,7 +12,7 @@ "dist": { "type": "path", "url": "/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All", - "reference": "c7ec1300b779f5ebc9dce52d4ce5484e4d22e9c2" + "reference": "b16ff4c613f6f76e8f518d47b4a04c1b914fee82" }, "require": { "php": ">=8.1" diff --git a/www/vendor/composer/installed.json b/www/vendor/composer/installed.json index fba64cd4..dd91d0b8 100644 --- a/www/vendor/composer/installed.json +++ b/www/vendor/composer/installed.json @@ -7,7 +7,7 @@ "dist": { "type": "path", "url": "/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All", - "reference": "c7ec1300b779f5ebc9dce52d4ce5484e4d22e9c2" + "reference": "b16ff4c613f6f76e8f518d47b4a04c1b914fee82" }, "require": { "php": ">=8.1" diff --git a/www/vendor/composer/installed.php b/www/vendor/composer/installed.php index 9d2847ab..4f5409bd 100644 --- a/www/vendor/composer/installed.php +++ b/www/vendor/composer/installed.php @@ -13,7 +13,7 @@ 'egrajp/corelibs-composer-all' => array( 'pretty_version' => 'dev-development', 'version' => 'dev-development', - 'reference' => 'c7ec1300b779f5ebc9dce52d4ce5484e4d22e9c2', + 'reference' => 'b16ff4c613f6f76e8f518d47b4a04c1b914fee82', 'type' => 'library', 'install_path' => __DIR__ . '/../egrajp/corelibs-composer-all', 'aliases' => array(), diff --git a/www/vendor/egrajp/corelibs-composer-all/publish/last.published b/www/vendor/egrajp/corelibs-composer-all/publish/last.published index 56b6be4e..a2f28f43 100644 --- a/www/vendor/egrajp/corelibs-composer-all/publish/last.published +++ b/www/vendor/egrajp/corelibs-composer-all/publish/last.published @@ -1 +1 @@ -8.3.1 +8.4.0 diff --git a/www/vendor/egrajp/corelibs-composer-all/src/ACL/Login.php b/www/vendor/egrajp/corelibs-composer-all/src/ACL/Login.php index c4126be7..e657ee19 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/ACL/Login.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/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 @@ -1608,7 +1608,7 @@ class Login // TODO: submit or JS to set target page as ajax call // NOTE: for the HTML block I ignore line lengths // phpcs:disable - $this->login_template['password_change'] = <<login_template['password_change'] = << @@ -1626,7 +1626,7 @@ class Login

{TITLE_PASSWORD_CHANGE}

{PASSWORD_CHANGE_SHOW} -EOM; +HTML; // phpcs:enable } if ($this->password_forgot) { @@ -1650,7 +1650,7 @@ EOM; // now check templates // TODO: submit or JS to set target page as ajax call if (!$this->login_template['template']) { - $this->login_template['template'] = <<login_template['template'] = << @@ -1712,7 +1712,7 @@ h3 { font-size: 18px; } -EOM; +HTML; } } diff --git a/www/vendor/egrajp/corelibs-composer-all/src/Basic.php b/www/vendor/egrajp/corelibs-composer-all/src/Basic.php index a9a70c52..7e1882e8 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/Basic.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/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/vendor/egrajp/corelibs-composer-all/src/Check/Password.php b/www/vendor/egrajp/corelibs-composer-all/src/Check/Password.php index 1475064c..f1f588b1 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/Check/Password.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/Check/Password.php @@ -1,6 +1,8 @@ db_functions->__dbPrintLastError(); if ($cursor !== false) { - $pg_error_string = $this->db_functions->__dbPrintError($cursor); + [$db_prefix, $db_error_string] = $this->db_functions->__dbPrintError($cursor); } if ($cursor === false && method_exists($this->db_functions, '__dbPrintError')) { - $pg_error_string = $this->db_functions->__dbPrintError(); + [$db_prefix, $db_error_string] = $this->db_functions->__dbPrintError(); } - if ($pg_error_string) { - $this->__dbDebug('db', $pg_error_string, 'DB_ERROR', $where_called); + // prefix the master if not the same + if ( + !empty($db_error_string_last) && + trim($db_error_string) != trim($db_error_string_last) + ) { + $db_error_string = + $db_prefix_last . ' ' . $db_error_string_last . ';' + . $db_prefix . ' ' . $db_error_string; + } elseif (!empty($db_error_string)) { + $db_error_string = $db_prefix . ' ' . $db_error_string; } - return [$where_called, $pg_error_string]; + if ($db_error_string) { + $this->__dbDebug('db', $db_error_string, 'DB_ERROR', $where_called); + } + return [ + $where_called, + $db_error_string + ]; } /** @@ -902,11 +920,14 @@ class IO // because the placeholders start with $ and at 1, // we need to increase each key and prefix it with a $ char for ($i = 0, $iMax = count($keys); $i < $iMax; $i++) { - $keys[$i] = '$' . ($keys[$i] + 1); + // note: if I use $ here, the str_replace will + // replace it again. eg $11 '$1'1would be replaced with $1 again // prefix data set with parameter pos - $data[$i] = $keys[$i] . ':' . ($data[$i] === null ? + $data[$i] = '#' . ($keys[$i] + 1) . ':' . ($data[$i] === null ? '"NULL"' : (string)$data[$i] ); + // search part + $keys[$i] = '$' . ($keys[$i] + 1); } // simply replace the $1, $2, ... with the actual data and return it return str_replace( diff --git a/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/Interface/SqlFunctions.php b/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/Interface/SqlFunctions.php index 5aa7fdb5..7e81a164 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/Interface/SqlFunctions.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/Interface/SqlFunctions.php @@ -209,10 +209,17 @@ interface SqlFunctions /** * Undocumented function * - * @param \PgSql\Result|false $cursor - * @return string + * @return array{0:string,1:string} */ - public function __dbPrintError(\PgSql\Result|false $cursor = false): string; + public function __dbPrintLastError(): array; + + /** + * Undocumented function + * + * @param \PgSql\Result|false $cursor + * @return array{0:string,1:string} + */ + public function __dbPrintError(\PgSql\Result|false $cursor = false): array; /** * Undocumented function diff --git a/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/PgSQL.php b/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/PgSQL.php index 9f42f76a..2d9b34eb 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/PgSQL.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/PgSQL.php @@ -61,7 +61,7 @@ class PgSQL implements Interface\SqlFunctions /** @var string */ private $last_error_query; /** @var \PgSql\Connection|false */ - private $dbh; + private $dbh = false; /** * queries last error query and returns true or false if error was set @@ -532,18 +532,37 @@ class PgSQL implements Interface\SqlFunctions return $this->dbh; } + /** + * Returns last error for active cursor + * + * @return array{0:string,1:string} prefix, error string + */ + public function __dbPrintLastError(): array + { + if (is_bool($this->dbh)) { + return ['', '']; + } + if (!empty($error_message = pg_last_error($this->dbh))) { + return [ + '-PostgreSQL-Error-Last-', + $error_message + ]; + } + return ['', '']; + } + /** * reads the last error for this cursor and returns * html formatted string with error name * * @param \PgSql\Result|false $cursor cursor - * or null - * @return string error string + * or null + * @return array{0:string,1:string} prefix, error string */ - public function __dbPrintError(\PgSql\Result|false $cursor = false): string + public function __dbPrintError(\PgSql\Result|false $cursor = false): array { if (is_bool($this->dbh)) { - return ''; + return ['', '']; } // run the query again for the error result here if ((is_bool($cursor)) && $this->last_error_query) { @@ -552,10 +571,12 @@ class PgSQL implements Interface\SqlFunctions $cursor = pg_get_result($this->dbh); } if ($cursor && $error_str = pg_result_error($cursor)) { - return '-PostgreSQL-Error- ' - . $error_str; + return [ + '-PostgreSQL-Error-', + $error_str + ]; } else { - return ''; + return ['', '']; } } diff --git a/www/vendor/egrajp/corelibs-composer-all/src/Output/Form/Generate.php b/www/vendor/egrajp/corelibs-composer-all/src/Output/Form/Generate.php index 65555bc3..90daccc8 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/Output/Form/Generate.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/Output/Form/Generate.php @@ -1954,7 +1954,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO if ($this->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/vendor/egrajp/corelibs-composer-all/src/Security/CreateKey.php b/www/vendor/egrajp/corelibs-composer-all/src/Security/CreateKey.php new file mode 100644 index 00000000..add2773e --- /dev/null +++ b/www/vendor/egrajp/corelibs-composer-all/src/Security/CreateKey.php @@ -0,0 +1,61 @@ +dbExec("DROP TABLE test_meta"); } // uid is for internal reference tests - $base_table = <<dbExec( // primary key name is table + '_id' - <<dbExec( - <<dbExec( - << false, 'array dims' => 0, 'is enum' => false, - 'is base' => 1, + 'is base' => true, 'is composite' => false, - 'is pesudo' => false, 'description' => '', + 'is pseudo' => false ], 'row_2' => [ 'num' => 2, @@ -1355,10 +1355,10 @@ final class CoreLibsDBIOTest extends TestCase 'has default' => false, 'array dims' => 0, 'is enum' => false, - 'is base' => 1, + 'is base' => true, 'is composite' => false, - 'is pesudo' => false, 'description' => '', + 'is pseudo' => false ] ] ], @@ -1374,10 +1374,10 @@ final class CoreLibsDBIOTest extends TestCase 'has default' => false, 'array dims' => 0, 'is enum' => false, - 'is base' => 1, + 'is base' => true, 'is composite' => false, - 'is pesudo' => false, 'description' => '', + 'is pseudo' => false ], 'row_2' => [ 'num' => 2, @@ -1387,10 +1387,10 @@ final class CoreLibsDBIOTest extends TestCase 'has default' => false, 'array dims' => 0, 'is enum' => false, - 'is base' => 1, + 'is base' => true, 'is composite' => false, - 'is pesudo' => false, 'description' => '', + 'is pseudo' => false ] ] ], @@ -4425,16 +4425,16 @@ final class CoreLibsDBIOTest extends TestCase ] ] ], - // same but as EOM - 'single insert (PK), EOM string' => [ - << [ + << [ - << [ + <<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/www/vendor/egrajp/corelibs-composer-all/test/phpunit/Security/CoreLibsSecuritySymmetricEncryption.php b/www/vendor/egrajp/corelibs-composer-all/test/phpunit/Security/CoreLibsSecuritySymmetricEncryption.php new file mode 100644 index 00000000..9e2773da --- /dev/null +++ b/www/vendor/egrajp/corelibs-composer-all/test/phpunit/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__