Page MenuHomeIn-Portal Phabricator

D354.id.diff
No OneTemporary

File Metadata

Created
Sat, Apr 19, 3:18 PM

D354.id.diff

Index: branches/5.2.x/composer.json
===================================================================
--- branches/5.2.x/composer.json
+++ branches/5.2.x/composer.json
@@ -1,5 +1,11 @@
{
"name": "In-Portal",
+ "require": {
+ "php": ">=5.3.7",
+ "paragonie/random_compat": "^2.0",
+ "symfony/polyfill-php55": "^1.19",
+ "symfony/polyfill-php56": "^1.19"
+ },
"require-dev": {
"aik099/phpunit-mink": "^2.2",
"qa-tools/qa-tools": "^1.2",
Index: branches/5.2.x/composer.lock
===================================================================
--- branches/5.2.x/composer.lock
+++ branches/5.2.x/composer.lock
@@ -4,8 +4,318 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "bdf85cd4a4719d9255c1eb5022f63706",
- "packages": [],
+ "content-hash": "ef20f3d542075e2d792fba4a7d3379c2",
+ "packages": [
+ {
+ "name": "ircmaxell/password-compat",
+ "version": "v1.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ircmaxell/password_compat.git",
+ "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c",
+ "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c",
+ "shasum": ""
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.*"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "lib/password.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Anthony Ferrara",
+ "email": "ircmaxell@php.net",
+ "homepage": "http://blog.ircmaxell.com"
+ }
+ ],
+ "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash",
+ "homepage": "https://github.com/ircmaxell/password_compat",
+ "keywords": [
+ "hashing",
+ "password"
+ ],
+ "time": "2014-11-20T16:49:30+00:00"
+ },
+ {
+ "name": "paragonie/random_compat",
+ "version": "v2.0.19",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "446fc9faa5c2a9ddf65eb7121c0af7e857295241"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/446fc9faa5c2a9ddf65eb7121c0af7e857295241",
+ "reference": "446fc9faa5c2a9ddf65eb7121c0af7e857295241",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.*|5.*"
+ },
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "lib/random.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
+ ],
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "polyfill",
+ "pseudorandom",
+ "random"
+ ],
+ "time": "2020-10-15T10:06:57+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php55",
+ "version": "v1.19.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php55.git",
+ "reference": "248a5c9877b126493abb661e4fb47792e418035b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/248a5c9877b126493abb661e4fb47792e418035b",
+ "reference": "248a5c9877b126493abb661e4fb47792e418035b",
+ "shasum": ""
+ },
+ "require": {
+ "ircmaxell/password-compat": "~1.0",
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.19-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php55\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-10-23T09:01:57+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php56",
+ "version": "v1.19.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php56.git",
+ "reference": "ea19621731cbd973a6702cfedef3419768bf3372"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/ea19621731cbd973a6702cfedef3419768bf3372",
+ "reference": "ea19621731cbd973a6702cfedef3419768bf3372",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "symfony/polyfill-util": "~1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.19-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php56\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-10-23T09:01:57+00:00"
+ },
+ {
+ "name": "symfony/polyfill-util",
+ "version": "v1.19.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-util.git",
+ "reference": "8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a",
+ "reference": "8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.19-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Util\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony utilities for portability of PHP codes",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compat",
+ "compatibility",
+ "polyfill",
+ "shim"
+ ],
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-10-21T09:57:48+00:00"
+ }
+ ],
"packages-dev": [
{
"name": "aik099/coding-standard",
@@ -747,6 +1057,7 @@
"keywords": [
"tokenizer"
],
+ "abandoned": true,
"time": "2017-12-04T08:55:13+00:00"
},
{
@@ -875,6 +1186,7 @@
"mock",
"xunit"
],
+ "abandoned": true,
"time": "2015-10-02T06:51:40+00:00"
},
{
@@ -1593,9 +1905,12 @@
},
"prefer-stable": false,
"prefer-lowest": false,
- "platform": [],
+ "platform": {
+ "php": ">=5.3.7"
+ },
"platform-dev": [],
"platform-overrides": {
"php": "5.3.7"
- }
+ },
+ "plugin-api-version": "1.1.0"
}
Index: branches/5.2.x/core/install.php
===================================================================
--- branches/5.2.x/core/install.php
+++ branches/5.2.x/core/install.php
@@ -270,7 +270,7 @@
case 'sys_requirements':
$required_checks = Array (
'php_version', 'composer', 'curl', 'simplexml', 'freetype', 'gd_version',
- 'jpeg', 'mysql', 'json', 'date.timezone', 'output_buffering',
+ 'jpeg', 'mysql', 'json', 'openssl', 'date.timezone', 'output_buffering',
);
$check_results = $this->toolkit->CallPrerequisitesMethod('core/', 'CheckSystemRequirements');
@@ -841,6 +841,24 @@
}
}
+ if ( !$this->toolkit->systemConfig->get('SecurityHmacKey', 'Misc')
+ || !$this->toolkit->systemConfig->get('SecurityEncryptionKey', 'Misc')
+ ) {
+ $this->toolkit->systemConfig->set(
+ 'SecurityHmacKey',
+ 'Misc',
+ base64_encode(SecurityGenerator::generateString(
+ SecurityEncrypter::HASHING_KEY_LENGTH,
+ SecurityGenerator::CHAR_ALNUM | SecurityGenerator::CHAR_SYMBOLS
+ ))
+ );
+ $this->toolkit->systemConfig->set(
+ 'SecurityEncryptionKey',
+ 'Misc',
+ SecurityGenerator::generateBytes(SecurityEncrypter::ENCRYPTION_KEY_LENGTH)
+ );
+ }
+
$this->toolkit->systemConfig->save();
break;
Index: branches/5.2.x/core/install/prerequisites.php
===================================================================
--- branches/5.2.x/core/install/prerequisites.php
+++ branches/5.2.x/core/install/prerequisites.php
@@ -145,6 +145,9 @@
$ret['mysql'] = function_exists('mysqli_connect');
$ret['json'] = function_exists('json_encode');
+ // Unable to properly use "SecurityEncrypter::cipherAvailable" method, because no factory here.
+ $ret['openssl'] = function_exists('openssl_encrypt') && in_array('aes-128-cbc', openssl_get_cipher_methods(), true);
+
$output = shell_exec('java -version 2>&1');
$ret['java'] = stripos($output, 'java version') !== false;
Index: branches/5.2.x/core/install/step_templates/sys_requirements.tpl
===================================================================
--- branches/5.2.x/core/install/step_templates/sys_requirements.tpl
+++ branches/5.2.x/core/install/step_templates/sys_requirements.tpl
@@ -25,6 +25,7 @@
'jpeg' => '- JPEG images support<span class="error">*</span>',
'mysql' => '- Database connectivity (via MySQL)<span class="error">*</span>',
'json' => '- JSON processing support<span class="error">*</span>',
+ 'openssl' => '- OpenSSL support<span class="error">*</span>',
'sep2' => '<strong>PHP settings:</strong>',
'memory_limit' => "- Memory requirements changing on the fly",
'display_errors' => "- Prevent script errors in production environment",
Index: branches/5.2.x/core/kernel/application.php
===================================================================
--- branches/5.2.x/core/kernel/application.php
+++ branches/5.2.x/core/kernel/application.php
@@ -756,6 +756,11 @@
$this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params');
$this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery');
+ // security
+ $this->registerClass('SecurityGenerator', KERNEL_PATH . '/security/SecurityGenerator.php');
+ $this->registerClass('SecurityGeneratorPromise', KERNEL_PATH . '/security/SecurityGeneratorPromise.php');
+ $this->registerClass('SecurityEncrypter', KERNEL_PATH . '/security/SecurityEncrypter.php');
+
// session
$this->registerClass('Session', KERNEL_PATH . '/session/session.php');
$this->registerClass('SessionStorage', KERNEL_PATH . '/session/session_storage.php');
Index: branches/5.2.x/core/kernel/security/SecurityEncrypter.php
===================================================================
--- branches/5.2.x/core/kernel/security/SecurityEncrypter.php
+++ branches/5.2.x/core/kernel/security/SecurityEncrypter.php
@@ -0,0 +1,228 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2018 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+
+class SecurityEncrypter extends kBase
+{
+
+ const HASHING_ALGORITHM = 'sha256';
+
+ const HASHING_KEY_LENGTH = 32;
+
+ const ENCRYPTION_METHOD = 'aes-128-cbc'; // Or 'aes-256-cbc'.
+
+ const ENCRYPTION_KEY_LENGTH = 16; // Or 32.
+
+ /**
+ * The HMAC key.
+ *
+ * @var string
+ */
+ protected $hmacKey;
+
+ /**
+ * The encryption key.
+ *
+ * @var string
+ */
+ protected $encryptionKey;
+
+ /**
+ * SecurityEncrypter constructor.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+
+ $vars = kUtil::getSystemConfig()->getData();
+
+ // In the config data is encoded stored to avoid corruption.
+ $this->hmacKey = base64_decode($vars['SecurityHmacKey']);
+
+ // Equivalent of "hex2bin" for PHP < 5.4.
+ $this->encryptionKey = pack('H*', $vars['SecurityEncryptionKey']);
+ }
+
+ /**
+ * Creates signature.
+ *
+ * @param string $string String.
+ * @param boolean $raw_output Return raw signature version.
+ * @param string|null $key_override Override key.
+ *
+ * @return string
+ */
+ public function createSignature($string, $raw_output = false, $key_override = null)
+ {
+ return hash_hmac(self::HASHING_ALGORITHM, $string, $this->_getHmacKey($key_override), $raw_output);
+ }
+
+ /**
+ * Returns HMAC key.
+ *
+ * @param string|null $key_override Key override.
+ *
+ * @return string
+ * @throws InvalidArgumentException When HMAC key is empty.
+ */
+ private function _getHmacKey($key_override = null)
+ {
+ $key = $this->doGetHmacKey($key_override);
+
+ if ( empty($key) ) {
+ throw new InvalidArgumentException('The HMAC key is empty.');
+ }
+
+ if ( mb_strlen($key, '8bit') === self::HASHING_KEY_LENGTH ) {
+ return $key;
+ }
+
+ return $this->deriveKey($key, self::HASHING_KEY_LENGTH);
+ }
+
+ /**
+ * Returns HMAC key.
+ *
+ * @param string|null $key_override Key override.
+ *
+ * @return string
+ */
+ protected function doGetHmacKey($key_override = null)
+ {
+ return isset($key_override) ? $key_override : $this->hmacKey;
+ }
+
+ /**
+ * Returns encryption key.
+ *
+ * @param string|null $key_override Key override.
+ *
+ * @return string
+ * @throws InvalidArgumentException When encryption key is empty.
+ */
+ private function _getEncryptionKey($key_override = null)
+ {
+ $key = $this->doGetEncryptionKey($key_override);
+
+ if ( empty($key) ) {
+ throw new InvalidArgumentException('The encryption key is empty.');
+ }
+
+ if ( mb_strlen($key, '8bit') === self::ENCRYPTION_KEY_LENGTH ) {
+ return $key;
+ }
+
+ return $this->deriveKey($key, self::ENCRYPTION_KEY_LENGTH);
+ }
+
+ /**
+ * Returns encryption key.
+ *
+ * @param string|null $key_override Key override.
+ *
+ * @return string
+ */
+ protected function doGetEncryptionKey($key_override = null)
+ {
+ return isset($key_override) ? $key_override : $this->encryptionKey;
+ }
+
+ /**
+ * Derives given key.
+ *
+ * @param string $key Key.
+ * @param integer $length Length.
+ *
+ * @return string
+ */
+ final public function deriveKey($key, $length)
+ {
+ $salt = SecurityGenerator::generateBytes(16, true);
+
+ return hash_pbkdf2(self::HASHING_ALGORITHM, $key, $salt, 80000, $length, true);
+ }
+
+ /**
+ * Encrypts a plain text.
+ *
+ * @param string $plaintext Plain text.
+ * @param string|null $key_override Key override.
+ *
+ * @return string
+ */
+ final public function encrypt($plaintext, $key_override = null)
+ {
+ $iv_size = openssl_cipher_iv_length(self::ENCRYPTION_METHOD);
+ $iv = openssl_random_pseudo_bytes($iv_size);
+
+ $ciphertext = openssl_encrypt(
+ $plaintext,
+ self::ENCRYPTION_METHOD,
+ $this->_getEncryptionKey($key_override),
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+
+ // Note: We cover the IV in our HMAC.
+ $hmac = $this->createSignature($iv . $ciphertext, true);
+
+ return base64_encode($hmac . $iv . $ciphertext);
+ }
+
+ /**
+ * Decrypts a cipher text.
+ *
+ * @param string $ciphertext Cipher text.
+ * @param string|null $key_override Key override.
+ *
+ * @return string|false
+ * @throws LogicException When signature verification has failed.
+ */
+ final public function decrypt($ciphertext, $key_override = null)
+ {
+ $iv_size = openssl_cipher_iv_length(self::ENCRYPTION_METHOD);
+
+ $decoded = base64_decode($ciphertext);
+ $hmac = mb_substr($decoded, 0, self::HASHING_KEY_LENGTH, '8bit');
+ $iv = mb_substr($decoded, self::HASHING_KEY_LENGTH, $iv_size, '8bit');
+ $ciphertext = mb_substr($decoded, self::HASHING_KEY_LENGTH + $iv_size, null, '8bit');
+
+ $calculated = $this->createSignature($iv . $ciphertext, true);
+
+ if ( !hash_equals($hmac, $calculated) ) {
+ throw new LogicException('Signature verification of ciphertext failed.');
+ }
+
+ return openssl_decrypt(
+ $ciphertext,
+ self::ENCRYPTION_METHOD,
+ $this->_getEncryptionKey($key_override),
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+ }
+
+ /**
+ * Determines if used cipher is available.
+ *
+ * @return boolean
+ */
+ final public function cipherAvailable()
+ {
+ return in_array(self::ENCRYPTION_METHOD, openssl_get_cipher_methods(), true);
+ }
+
+}
Index: branches/5.2.x/core/kernel/security/SecurityGenerator.php
===================================================================
--- branches/5.2.x/core/kernel/security/SecurityGenerator.php
+++ branches/5.2.x/core/kernel/security/SecurityGenerator.php
@@ -0,0 +1,193 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2018 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+
+final class SecurityGenerator
+{
+
+ /**
+ * Flag for uppercase letters.
+ */
+ const CHAR_UPPER = 1;
+
+ /**
+ * Flag for lowercase letters.
+ */
+ const CHAR_LOWER = 2;
+
+ /**
+ * Flag for alpha characters (CHAR_UPPER | CHAR_LOWER).
+ */
+ const CHAR_ALPHA = 3;
+
+ /**
+ * Flag for digits.
+ */
+ const CHAR_DIGITS = 4;
+
+ /**
+ * Flag for alpha numeric characters (CHAR_ALPHA | CHAR_DIGITS).
+ */
+ const CHAR_ALNUM = 7;
+
+ /**
+ * Flag for uppercase hexadecimal symbols (8 | CHAR_DIGITS).
+ */
+ const CHAR_UPPER_HEX = 12;
+
+ /**
+ * Flag for lowercase hexidecimal symbols (16 | CHAR_DIGITS).
+ */
+ const CHAR_LOWER_HEX = 20;
+
+ /**
+ * Flag for base64 symbols (32 | CHAR_ALNUM).
+ */
+ const CHAR_BASE64 = 39;
+
+ /**
+ * Flag for additional symbols accessible via the keyboard.
+ */
+ const CHAR_SYMBOLS = 64;
+
+ /**
+ * Flag for brackets.
+ */
+ const CHAR_BRACKETS = 128;
+
+ /**
+ * Flag for punctuation marks.
+ */
+ const CHAR_PUNCT = 256;
+
+ /**
+ * Flag for upper/lower-case and digits but without "B8G6I1l|0OQDS5Z2".
+ */
+ const EASY_TO_READ = 512;
+
+ /**
+ * Ambiguous characters for "Easy To Read" sets.
+ *
+ * @internal
+ */
+ const AMBIGUOUS_CHARS = 'B8G6I1l|0OQDS5Z2()[]{}:;,.';
+
+ /**
+ * The different characters, by Flag.
+ *
+ * @var array
+ */
+ static protected $charArrays = array(
+ self::CHAR_UPPER => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ self::CHAR_LOWER => 'abcdefghijklmnopqrstuvwxyz',
+ self::CHAR_DIGITS => '0123456789',
+ self::CHAR_UPPER_HEX => 'ABCDEF',
+ self::CHAR_LOWER_HEX => 'abcdef',
+ self::CHAR_BASE64 => '+/',
+ self::CHAR_SYMBOLS => '!"#$%&\'()* +,-./:;<=>?@[\]^_`{|}~',
+ self::CHAR_BRACKETS => '()[]{}<>',
+ self::CHAR_PUNCT => ',.;:',
+ );
+
+ /**
+ * Generate a random string of specified length using supplied character list.
+ *
+ * @param integer $length The length of the generated string.
+ * @param mixed $characters List of characters to use or character flags.
+ *
+ * @return SecurityGeneratorPromise
+ */
+ public static function generateString($length, $characters)
+ {
+ // Combine character sets.
+ if ( is_int($characters) ) {
+ $characters = self::expandCharacterSets($characters);
+ }
+
+ return new SecurityGeneratorPromise(
+ SecurityGeneratorPromise::TYPE_STRING,
+ array($length, $characters)
+ );
+ }
+
+ /**
+ * Expand a character set bitwise spec into a string character set.
+ * This will also replace EASY_TO_READ characters if the flag is set.
+ *
+ * @param integer $spec The spec to expand (bitwise combination of flags).
+ *
+ * @return string The expanded string
+ */
+ protected static function expandCharacterSets($spec)
+ {
+ $combined = '';
+
+ if ( $spec == self::EASY_TO_READ ) {
+ $spec |= self::CHAR_ALNUM;
+ }
+
+ foreach ( self::$charArrays as $flag => $chars ) {
+ // Handle this later.
+ if ( $flag == self::EASY_TO_READ ) {
+ continue;
+ }
+
+ if ( ($spec & $flag) === $flag ) {
+ $combined .= $chars;
+ }
+ }
+
+ // Remove ambiguous characters.
+ if ( $spec & self::EASY_TO_READ ) {
+ $combined = str_replace(str_split(self::AMBIGUOUS_CHARS), '', $combined);
+ }
+
+ return count_chars($combined, 3);
+ }
+
+ /**
+ * Generates a random number.
+ *
+ * @param integer $min Smallest value.
+ * @param integer $max Largest value.
+ *
+ * @return SecurityGeneratorPromise
+ */
+ public static function generateNumber($min, $max)
+ {
+ return new SecurityGeneratorPromise(
+ SecurityGeneratorPromise::TYPE_NUMBER,
+ array($min, $max)
+ );
+ }
+
+ /**
+ * Generates random bytes.
+ *
+ * @param integer $length Raw result length.
+ * @param boolean $raw_output Return raw result.
+ *
+ * @return SecurityGeneratorPromise
+ */
+ public static function generateBytes($length = 16, $raw_output = false)
+ {
+ return new SecurityGeneratorPromise(
+ SecurityGeneratorPromise::TYPE_BYTES,
+ array($length, $raw_output)
+ );
+ }
+
+}
Index: branches/5.2.x/core/kernel/security/SecurityGeneratorPromise.php
===================================================================
--- branches/5.2.x/core/kernel/security/SecurityGeneratorPromise.php
+++ branches/5.2.x/core/kernel/security/SecurityGeneratorPromise.php
@@ -0,0 +1,274 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2018 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+
+final class SecurityGeneratorPromise
+{
+
+ /**
+ * Promise, that returns random integer number.
+ */
+ const TYPE_NUMBER = 1;
+
+ /**
+ * Promise, that returns random string.
+ */
+ const TYPE_STRING = 2;
+
+ /**
+ * Promise, that returns random bytes.
+ */
+ const TYPE_BYTES = 3;
+
+ /**
+ * Generator method.
+ *
+ * @var string
+ */
+ protected $method;
+
+ /**
+ * Generator method arguments.
+ *
+ * @var array
+ */
+ protected $arguments = array();
+
+ /**
+ * Resolved value (only, when was resolved).
+ *
+ * @var string
+ */
+ protected $resolvedValue;
+
+ /**
+ * Return signature of resolved value.
+ *
+ * @var boolean
+ */
+ protected $asSignature = false;
+
+ /**
+ * Return raw resolved value's signature representation.
+ *
+ * @var boolean
+ */
+ protected $signatureRawOutput = false;
+
+ /**
+ * Alternative key used for creating signature of resolved value.
+ *
+ * @var string
+ */
+ protected $signatureKeyOverride;
+
+ /**
+ * SecurityGeneratorPromise constructor.
+ *
+ * @param integer $type Promise type.
+ * @param array $arguments Generator method arguments.
+ *
+ * @throws InvalidArgumentException When unsupported promise type is given.
+ */
+ public function __construct($type, array $arguments = array())
+ {
+ $mapping = array(
+ self::TYPE_NUMBER => 'generateNumber',
+ self::TYPE_STRING => 'generateString',
+ self::TYPE_BYTES => 'generateBytes',
+ );
+
+ if ( !isset($mapping[$type]) ) {
+ throw new InvalidArgumentException('The "' . $type . '" promise type is not supported.');
+ }
+
+ $this->method = $mapping[$type];
+ $this->arguments = $arguments;
+ }
+
+ /**
+ * Makes sure, that resolved value isn't already used in the given database table's column.
+ *
+ * @param string $prefix_or_table Unit config prefix or table name.
+ * @param string $column Column in specified table.
+ *
+ * @return mixed
+ * @throws LogicException When after 10 retries still unable to generate unique value for database.
+ */
+ public function resolveForPersisting($prefix_or_table, $column)
+ {
+ $application =& kApplication::Instance();
+
+ if ( $application->prefixRegistred($prefix_or_table) ) {
+ $table = $application->getUnitOption($prefix_or_table, 'TableName');
+ }
+ else {
+ $table = $prefix_or_table;
+ }
+
+ $retries = 0;
+
+ do {
+ $resolved_value = $this->resolve();
+
+ $sql = 'SELECT ' . $column . '
+ FROM ' . $table . '
+ WHERE ' . $column . ' = ' . $application->Conn->qstr($resolved_value);
+ $found = $application->Conn->GetOne($sql) !== false;
+
+ if ( $found ) {
+ $this->resolvedValue = null;
+ $retries++;
+ }
+ } while ( $found && $retries < 10 );
+
+ if ( $found ) {
+ throw new LogicException(sprintf(
+ 'Unable to generate unique value for "%s" column with current generator configuration.',
+ $table . '.' . $column
+ ));
+ }
+
+ return $resolved_value;
+ }
+
+ /**
+ * Resolves a promise.
+ *
+ * @return mixed
+ */
+ public function resolve()
+ {
+ if ( !$this->isResolved() ) {
+ $this->resolvedValue = call_user_func_array(array($this, $this->method), $this->arguments);
+ }
+
+ if ( !$this->asSignature ) {
+ return $this->resolvedValue;
+ }
+
+ $application =& kApplication::Instance();
+
+ /** @var SecurityEncrypter $encrypter */
+ $encrypter = $application->recallObject('SecurityEncrypter');
+
+ return $encrypter->createSignature(
+ $this->resolvedValue,
+ $this->signatureRawOutput,
+ $this->signatureKeyOverride
+ );
+ }
+
+ /**
+ * Configures promise to return signature of a resolved value.
+ *
+ * @param boolean $raw_output Return raw signature value during resolving.
+ * @param string|null $key_override Alternative key used for creating signature of resolved value.
+ *
+ * @return self
+ */
+ public function asSignature($raw_output = false, $key_override = null)
+ {
+ $this->asSignature = true;
+ $this->signatureRawOutput = $raw_output;
+ $this->signatureKeyOverride = $key_override;
+
+ return $this;
+ }
+
+ /**
+ * Configures promise to return resolved value as-is.
+ *
+ * @return self
+ */
+ public function asValue()
+ {
+ $this->asSignature = false;
+ $this->signatureRawOutput = false;
+ $this->signatureKeyOverride = null;
+
+ return $this;
+ }
+
+ /**
+ * Generate a random string of specified length using supplied character list.
+ *
+ * @param integer $length The length of the generated string.
+ * @param mixed $characters List of characters to use.
+ *
+ * @return string
+ */
+ protected function generateString($length, $characters)
+ {
+ $ret = '';
+ $max_index = mb_strlen($characters) - 1;
+
+ for ( $i = 0; $i < $length; $i++ ) {
+ $ret .= mb_substr($characters, $this->generateNumber(0, $max_index), 1);
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Generates a random number.
+ *
+ * @param integer $min Smallest value.
+ * @param integer $max Largest value.
+ *
+ * @return integer
+ */
+ protected function generateNumber($min, $max)
+ {
+ return random_int($min, $max);
+ }
+
+ /**
+ * Generates random bytes.
+ *
+ * @param integer $length Raw result length.
+ * @param boolean $raw_output Return raw result.
+ *
+ * @return string
+ */
+ protected function generateBytes($length = 16, $raw_output = false)
+ {
+ $ret = random_bytes($length);
+
+ return $raw_output ? $ret : bin2hex($ret);
+ }
+
+ /**
+ * Determines if promise was already resolved.
+ *
+ * @return boolean
+ */
+ protected function isResolved()
+ {
+ return $this->resolvedValue !== null;
+ }
+
+ /**
+ * Returns promise resolved value casted to string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string)$this->resolve();
+ }
+
+}
Index: branches/5.2.x/core/kernel/utility/system_config.php
===================================================================
--- branches/5.2.x/core/kernel/utility/system_config.php
+++ branches/5.2.x/core/kernel/utility/system_config.php
@@ -84,6 +84,8 @@
'WebsiteCharset' => 'utf-8',
'WebsitePath' => rtrim(preg_replace('/'.preg_quote(rtrim(defined('REL_PATH') ? REL_PATH : '', '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/'),
'WriteablePath' => DIRECTORY_SEPARATOR . 'system',
+ 'SecurityHmacKey' => '',
+ 'SecurityEncryptionKey' => '',
);
return $this->parseSections ? array('Misc' => $ret) : $ret;

Event Timeline