Merge pull request #8229 from JakubOnderka/sign-fix

chg: [sign] Simplified key handling
pull/8244/head
Jakub Onderka 2022-03-27 10:47:16 +02:00 committed by GitHub
commit 4ff7cccc7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 288 additions and 140 deletions

View File

@ -30,10 +30,6 @@ jobs:
with:
submodules: 'recursive'
# Stop mysql
- name: Shutdown Ubuntu MySQL (SUDO)
run: sudo service mysql stop
# Run mariadb
- uses: getong/mariadb-action@v1.1
with:
@ -71,7 +67,7 @@ jobs:
LC_ALL=C.UTF-8 sudo apt-add-repository ppa:ondrej/php -y
if [[ $php_version == "7.2" ]]; then
# hotfix due to: https://bugs.php.net/bug.php?id=81640 TODO: remove after libpcre2-8-0:10.36 gets to stable channel
sudo apt --fix-broken install
sudo apt-get --fix-broken install
fi
sudo apt-get -y install curl python3 python3-zmq python3-requests python3-pip python3-nose python3-redis python3-lxml apache2 libapache2-mod-php$php_version
sudo pip3 install virtualenv # virtualenv must be instaled from pip and not from ubuntu packages
@ -149,32 +145,33 @@ jobs:
- name: Configure MISP
run: |
sudo -E su $USER -c 'app/Console/cake userInit -q | sudo tee ./key.txt'
sudo -u $USER app/Console/cake userInit -q | sudo tee ./key.txt
echo "AUTH=`cat key.txt`" >> $GITHUB_ENV
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.autoRegenerate" 0'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.timeout" 600'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.cookieTimeout" 3600'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.host_org_id" 1'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.email" "info@admin.test"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.disable_emailing" false'
sudo -E su $USER -c 'app/Console/cake Admin setSetting --force "debug" true'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.CustomAuth_disable_logout" false'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.redis_host" "127.0.0.1"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.redis_port" 6379'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.redis_database" 13'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.redis_password" ""'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "GnuPG.email" "info@admin.test"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "GnuPG.homedir" "`pwd`/.gnupg"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "GnuPG.password" "travistest"'
sudo -u $USER app/Console/cake Admin setSetting "Session.autoRegenerate" 0
sudo -u $USER app/Console/cake Admin setSetting "Session.timeout" 600
sudo -u $USER app/Console/cake Admin setSetting "Session.cookieTimeout" 3600
sudo -u $USER app/Console/cake Admin setSetting "MISP.host_org_id" 1
sudo -u $USER app/Console/cake Admin setSetting "MISP.email" "info@admin.test"
sudo -u $USER app/Console/cake Admin setSetting "MISP.disable_emailing" false
sudo -u $USER app/Console/cake Admin setSetting --force "debug" true
sudo -u $USER app/Console/cake Admin setSetting "Plugin.CustomAuth_disable_logout" false
sudo -u $USER app/Console/cake Admin setSetting "MISP.redis_host" "127.0.0.1"
sudo -u $USER app/Console/cake Admin setSetting "MISP.redis_port" 6379
sudo -u $USER app/Console/cake Admin setSetting "MISP.redis_database" 13
sudo -u $USER app/Console/cake Admin setSetting "MISP.redis_password" ""
sudo -u $USER app/Console/cake Admin setSetting "GnuPG.email" "info@admin.test"
sudo -u $USER app/Console/cake Admin setSetting "GnuPG.homedir" "`pwd`/.gnupg"
sudo -u $USER app/Console/cake Admin setSetting "GnuPG.password" "travistest"
sudo -u $USER app/Console/cake Admin setSetting "MISP.download_gpg_from_homedir" 1
- name: Configure ZMQ
run: |
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_host" "127.0.0.1"'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_port" 6379'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_database" 1'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_password" ""'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_enable" 1'
sudo -E su $USER -c 'app/Console/cake Admin setSetting "Plugin.ZeroMQ_audit_notifications_enable" 1'
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_host" "127.0.0.1"
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_port" 6379
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_database" 1
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_redis_password" ""
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_enable" 1
sudo -u $USER app/Console/cake Admin setSetting "Plugin.ZeroMQ_audit_notifications_enable" 1
- name: Update Galaxies
run: sudo -E su $USER -c 'app/Console/cake Admin updateGalaxies'
@ -209,16 +206,7 @@ jobs:
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.python_bin" "$GITHUB_WORKSPACE/venv/bin/python"'
. ./venv/bin/activate
export PYTHONPATH=$PYTHONPATH:./app/files/scripts
pushd ./app/files/scripts/cti-python-stix2
pip install .
popd
pushd ./app/files/scripts/python-stix
pip install .
popd
pushd PyMISP
pip install .[fileobjects,email]
popd
pip install zmq redis plyara
pip install ./PyMISP[fileobjects,email] ./app/files/scripts/python-stix ./app/files/scripts/cti-python-stix2 zmq redis plyara
deactivate
- name: Test if apache is working
@ -242,7 +230,7 @@ jobs:
- name: Run PHP tests
run: |
./app/Vendor/bin/parallel-lint --exclude app/Lib/cakephp/ --exclude app/Vendor/ --exclude app/Lib/random_compat/ -e php,ctp app/
./app/Vendor/bin/phpunit app/Test/
sudo -u www-data ./app/Vendor/bin/phpunit app/Test/
- name: Run tests
run: |

