forked from p15670423/monkey
Add a secret to datastore encryptor
This change enables the encryption/decryption of mongo key with a custom secret
This commit is contained in:
parent
191fbea665
commit
fd1cb9d36d
|
@ -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():
|
||||||
|
|
Binary file not shown.
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue