Page MenuHomeIn-Portal Phabricator

D354.id887.diff
No OneTemporary

File Metadata

Created
Wed, May 7, 9:54 PM

D354.id887.diff

Index: composer.json
===================================================================
--- composer.json
+++ composer.json
@@ -1,5 +1,11 @@
{
"name": "In-Portal",
+ "require": {
+ "php": ">=5.3.3",
+ "paragonie/random_compat": "^2.0",
+ "symfony/polyfill-php55": "^1.10",
+ "symfony/polyfill-php56": "^1.10"
+ },
"require-dev": {
"aik099/phpunit-mink": "~2.0",
"qa-tools/qa-tools": "~1.0",
@@ -7,5 +13,10 @@
"mockery/mockery": "~0.9",
"behat/mink": "~1.6"
+ },
+ "config": {
+ "platform": {
+ "php": "5.3.3"
+ }
}
}
Index: composer.lock
===================================================================
--- composer.lock
+++ composer.lock
@@ -1,11 +1,267 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "hash": "eb74d1a9944355d24b1a70b7895b60c5",
- "packages": [],
+ "content-hash": "c81a156419a19e712f85783a7ad6df76",
+ "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.17",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d",
+ "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d",
+ "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": "2018-07-04T16:31:37+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php55",
+ "version": "v1.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php55.git",
+ "reference": "42a4c00a347625ac8853c3358c47eeadc7fd4e96"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/42a4c00a347625ac8853c3358c47eeadc7fd4e96",
+ "reference": "42a4c00a347625ac8853c3358c47eeadc7fd4e96",
+ "shasum": ""
+ },
+ "require": {
+ "ircmaxell/password-compat": "~1.0",
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "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"
+ ],
+ "time": "2018-10-31T12:13:01+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php56",
+ "version": "v1.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php56.git",
+ "reference": "ff208829fe1aa48ab9af356992bb7199fed551af"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/ff208829fe1aa48ab9af356992bb7199fed551af",
+ "reference": "ff208829fe1aa48ab9af356992bb7199fed551af",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "symfony/polyfill-util": "~1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "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"
+ ],
+ "time": "2018-09-21T06:26:08+00:00"
+ },
+ {
+ "name": "symfony/polyfill-util",
+ "version": "v1.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-util.git",
+ "reference": "3b58903eae668d348a7126f999b0da0f2f93611c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/3b58903eae668d348a7126f999b0da0f2f93611c",
+ "reference": "3b58903eae668d348a7126f999b0da0f2f93611c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "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"
+ ],
+ "time": "2018-09-30T16:36:12+00:00"
+ }
+ ],
"packages-dev": [
{
"name": "aik099/coding-standard",
@@ -17,7 +273,7 @@
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aik099/CodingStandard/zipball/987a6781521c9293b3e36ff3877cdff63010b291",
+ "url": "https://api.github.com/repos/aik099/CodingStandard/zipball/a47f2b767c99a33f437802b3315179887018e114",
"reference": "a47f2b767c99a33f437802b3315179887018e114",
"shasum": ""
},
@@ -45,7 +301,7 @@
"PHP_CodeSniffer",
"codesniffer"
],
- "time": "2015-05-06 08:54:38"
+ "time": "2015-05-06T08:54:38+00:00"
},
{
"name": "aik099/phpunit-mink",
@@ -105,7 +361,7 @@
"selenium",
"tests"
],
- "time": "2015-05-06 13:33:55"
+ "time": "2015-05-06T13:33:55+00:00"
},
{
"name": "behat/mink",
@@ -160,7 +416,7 @@
"testing",
"web"
],
- "time": "2015-02-04 17:02:06"
+ "time": "2015-02-04T17:02:06+00:00"
},
{
"name": "behat/mink-selenium2-driver",
@@ -218,7 +474,7 @@
"testing",
"webdriver"
],
- "time": "2014-09-29 13:12:12"
+ "time": "2014-09-29T13:12:12+00:00"
},
{
"name": "doctrine/instantiator",
@@ -272,7 +528,7 @@
"constructor",
"instantiate"
],
- "time": "2014-10-13 12:58:55"
+ "time": "2014-10-13T12:58:55+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@@ -317,7 +573,7 @@
"keywords": [
"test"
],
- "time": "2015-05-11 14:41:42"
+ "time": "2015-05-11T14:41:42+00:00"
},
{
"name": "instaclick/php-webdriver",
@@ -375,7 +631,7 @@
"webdriver",
"webtest"
],
- "time": "2015-04-05 19:52:55"
+ "time": "2015-04-05T19:52:55+00:00"
},
{
"name": "mindplay/annotations",
@@ -425,19 +681,19 @@
"annotations",
"framework"
],
- "time": "2015-04-15 12:54:35"
+ "time": "2015-04-15T12:54:35+00:00"
},
{
"name": "mockery/mockery",
"version": "0.9.4",
"source": {
"type": "git",
- "url": "https://github.com/padraic/mockery.git",
+ "url": "https://github.com/mockery/mockery.git",
"reference": "70bba85e4aabc9449626651f48b9018ede04f86b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/padraic/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b",
"reference": "70bba85e4aabc9449626651f48b9018ede04f86b",
"shasum": ""
},
@@ -490,7 +746,7 @@
"test double",
"testing"
],
- "time": "2015-04-02 19:54:00"
+ "time": "2015-04-02T19:54:00+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
@@ -539,7 +795,7 @@
"email": "mike.vanriel@naenius.com"
}
],
- "time": "2015-02-03 12:10:50"
+ "time": "2015-02-03T12:10:50+00:00"
},
{
"name": "phpspec/prophecy",
@@ -599,7 +855,7 @@
"spy",
"stub"
],
- "time": "2015-04-27 22:15:08"
+ "time": "2015-04-27T22:15:08+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -661,7 +917,7 @@
"testing",
"xunit"
],
- "time": "2015-04-11 04:35:00"
+ "time": "2015-04-11T04:35:00+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -708,7 +964,7 @@
"filesystem",
"iterator"
],
- "time": "2015-04-02 05:19:05"
+ "time": "2015-04-02T05:19:05+00:00"
},
{
"name": "phpunit/php-text-template",
@@ -752,7 +1008,7 @@
"keywords": [
"template"
],
- "time": "2014-01-30 17:20:04"
+ "time": "2014-01-30T17:20:04+00:00"
},
{
"name": "phpunit/php-timer",
@@ -796,7 +1052,7 @@
"keywords": [
"timer"
],
- "time": "2013-08-02 07:42:54"
+ "time": "2013-08-02T07:42:54+00:00"
},
{
"name": "phpunit/php-token-stream",
@@ -845,7 +1101,7 @@
"keywords": [
"tokenizer"
],
- "time": "2015-04-08 04:46:07"
+ "time": "2015-04-08T04:46:07+00:00"
},
{
"name": "phpunit/phpunit",
@@ -917,7 +1173,7 @@
"testing",
"xunit"
],
- "time": "2015-04-29 15:18:52"
+ "time": "2015-04-29T15:18:52+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@@ -972,7 +1228,7 @@
"mock",
"xunit"
],
- "time": "2015-04-02 05:36:41"
+ "time": "2015-04-02T05:36:41+00:00"
},
{
"name": "pimple/pimple",
@@ -1018,7 +1274,7 @@
"container",
"dependency injection"
],
- "time": "2014-07-24 09:48:15"
+ "time": "2014-07-24T09:48:15+00:00"
},
{
"name": "qa-tools/qa-tools",
@@ -1091,7 +1347,7 @@
"phpunit",
"tests"
],
- "time": "2014-09-26 15:15:51"
+ "time": "2014-09-26T15:15:51+00:00"
},
{
"name": "sebastian/comparator",
@@ -1155,7 +1411,7 @@
"compare",
"equality"
],
- "time": "2015-01-29 16:28:08"
+ "time": "2015-01-29T16:28:08+00:00"
},
{
"name": "sebastian/diff",
@@ -1207,7 +1463,7 @@
"keywords": [
"diff"
],
- "time": "2015-02-22 15:13:53"
+ "time": "2015-02-22T15:13:53+00:00"
},
{
"name": "sebastian/environment",
@@ -1257,7 +1513,7 @@
"environment",
"hhvm"
],
- "time": "2015-01-01 10:01:08"
+ "time": "2015-01-01T10:01:08+00:00"
},
{
"name": "sebastian/exporter",
@@ -1323,7 +1579,7 @@
"export",
"exporter"
],
- "time": "2015-01-27 07:23:06"
+ "time": "2015-01-27T07:23:06+00:00"
},
{
"name": "sebastian/global-state",
@@ -1374,7 +1630,7 @@
"keywords": [
"global state"
],
- "time": "2014-10-06 09:23:50"
+ "time": "2014-10-06T09:23:50+00:00"
},
{
"name": "sebastian/recursion-context",
@@ -1427,7 +1683,7 @@
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
- "time": "2015-01-24 09:48:32"
+ "time": "2015-01-24T09:48:32+00:00"
},
{
"name": "sebastian/version",
@@ -1462,7 +1718,7 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
- "time": "2015-02-24 06:35:25"
+ "time": "2015-02-24T06:35:25+00:00"
},
{
"name": "symfony/css-selector",
@@ -1470,12 +1726,12 @@
"target-dir": "Symfony/Component/CssSelector",
"source": {
"type": "git",
- "url": "https://github.com/symfony/CssSelector.git",
+ "url": "https://github.com/symfony/css-selector.git",
"reference": "189cf0f7f56d7c4be3b778df15a7f16a29f3680d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/CssSelector/zipball/189cf0f7f56d7c4be3b778df15a7f16a29f3680d",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/189cf0f7f56d7c4be3b778df15a7f16a29f3680d",
"reference": "189cf0f7f56d7c4be3b778df15a7f16a29f3680d",
"shasum": ""
},
@@ -1516,7 +1772,7 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
- "time": "2015-05-02 15:18:45"
+ "time": "2015-05-02T15:18:45+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -1524,12 +1780,12 @@
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
- "url": "https://github.com/symfony/EventDispatcher.git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
"reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/672593bc4b0043a0acf91903bb75a1c82d8f2e02",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/672593bc4b0043a0acf91903bb75a1c82d8f2e02",
"reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02",
"shasum": ""
},
@@ -1575,7 +1831,7 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2015-05-02 15:18:45"
+ "time": "2015-05-02T15:18:45+00:00"
},
{
"name": "symfony/yaml",
@@ -1625,7 +1881,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2015-05-02 15:18:45"
+ "time": "2015-05-02T15:18:45+00:00"
}
],
"aliases": [],
@@ -1635,6 +1891,11 @@
},
"prefer-stable": false,
"prefer-lowest": false,
- "platform": [],
- "platform-dev": []
+ "platform": {
+ "php": ">=5.3.3"
+ },
+ "platform-dev": [],
+ "platform-overrides": {
+ "php": "5.3.3"
+ }
}
Index: core/install.php
===================================================================
--- core/install.php
+++ 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');
@@ -836,6 +836,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: core/install/prerequisites.php
===================================================================
--- core/install/prerequisites.php
+++ 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: core/install/step_templates/sys_requirements.tpl
===================================================================
--- core/install/step_templates/sys_requirements.tpl
+++ 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: core/kernel/application.php
===================================================================
--- core/kernel/application.php
+++ 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: core/kernel/security/SecurityEncrypter.php
===================================================================
--- /dev/null
+++ 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: core/kernel/security/SecurityGenerator.php
===================================================================
--- /dev/null
+++ 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: core/kernel/security/SecurityGeneratorPromise.php
===================================================================
--- /dev/null
+++ 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: core/kernel/utility/system_config.php
===================================================================
--- core/kernel/utility/system_config.php
+++ 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