View File

@ -592,14 +592,14 @@ class RestResponseComponent extends Component
}
if ($response instanceof TmpFileTool) {
App::uses('CakeResponseFile', 'Tools');
$cakeResponse = new CakeResponseFile(['status' => $code, 'type' => $type]);
if ($this->signContents) {
$this->CryptographicKey = ClassRegistry::init('CryptographicKey');
$data = $response->intoString();
$headers['x-pgp-signature'] = base64_encode($this->CryptographicKey->signWithInstanceKey($data));
$cakeResponse = new CakeResponse(array('body' => $data, 'status' => $code, 'type' => $type));
$cakeResponse = new CakeResponse(['body' => $data, 'status' => $code, 'type' => $type]);
} else {
App::uses('CakeResponseFile', 'Tools');
$cakeResponse = new CakeResponseFile(['status' => $code, 'type' => $type]);
$cakeResponse->file($response);
}
} else {

View File

@ -6139,7 +6139,7 @@ class EventsController extends AppController
/**
* @param array $event
* @return CakeResponseTmp
* @return CakeResponseFile
* @throws Exception
*/
private function __restResponse(array $event)
@ -6171,23 +6171,24 @@ class EventsController extends AppController
public function protect($id)
{
$this->__toggleProtect($id, true);
return $this->__toggleProtect($id, true);
}
public function unprotect($id)
{
$this->__toggleProtect($id, false);
return $this->__toggleProtect($id, false);
}
/**
* @param string|int $id Event ID or UUID
* @param bool $protect
* @return CakeResponse|void
* @throws Exception
*/
private function __toggleProtect($id, $protect)
{
$id = $this->Toolbox->findIdByUuid($this->Event, $id);
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id, ['contain' => ['Orgc']]);
if (
(!$this->_isSiteAdmin && $event['Event']['orgc_id'] !== $this->Auth->user('org_id')) ||
!$event ||
!$this->__canModifyEvent($event)
) {
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id);
if (empty($event) || !$this->__canModifyEvent($event)) {
throw new NotFoundException(__('Invalid event'));
}
if ($this->request->is('post')) {
@ -6197,7 +6198,7 @@ class EventsController extends AppController
if ($this->Event->save($event)) {
$message = __('Event switched to %s mode.', $protect ? __('protected') : __('unprotected'));
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('events', $protect ? 'protect' : 'unprotect', $id, false, $message);
return $this->RestResponse->saveSuccessResponse('events', $protect ? 'protect' : 'unprotect', $event['Event']['id'], false, $message);
} else {
$this->Flash->success($message);
$this->redirect(['controller' => 'events', 'action' => 'view', $id]);
@ -6205,14 +6206,14 @@ class EventsController extends AppController
} else {
$message = __('Something went wrong - could not switch event to %s mode.', $protect ? __('protected') : __('unprotected'));
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Events', $protect ? 'protect' : 'unprotect', false, $message, $this->response->type());
return $this->RestResponse->saveFailResponse('Events', $protect ? 'protect' : 'unprotect', $event['Event']['id'], $message);
} else {
$this->Flash->error($message);
$this->redirect(['controller' => 'events', 'action' => 'view', $id]);
$this->redirect(['controller' => 'events', 'action' => 'view', $event['Event']['id']]);
}
}
} else {
$this->set('id', $id);
$this->set('id', $event['Event']['id']);
$this->set('title', $protect ? __('Protect event') : __('Remove event protection'));
$this->set(
'question',

View File

@ -173,4 +173,21 @@ class CryptGpgExtended extends Crypt_GPG
return $armored;
}
/**
* @param mixed $data
* @param bool $isFile
* @param bool $allowEmpty
* @return resource|string|null
* @throws Crypt_GPG_FileException
* @throws Crypt_GPG_NoDataException
*/
protected function _prepareInput($data, $isFile = false, $allowEmpty = true)
{
if ($isFile && $data instanceof TmpFileTool) {
return $data->resource();
}
return parent::_prepareInput($data, $isFile, $allowEmpty);
}
}

