Add a secret to datastore encryptor

This change enables the encryption/decryption of mongo key with a custom secret
This commit is contained in:
VakarisZ 2021-09-30 12:02:43 +03:00
parent 191fbea665
commit fd1cb9d36d
6 changed files with 38 additions and 47 deletions

View File

@ -1,3 +1,4 @@
import io
import os import os
# PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but # PyCrypto is deprecated, but we use pycryptodome, which uses the exact same imports but
@ -5,6 +6,9 @@ import os
from Crypto import Random # noqa: DUO133 # nosec: B413 from Crypto import Random # noqa: DUO133 # nosec: B413
from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor from monkey_island.cc.server_utils.encryption import KeyBasedEncryptor
from monkey_island.cc.server_utils.encryption.password_based_byte_encryption import (
PasswordBasedByteEncryptor,
)
from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file
_encryptor = None _encryptor = None
@ -14,24 +18,30 @@ class DataStoreEncryptor:
_BLOCK_SIZE = 32 _BLOCK_SIZE = 32
_KEY_FILENAME = "mongo_key.bin" _KEY_FILENAME = "mongo_key.bin"
def __init__(self, key_file_dir): def __init__(self, key_file_dir: str, secret: str):
key_file = os.path.join(key_file_dir, self._KEY_FILENAME) key_file = os.path.join(key_file_dir, self._KEY_FILENAME)
if os.path.exists(key_file): if os.path.exists(key_file):
self._load_existing_key(key_file) self._load_existing_key(key_file, secret)
else: else:
self._init_key(key_file) self._init_key(key_file, secret)
self._key_base_encryptor = KeyBasedEncryptor(self._cipher_key) self._key_base_encryptor = KeyBasedEncryptor(self._cipher_key)
def _init_key(self, password_file_path: str): def _init_key(self, password_file_path: str, secret: str):
self._cipher_key = Random.new().read(self._BLOCK_SIZE) self._cipher_key = Random.new().read(self._BLOCK_SIZE)
encrypted_key = (
PasswordBasedByteEncryptor(secret).encrypt(io.BytesIO(self._cipher_key)).getvalue()
)
with open_new_securely_permissioned_file(password_file_path, "wb") as f: with open_new_securely_permissioned_file(password_file_path, "wb") as f:
f.write(self._cipher_key) f.write(encrypted_key)
def _load_existing_key(self, key_file): def _load_existing_key(self, key_file_path: str, secret: str):
with open(key_file, "rb") as f: with open(key_file_path, "rb") as f:
self._cipher_key = f.read() encrypted_key = f.read()
self._cipher_key = (
PasswordBasedByteEncryptor(secret).decrypt(io.BytesIO(encrypted_key)).getvalue()
)
def enc(self, message: str): def enc(self, message: str):
return self._key_base_encryptor.encrypt(message) return self._key_base_encryptor.encrypt(message)
@ -40,10 +50,10 @@ class DataStoreEncryptor:
return self._key_base_encryptor.decrypt(enc_message) return self._key_base_encryptor.decrypt(enc_message)
def initialize_datastore_encryptor(key_file_dir): def initialize_datastore_encryptor(key_file_dir: str, secret: str):
global _encryptor global _encryptor
_encryptor = DataStoreEncryptor(key_file_dir) _encryptor = DataStoreEncryptor(key_file_dir, secret)
def get_datastore_encryptor(): def get_datastore_encryptor():

View File

@ -27,6 +27,9 @@ def monkey_config_json(monkey_config):
return json.dumps(monkey_config) return json.dumps(monkey_config)
ENCRYPTOR_SECRET = "m0nk3y_u53r:53cr3t_p455w0rd"
@pytest.fixture @pytest.fixture
def uses_encryptor(data_for_tests_dir): def uses_encryptor(data_for_tests_dir):
initialize_datastore_encryptor(data_for_tests_dir) initialize_datastore_encryptor(data_for_tests_dir, ENCRYPTOR_SECRET)

View File

