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
# 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 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
_encryptor = None
@ -14,24 +18,30 @@ class DataStoreEncryptor:
_BLOCK_SIZE = 32
_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)
if os.path.exists(key_file):
self._load_existing_key(key_file)
self._load_existing_key(key_file, secret)
else:
self._init_key(key_file)
self._init_key(key_file, secret)
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)
encrypted_key = (
PasswordBasedByteEncryptor(secret).encrypt(io.BytesIO(self._cipher_key)).getvalue()
)
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):
with open(key_file, "rb") as f:
self._cipher_key = f.read()
def _load_existing_key(self, key_file_path: str, secret: str):
with open(key_file_path, "rb") as f:
encrypted_key = f.read()
self._cipher_key = (
PasswordBasedByteEncryptor(secret).decrypt(io.BytesIO(encrypted_key)).getvalue()
)
def enc(self, message: str):
return self._key_base_encryptor.encrypt(message)
@ -40,10 +50,10 @@ class DataStoreEncryptor:
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
_encryptor = DataStoreEncryptor(key_file_dir)
_encryptor = DataStoreEncryptor(key_file_dir, secret)
def get_datastore_encryptor():

View File

@ -27,6 +27,9 @@ def monkey_config_json(monkey_config):
return json.dumps(monkey_config)
ENCRYPTOR_SECRET = "m0nk3y_u53r:53cr3t_p455w0rd"
@pytest.fixture
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 (
StringListEncryptor,
)
@ -9,11 +6,6 @@ MOCK_STRING_LIST = ["test_1", "test_2"]
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):
encrypted_list = StringListEncryptor.encrypt(MOCK_STRING_LIST)
assert not encrypted_list == MOCK_STRING_LIST

View File

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

View File

@ -5,10 +5,7 @@ import pytest
from common.config_value_paths import AWS_KEYS_PATH
from monkey_island.cc.database import mongo
from monkey_island.cc.server_utils.encryption import (
get_datastore_encryptor,
initialize_datastore_encryptor,
)
from monkey_island.cc.server_utils.encryption import get_datastore_encryptor
from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import (
is_aws_keys_setup,
@ -19,7 +16,7 @@ class MockObject:
pass
@pytest.mark.usefixtures("uses_database")
@pytest.mark.usefixtures("uses_database", "uses_encryptor")
def test_is_aws_keys_setup(tmp_path):
# Mock default configuration
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)
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")
dpath.util.set(
ConfigService.default_config, AWS_KEYS_PATH + ["aws_secret_access_key"], bogus_key_value