View File

@ -1,6 +1,9 @@
<?php
class GpgTool
{
/** @var CryptGpgExtended */
private $gpg;
/**
* @return CryptGpgExtended
* @throws Exception
@ -22,19 +25,16 @@ class GpgTool
throw new Exception("Configuration option 'GnuPG.homedir' is not set, Crypt_GPG cannot be initialized.");
}
$options = array(
$options = [
'homedir' => $homedir,
'gpgconf' => Configure::read('GnuPG.gpgconf'),
'binary' => Configure::read('GnuPG.binary') ?: '/usr/bin/gpg',
);
];
return new CryptGpgExtended($options);
}
/** @var CryptGpgExtended */
private $gpg;
public function __construct($gpg)
public function __construct(CryptGpgExtended $gpg = null)
{
$this->gpg = $gpg;
}
@ -47,11 +47,13 @@ class GpgTool
public function searchGpgKey($search)
{
$uri = 'https://openpgp.circl.lu/pks/lookup?search=' . urlencode($search) . '&op=index&fingerprint=on&options=mr';
$response = $this->keyServerLookup($uri);
if ($response->code == 404) {
return array(); // no keys found
} else if ($response->code != 200) {
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
try {
$response = $this->keyServerLookup($uri);
} catch (HttpSocketHttpException $e) {
if ($e->getCode() === 404) {
return [];
}
throw $e;
}
return $this->extractKeySearch($response->body);
}
@ -64,11 +66,13 @@ class GpgTool
public function fetchGpgKey($fingerprint)
{
$uri = 'https://openpgp.circl.lu/pks/lookup?search=0x' . urlencode($fingerprint) . '&op=get&options=mr';
$response = $this->keyServerLookup($uri);
if ($response->code == 404) {
return null; // key with given fingerprint not found
} else if ($response->code != 200) {
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
try {
$response = $this->keyServerLookup($uri);
} catch (HttpSocketHttpException $e) {
if ($e->getCode() === 404) {
return null;
}
throw $e;
}
$key = $response->body;
@ -169,30 +173,20 @@ class GpgTool
$advancedUrl = "https://openpgpkey.$domain/.well-known/openpgpkey/" . strtolower($domain) . "/hu/$localPartHash";
try {
$response = $this->keyServerLookup($advancedUrl);
return $this->processWkdResponse($response);
return $this->gpg->enarmor($response->body());
} catch (Exception $e) {
// pass, continue to direct method
}
$directUrl = "https://$domain/.well-known/openpgpkey/hu/$localPartHash";
$response = $this->keyServerLookup($directUrl);
return $this->processWkdResponse($response);
}
/**
* @param HttpSocketResponse $response
* @return string
* @throws Crypt_GPG_Exception
* @throws Crypt_GPG_InvalidOperationException
*/
private function processWkdResponse(HttpSocketResponse $response)
{
if ($response->code == 404) {
throw new NotFoundException("Key not found");
} else if (!$response->isOk()) {
throw new Exception("Fetching the WKD failed with HTTP error {$response->code}: {$response->reasonPhrase}");
try {
$response = $this->keyServerLookup($directUrl);
} catch (HttpSocketHttpException $e) {
if ($e->getCode() === 404) {
throw new NotFoundException("Key not found");
}
throw $e;
}
return $this->gpg->enarmor($response->body());
}
@ -232,17 +226,18 @@ class GpgTool
/**
* @param string $uri
* @return HttpSocketResponse
* @return HttpSocketResponseExtended
* @throws HttpSocketHttpException
* @throws Exception
*/
private function keyServerLookup($uri)
{
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocket();
$HttpSocket = $syncTool->createHttpSocket(['compress' => true]);
$response = $HttpSocket->get($uri);
if ($response === false) {
throw new Exception("Could not fetch '$uri'.");
if (!$response->isOk()) {
throw new HttpSocketHttpException($response, $uri);
}
return $response;
}

View File

@ -93,7 +93,7 @@ class JSONConverterTool
if ($raw) {
return $result;
}
return json_encode($result, JSON_PRETTY_PRINT);
return json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
/**
@ -127,7 +127,7 @@ class JSONConverterTool
}
}
if (isset($event['errors'])) {
yield '},"errors":' . json_encode($event['errors']) . '}';
yield '},"errors":' . json_encode($event['errors'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . '}';
} else {
yield "}}";
}

View File

@ -20,6 +20,9 @@ class ServerSyncTool
/** @var HttpSocketExtended */
private $socket;
/** @var CryptographicKey */
private $cryptographicKey;
/** @var array|null */
private $info;
@ -418,8 +421,10 @@ class ServerSyncTool
throw new Exception(__('Remote instance is not protected event aware yet (< 2.4.156), aborting.'));
}
$this->CryptographicKey = ClassRegistry::init('CryptographicKey');
$signature = $this->CryptographicKey->signWithInstanceKey($data);
if (!$this->cryptographicKey) {
$this->cryptographicKey = ClassRegistry::init('CryptographicKey');
}
$signature = $this->cryptographicKey->signWithInstanceKey($data);
if (empty($signature)) {
throw new Exception(__("Invalid signing key. This should never happen."));
}

View File

@ -1,14 +1,14 @@
<?php
class TmpFileTool
{
/** @var resource */
/** @var resource|null */
private $tmpfile;
/** @var string */
private $separator;
/**
* @param int $maxInMemory How many bytes should keep in memory before creating file on disk. By default is is 2 MB.
* @param int $maxInMemory How many bytes should keep in memory before creating file on disk. By default is is 5 MB.
* @throws Exception
*/
public function __construct($maxInMemory = null)
@ -144,20 +144,17 @@ class TmpFileTool
}
/**
* @param boolean $close
* @return string
* @throws Exception
*/
public function intoString($close = true)
public function intoString()
{
$this->rewind();
$string = stream_get_contents($this->tmpfile);
if ($string === false) {
throw new Exception('Could not read from temporary file.');
}
if ($close) {
$this->close();
}
$this->close();
return $string;
}
@ -175,6 +172,16 @@ class TmpFileTool
$this->close();
}
/**
* @return resource
* @throws Exception
*/
public function resource()
{
$this->rewind();
return $this->tmpfile;
}
/**
* @return int
* @throws Exception

View File

@ -35,6 +35,9 @@ class CryptographicKey extends AppModel
public $validate = [];
/** @var CryptGpgExtended|null */
private $gpg;
public function __construct($id = false, $table = null, $ds = null)
{
parent::__construct($id, $table, $ds);
@ -44,6 +47,12 @@ class CryptographicKey extends AppModel
$this->gpg = null;
}
$this->validate = [
'uuid' => [
'uuid' => [
'rule' => 'uuid',
'message' => 'Please provide a valid RFC 4122 UUID',
],
],
'type' => [
'rule' => ['inList', $this->validTypes],
'message' => __('Invalid key type'),
@ -76,16 +85,32 @@ class CryptographicKey extends AppModel
$this->data['CryptographicKey']['uuid'] = CakeText::uuid();
$this->data['CryptographicKey']['fingerprint'] = $this->extractKeyData($this->data['CryptographicKey']['type'], $this->data['CryptographicKey']['key_data']);
}
$existingKeyForObject = $this->find('first', [
'recursive'
]);
return true;
}
/**
* @return string Instance key fingerprint
* @throws Crypt_GPG_BadPassphraseException
* @throws Crypt_GPG_Exception
*/
public function ingestInstanceKey()
{
// If instance just key stored just in GPG homedir, use that key.
if (Configure::read('MISP.download_gpg_from_homedir')) {
if (!$this->gpg) {
throw new Exception("Could not initiate GPG");
}
/** @var Crypt_GPG_Key[] $keys */
$keys = $this->gpg->getKeys(Configure::read('GnuPG.email'));
if (empty($keys)) {
return false;
}
$this->gpg->addSignKey($keys[0], Configure::read('GnuPG.password'));
return $keys[0]->getPrimaryKey()->getFingerprint();
}
try {
$redis = $this->setupRedis();
$redis = $this->setupRedisWithException();
} catch (Exception $e) {
$redis = false;
}
@ -123,25 +148,29 @@ class CryptographicKey extends AppModel
return $fingerprint;
}
/**
* @param string $data
* @return false|string
* @throws Crypt_GPG_BadPassphraseException
* @throws Crypt_GPG_Exception
* @throws Crypt_GPG_KeyNotFoundException
*/
public function signWithInstanceKey($data)
{
if (!$this->ingestInstanceKey()) {
return false;
}
$data = preg_replace("/\s+/", "", $data);
$signature = $this->gpg->sign($data, Crypt_GPG::SIGN_MODE_DETACHED);
return $signature;
}
public function signFileWithInstanceKey($path)
{
if (!$this->ingestInstanceKey()) {
return false;
}
$signature = $this->gpg->signFile($path, Crypt_GPG::SIGN_MODE_DETACHED);
$signature = $this->gpg->sign($data, Crypt_GPG::SIGN_MODE_DETACHED, Crypt_GPG::ARMOR_BINARY);
return $signature;
}
/**
* @param string $data
* @param string $signature
* @param string $key
* @return bool
*/
public function verifySignature($data, $signature, $key)
{
$this->error = false;
@ -217,20 +246,15 @@ class CryptographicKey extends AppModel
public function uniqueKeyForElement($data)
{
$existingKey = $this->find('first', [
'recursive' => -1,
'conditions' => [
'parent_type' => $this->data['CryptographicKey']['parent_type'],
'parent_id' => $this->data['CryptographicKey']['parent_id'],
'key_data' => $this->data['CryptographicKey']['key_data'],
'type' => $this->data['CryptographicKey']['type']
],
'fields' => ['id']
return !$this->hasAny([
'parent_type' => $this->data['CryptographicKey']['parent_type'],
'parent_id' => $this->data['CryptographicKey']['parent_id'],
'key_data' => $this->data['CryptographicKey']['key_data'],
'type' => $this->data['CryptographicKey']['type'],
]);
return empty($existingKey);
}
public function validateProtectedEvent($raw_data, $user, $pgp_signature, $event)
public function validateProtectedEvent($raw_data, array $user, $pgp_signature, array $event)
{
$eventCryptoGraphicKey = [];
if (!empty($event['Event']['CryptographicKey'])) { // Depending if $event comes from fetchEvent or from pushed data
@ -240,8 +264,7 @@ class CryptographicKey extends AppModel
}
if (empty($eventCryptoGraphicKey)) {
$message = __('No valid signatures found for validating the signature.');
$this->Log = ClassRegistry::init('Log');
$this->Log->createLogEntry($user, 'validateSig', 'Event', $event['Event']['id'], $message);
$this->loadLog()->createLogEntry($user, 'validateSig', 'Event', $event['Event']['id'], $message);
return false;
}
foreach ($eventCryptoGraphicKey as $supplied_key) {
@ -249,19 +272,26 @@ class CryptographicKey extends AppModel
return true;
}
}
$this->Log = ClassRegistry::init('Log');
$message = __('Could not validate the signature.');
$this->Log->createLogEntry($user, 'validateSig', 'Event', $event['Event']['id'], $message);
$this->loadLog()->createLogEntry($user, 'validateSig', 'Event', $event['Event']['id'], $message);
return false;
}
public function captureCryptographicKeyUpdate($user, $cryptographicKeys, $parent_id, $type)
/**
* @param array $user
* @param array $cryptographicKeys
* @param int $parent_id
* @param string $type
* @return void
* @throws Exception
*/
public function captureCryptographicKeyUpdate(array $user, array $cryptographicKeys, $parent_id, $type)
{
$existingKeys = $this->find('first', [
'recursive' => -1,
'conditions' => [
'parent_type' => $type,
'parent_id' => $parent_id
'parent_id' => $parent_id,
],
'fields' => [
'id',
@ -269,15 +299,14 @@ class CryptographicKey extends AppModel
'parent_type',
'parent_id',
'revoked',
'fingerprint'
'fingerprint',
]
]);
$toRemove = [];
$results = ['add' => [], 'remove' => []];
foreach ($existingKeys as $k => $existingKey) {
foreach ($existingKeys as $existingKey) {
foreach ($cryptographicKeys as $k2 => $cryptographicKey) {
if ($existingKey['fingerprint'] === $cryptographicKey['fingerprint']) {
$found = true;
if ($cryptographicKey['revoked'] && !$existingKey['CryptographicKey']['revoked']) {
$existingKey['CryptographicKey']['revoked'] = 1;
$this->save($existingKey['CryptographicKey']);
@ -314,7 +343,6 @@ class CryptographicKey extends AppModel
$parent_id
);
$this->deleteAll(['CryptographicKey.id' => $toRemove]);
$this->Log = ClassRegistry::init('Log');
$this->Log->createLogEntry($user, 'updateCryptoKeys', $cryptographicKey['parent_type'], $cryptographicKey['parent_id'], $message);
$this->loadLog()->createLogEntry($user, 'updateCryptoKeys', $cryptographicKey['parent_type'], $cryptographicKey['parent_id'], $message);
}
}

View File

@ -5666,6 +5666,15 @@ class Server extends AppModel
'type' => 'boolean',
'null' => true,
],
'download_gpg_from_homedir' => [
'level' => self::SETTING_OPTIONAL,
'description' => __('Fetch GPG instance key from GPG homedir.'),
'value' => false,
'test' => 'testBool',
'type' => 'boolean',
'null' => true,
'cli_only' => true,
],
),
'GnuPG' => array(
'branch' => 1,

View File

@ -0,0 +1,65 @@
<?php
require_once __DIR__ . '/../Lib/Tools/GpgTool.php';
require_once __DIR__ . '/../Lib/Tools/TmpFileTool.php';
require_once __DIR__ . '/../Lib/Tools/CryptGpgExtended.php';
use PHPUnit\Framework\TestCase;
class GpgToolTest extends TestCase
{
public function testInit(): void
{
$gpg = $this->init();
$this->assertInstanceOf('CryptGpgExtended', $gpg);
$this->assertIsString($gpg->getVersion());
}
public function testSignAndVerify()
{
$gpg = $this->init();
include __DIR__ . '/../Config/config.php';
$gpg->addSignKey($config['GnuPG']['email'], $config['GnuPG']['password']);
$testString = 'ahojSvete';
$signature = $gpg->sign($testString, Crypt_GPG::SIGN_MODE_DETACHED, Crypt_GPG::ARMOR_BINARY);
$this->assertIsString($signature);
$verified = $gpg->verify($testString, $signature);
$this->assertIsArray($verified);
$this->assertCount(1, $verified);
$this->assertTrue($verified[0]->isValid());
$signature = $gpg->sign($testString, Crypt_GPG::SIGN_MODE_DETACHED, Crypt_GPG::ARMOR_ASCII);
$this->assertIsString($signature);
$verified = $gpg->verify($testString, $signature);
$this->assertIsArray($verified);
$this->assertCount(1, $verified);
$this->assertTrue($verified[0]->isValid());
// Tmp file
$tmpFile = new TmpFileTool();
$tmpFile->write($testString);
$signature = $gpg->signFile($tmpFile, null, Crypt_GPG::SIGN_MODE_DETACHED, Crypt_GPG::ARMOR_BINARY);
$this->assertIsString($signature);
$verified = $gpg->verify($testString, $signature);
$this->assertIsArray($verified);
$this->assertCount(1, $verified);
$this->assertTrue($verified[0]->isValid());
}
private function init(): CryptGpgExtended
{
require_once 'Crypt/GPG.php';
include __DIR__ . '/../Config/config.php';
$options = [
'homedir' => $config['GnuPG']['homedir'],
'gpgconf' => $config['GnuPG']['gpgconf'] ?? null,
'binary' => $config['GnuPG']['binary'] ?? '/usr/bin/gpg',
];
return new CryptGpgExtended($options);
}
}

View File

@ -35,12 +35,28 @@ class JSONConverterToolTest extends TestCase
$this->check($event);
}
public function testCheckJsonIsValidUnicodeSlashes(): void
{
$attribute = ['id' => 1, 'event_id' => 2, 'type' => 'ip-src', 'value' => '1.1.1.1'];
$event = ['Event' => ['id' => 2, 'info' => 'Test event ěšřžýáí \/'], 'errors' => 'chyba ě+š'];
for ($i = 0; $i < 5; $i++) {
$event['Attribute'][] = $attribute;
}
$this->check($event);
}
private function check(array $event): void
{
$json = '';
foreach (JSONConverterTool::streamConvert($event) as $part) {
$json .= $part;
}
// Check if result is the same without spaces
$jsonStreamWithoutSpaces = preg_replace("/\s+/", "", $json);
$jsonNormalWithoutSpaces = preg_replace("/\s+/", "", JSONConverterTool::convert($event));
$this->assertEquals($jsonNormalWithoutSpaces, $jsonStreamWithoutSpaces);
if (defined('JSON_THROW_ON_ERROR')) {
json_decode($json, true, 512, JSON_THROW_ON_ERROR);
$this->assertTrue(true);

View File

@ -719,6 +719,23 @@ class TestComprehensive(unittest.TestCase):
response = request(self.admin_misp_connector, 'POST', f'warninglists/delete/{wl["Warninglist"]["id"]}')
check_response(response)
def test_protected_event(self):
event = create_simple_event()
event = check_response(self.admin_misp_connector.add_event(event))
response = request(self.admin_misp_connector, 'POST', f'events/protect/{event.id}')
check_response(response)
response = request(self.admin_misp_connector, 'POST', f'events/unprotect/{event.uuid}')
check_response(response)
response = request(self.admin_misp_connector, 'POST', f'events/protect/{event.uuid}')
check_response(response)
response = self.admin_misp_connector._prepare_request('GET', f'events/view/{event.id}')
self.assertIn('x-pgp-signature', response.headers)
self.assertTrue(len(response.headers['x-pgp-signature']) > 0, response.headers['x-pgp-signature'])
def _search(self, query: dict):
response = self.admin_misp_connector._prepare_request('POST', 'events/restSearch', data=query)
response = self.admin_misp_connector._check_response(response)