@ -1,6 +1,3 @@
import pytest
from monkey_island.cc.server_utils.encryption import initialize_datastore_encryptor
from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import ( from monkey_island.cc.server_utils.encryption.dict_encryption.field_encryptors import (
StringListEncryptor, StringListEncryptor,
) )
@ -9,11 +6,6 @@ MOCK_STRING_LIST = ["test_1", "test_2"]
EMPTY_LIST = [] EMPTY_LIST = []
@pytest.fixture
def uses_encryptor(data_for_tests_dir):
initialize_datastore_encryptor(data_for_tests_dir)
def test_encryption_and_decryption(uses_encryptor): def test_encryption_and_decryption(uses_encryptor):
encrypted_list = StringListEncryptor.encrypt(MOCK_STRING_LIST) encrypted_list = StringListEncryptor.encrypt(MOCK_STRING_LIST)
assert not encrypted_list == MOCK_STRING_LIST assert not encrypted_list == MOCK_STRING_LIST

View File

@ -1,35 +1,26 @@
import os import os
import pytest
from tests.unit_tests.monkey_island.cc.conftest import ENCRYPTOR_SECRET
from monkey_island.cc.server_utils.encryption import ( from monkey_island.cc.server_utils.encryption import (
DataStoreEncryptor,
get_datastore_encryptor, get_datastore_encryptor,
initialize_datastore_encryptor, initialize_datastore_encryptor,
) )
PASSWORD_FILENAME = "mongo_key.bin"
PLAINTEXT = "Hello, Monkey!" PLAINTEXT = "Hello, Monkey!"
CYPHERTEXT = "vKgvD6SjRyIh1dh2AM/rnTa0NI/vjfwnbZLbMocWtE4e42WJmSUz2ordtbQrH1Fq"
def test_aes_cbc_encryption(data_for_tests_dir): @pytest.mark.usefixtures("uses_encryptor")
initialize_datastore_encryptor(data_for_tests_dir) def test_encryption(data_for_tests_dir):
encrypted_data = get_datastore_encryptor().enc(PLAINTEXT)
assert encrypted_data != PLAINTEXT
assert get_datastore_encryptor().enc(PLAINTEXT) != PLAINTEXT decrypted_data = get_datastore_encryptor().dec(encrypted_data)
assert decrypted_data == PLAINTEXT
def test_aes_cbc_decryption(data_for_tests_dir):
initialize_datastore_encryptor(data_for_tests_dir)
assert get_datastore_encryptor().dec(CYPHERTEXT) == PLAINTEXT
def test_aes_cbc_enc_dec(data_for_tests_dir):
initialize_datastore_encryptor(data_for_tests_dir)
assert get_datastore_encryptor().dec(get_datastore_encryptor().enc(PLAINTEXT)) == PLAINTEXT
def test_create_new_password_file(tmpdir): def test_create_new_password_file(tmpdir):
initialize_datastore_encryptor(tmpdir) initialize_datastore_encryptor(tmpdir, ENCRYPTOR_SECRET)
assert os.path.isfile(os.path.join(tmpdir, DataStoreEncryptor._KEY_FILENAME))
assert os.path.isfile(os.path.join(tmpdir, PASSWORD_FILENAME))

View File

@ -5,10 +5,7 @@ import pytest
from common.config_value_paths import AWS_KEYS_PATH from common.config_value_paths import AWS_KEYS_PATH
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.server_utils.encryption import ( from monkey_island.cc.server_utils.encryption import get_datastore_encryptor
get_datastore_encryptor,
initialize_datastore_encryptor,
)
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import ( from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import (
is_aws_keys_setup, is_aws_keys_setup,
@ -19,7 +16,7 @@ class MockObject:
pass pass
@pytest.mark.usefixtures("uses_database") @pytest.mark.usefixtures("uses_database", "uses_encryptor")
def test_is_aws_keys_setup(tmp_path): def test_is_aws_keys_setup(tmp_path):
# Mock default configuration # Mock default configuration
ConfigService.init_default_config() ConfigService.init_default_config()
@ -29,8 +26,6 @@ def test_is_aws_keys_setup(tmp_path):
mongo.db.config.find_one = MagicMock(return_value=ConfigService.default_config) mongo.db.config.find_one = MagicMock(return_value=ConfigService.default_config)
assert not is_aws_keys_setup() assert not is_aws_keys_setup()
# Make sure noone changed config path and broke this function
initialize_datastore_encryptor(tmp_path)
bogus_key_value = get_datastore_encryptor().enc("bogus_aws_key") bogus_key_value = get_datastore_encryptor().enc("bogus_aws_key")
dpath.util.set( dpath.util.set(
ConfigService.default_config, AWS_KEYS_PATH + ["aws_secret_access_key"], bogus_key_value ConfigService.default_config, AWS_KEYS_PATH + ["aws_secret_access_key"], bogus_key_value