Island: Add ILockableEncryptor

This commit is contained in:
Mike Salvatore 2022-07-11 10:02:06 -04:00
parent 54c1eef309
commit bd2d79fd43
2 changed files with 65 additions and 0 deletions

View File

@ -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,

View File

@ -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
"""