From bd2d79fd43eedcb4717f90e56a7d8f6f07814cab Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 11 Jul 2022 10:02:06 -0400 Subject: [PATCH] Island: Add ILockableEncryptor --- .../cc/server_utils/encryption/__init__.py | 1 + .../encryption/i_lockable_encryptor.py | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 monkey/monkey_island/cc/server_utils/encryption/i_lockable_encryptor.py diff --git a/monkey/monkey_island/cc/server_utils/encryption/__init__.py b/monkey/monkey_island/cc/server_utils/encryption/__init__.py index a6671afb6..c3ed8c46d 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/__init__.py +++ b/monkey/monkey_island/cc/server_utils/encryption/__init__.py @@ -7,6 +7,7 @@ from .password_based_bytes_encryptor import ( InvalidCredentialsError, InvalidCiphertextError, ) +from .i_lockable_encryptor import ILockableEncryptor from .data_store_encryptor import ( get_datastore_encryptor, unlock_datastore_encryptor, diff --git a/monkey/monkey_island/cc/server_utils/encryption/i_lockable_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/i_lockable_encryptor.py new file mode 100644 index 000000000..b1b52f1d8 --- /dev/null +++ b/monkey/monkey_island/cc/server_utils/encryption/i_lockable_encryptor.py @@ -0,0 +1,64 @@ +from abc import abstractmethod + +from . import IEncryptor + +# NOTE: The ILockableEncryptor introduces temporal coupling, that is, you must first unlock the +# encryptor before you can use it. This is because the key material used to encrypt repository +# contents is encrypted using the user's username and password. This adds extra security by +# allowing us to fully encrypt data at rest. Without this, we'd need to store the repository +# key in plaintext. Alternative solutions are as follows: +# 1. Store the repository key in plaintext +# 2. Add an initialization phase to the island's boot sequence. At the moment, this is the +# only element of the design that would benefit from adding an additional phase. If +# other temporal coupling begins to creep in, we can add the initialization phase at +# that time and remove this interface. + + +class LockedKeyError(Exception): + """ + Raised when an ILockableEncryptor attemps to encrypt or decrypt data before the + ILockableEncryptor has been unlocked. + """ + + +class ILockableEncryptor(IEncryptor): + """ + An encryptor that can be locked or unlocked. + + ILockableEncryptor's require a secret in order to access their key material. These encryptors + must be unlocked before use and can be re-locked at the user's request. + """ + + @abstractmethod + def unlock(self, secret: bytes): + """ + Unlock the encryptor + + :param secret: A secret that must be used to access the ILockableEncryptor's key material. + """ + + @abstractmethod + def lock(self): + """ + Lock the encryptor, making it unusable + """ + + @abstractmethod + def encrypt(self, plaintext: bytes) -> bytes: + """ + Encrypts data and returns the ciphertext. + + :param plaintext: Data that will be encrypted + :return: Ciphertext generated by encrypting the plaintext + :raises LockedKeyError: If encrypt() is called while the ILockableEncryptor is locked + """ + + @abstractmethod + def decrypt(self, ciphertext: bytes) -> bytes: + """ + Decrypts data and returns the plaintext. + + :param ciphertext: Ciphertext that will be decrypted + :return: Plaintext generated by decrypting the ciphertext + :raises LockedKeyError: If decrypt() is called while the ILockableEncryptor is locked + """