Skip to content
Snippets Groups Projects
Commit 8846c128 authored by totten's avatar totten
Browse files

Crypto - Define CIVICRM_SIGN_KEYS as way to register signing keys

parent 9c976d32
Branches
Tags
No related merge requests found
......@@ -29,6 +29,15 @@ class CRM_Upgrade_Incremental_php_FiveThirtySix extends CRM_Upgrade_Incremental_
// if ($rev == '5.12.34') {
// $preUpgradeMessage .= '<p>' . ts('A new permission, "%1", has been added. This permission is now used to control access to the Manage Tags screen.', array(1 => ts('manage tags'))) . '</p>';
// }
if ($rev === '5.36.alpha1') {
if (empty(CRM_Utils_Constant::value('CIVICRM_SIGN_KEYS'))) {
// NOTE: We don't re-encrypt automatically because the old "civicrm.settings.php" lacks a good key, and we don't keep the old encryption because the format is ambiguous.
// The admin may forget to re-enable. That's OK -- this only affects 1 field, this is a secondary defense, and (in the future) we can remind the admin via status-checks.
$preUpgradeMessage .= '<p>' . ts('CiviCRM v5.36 introduces a new configuration option to support digital signatures. You may <a href="%1" target="_blank">setup CIVICRM_SIGN_KEYS</a> before or after upgrading. The option is not critical in v5.36, but it may be required for extensions or future upgrades.', [
1 => 'https://docs.civicrm.org/sysadmin/en/latest/upgrade/version-specific/#sign-key',
]) . '</p>';
}
}
}
/**
......
......@@ -78,6 +78,13 @@ class CryptoRegistry {
}
}
if (defined('CIVICRM_SIGN_KEYS') && CIVICRM_SIGN_KEYS !== '') {
foreach (explode(' ', CIVICRM_SIGN_KEYS) as $n => $keyExpr) {
$key = ['tags' => ['SIGN'], 'weight' => $n];
$registry->addSymmetricKey($registry->parseKey($keyExpr) + $key);
}
}
//if (isset($_COOKIE['CIVICRM_FORM_KEY'])) {
// $crypto->addSymmetricKey([
// 'key' => base64_decode($_COOKIE['CIVICRM_FORM_KEY']),
......
<?php
/**
* @file
*
* Generate the signing key(s).
*/
if (!defined('CIVI_SETUP')) {
exit("Installation plugins must only be loaded by the installer.\n");
}
\Civi\Setup::dispatcher()
->addListener('civi.setup.installFiles', function (\Civi\Setup\Event\InstallFilesEvent $e) {
\Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'installFiles'));
$toAlphanum = function($bits) {
return preg_replace(';[^a-zA-Z0-9];', '', base64_encode($bits));
};
if (empty($e->getModel()->signKeys)) {
$e->getModel()->signKeys = ['jwt-hs256:hkdf-sha256:' . $toAlphanum(random_bytes(40))];
// toAlpanum() occasionally loses a few bits of entropy, but random_bytes() has significant excess, so it's still more than ample for 256 bit hkdf.
}
if (is_string($e->getModel()->signKeys)) {
$e->getModel()->signKeys = [$e->getModel()->signKeys];
}
\Civi\Setup::log()->info(sprintf('[%s] Done %s', basename(__FILE__), 'installFiles'));
}, \Civi\Setup::PRIORITY_PREPARE);
......@@ -99,6 +99,7 @@ if (!defined('CIVI_SETUP')) {
$params['CMSdbSSL'] = empty($m->cmsDb['ssl_params']) ? '' : addslashes('&' . http_build_query($m->cmsDb['ssl_params'], '', '&', PHP_QUERY_RFC3986));
$params['siteKey'] = addslashes($m->siteKey);
$params['credKeys'] = addslashes(implode(' ', $m->credKeys));
$params['signKeys'] = addslashes(implode(' ', $m->signKeys));
$extraSettings = array();
......
......@@ -30,6 +30,8 @@ namespace Civi\Setup;
* Ex: 'abcd1234ABCD9876'.
* @property string[] $credKeys
* Ex: ['::abcd1234ABCD9876'].
* @property string[] $signKeys
* Ex: ['jwt-hs256::abcd1234ABCD9876'].
* @property string|NULL $lang
* The language of the default dataset.
* Ex: 'fr_FR'.
......@@ -116,6 +118,11 @@ class Model {
'name' => 'credKeys',
'type' => 'array',
));
$this->addField(array(
'description' => 'Signing keys',
'name' => 'signKeys',
'type' => 'array',
));
$this->addField(array(
'description' => 'Load example data',
'name' => 'loadGenerated',
......
......@@ -324,6 +324,27 @@ if (!defined('CIVICRM_CRED_KEYS') ) {
// Feel free to simplify post-install.
}
/**
* The signing key is used to generate and verify shareable tokens.
*
* This is a space-delimited list of keys (ordered by priority). Put the preferred
* key first. Any old/deprecated keys may be listed after.
*
* Each key is in format "<cipher-suite>:<key-encoding>:<key-content>", as in:
*
* Ex: define('CIVICRM_SIGN_KEYS', 'jwt-hs256:hkdf-sha256:RANDOM_1')
* Ex: define('CIVICRM_SIGN_KEYS', 'jwt-hs256::RANDOM_2 jwt-hs256::RANDOM_3')
* Ex: define('CIVICRM_SIGN_KEYS', 'jwt-hs256:b64:RANDOM_4 jwt-hs256:b64:RANDOM_5')
*
* If key-encoding is blank, it will default to "hkdf-sha256".
*/
if (!defined('CIVICRM_SIGN_KEYS') ) {
define( '_CIVICRM_SIGN_KEYS', '%%signKeys%%');
define( 'CIVICRM_SIGN_KEYS', _CIVICRM_SIGN_KEYS === '%%' . 'signKeys' . '%%' ? '' : _CIVICRM_SIGN_KEYS );
// Some old installers may not set a decent value, and this extra complexity is a failsafe.
// Feel free to simplify post-install.
}
/**
* Enable this constant, if you want to send your email through the smarty
* templating engine(allows you to do conditional and more complex logic)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment