Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F847886
D354.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Apr 19, 7:24 PM
Size
33 KB
Mime Type
text/x-diff
Expires
Sun, Apr 20, 7:24 PM (40 m, 19 s)
Engine
blob
Format
Raw Data
Handle
602512
Attached To
D354: INP-1756 - Create "Security*" classes for security-related jobs
D354.diff
View Options
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
Log In to Comment