class PasswordEncryption(AESinRSAStreamEncryption): """ This is a password encryption which uses a pub/private RSSA key pair which is specified to secure an AES stream for item encryption. What does this class do in detail: Usually, an RSA identity would be used for each separate password used (shared among all items that are encrypted with this password), and the private key would be stored encrypted with the given password. This structure just handles AES with the given identity - you need to handle storing the RSA identity (including the required private key encryption with the password) elsewhere. TODO: document where the RSA identities for pw encryption are handled """ def __init__(self, load_from_folder_path, password, aes_info_path=None): super().__init__() self.load_from_folder_path = load_from_folder_path # set the proper stored AES key, if this isn't a new encryption: if load_from_folder_path != None: if password == None: # do nothing for now return self.load_from_folder(load_from_folder_path) else: self.crypto_identity = Identity() self.stored_password = password def unlock(self, password): self.stored_password = password self.load_from_folder(self.load_from_folder_path) def lock(self): if hasattr(self, "aes_key"): del (self.aes_key) if hasattr(self, "crypto_identity"): del (self.crypto_identity) if hasattr(self, "stored_password"): del (self.stored_password) def save_to_folder(self, path): super().save_encrypted_aes_info(self.crypto_identity, os.path.join(path, "aes_info.data")) self.crypto_identity.save_to_file(os.path.join(path, "rsa_identity.data"), passphrase=self.stored_password) def load_from_folder(self, path, password=None): self.crypto_identity = Identity(os.path.join(path, "rsa_identity.data"), passphrase=password) super().load_encrypted_aes_info(self.crypto_identity)
def __init__(self, target_node_pubkey_path): super().__init__() self.privkey = None # construct public key-only identity with supplied key file: self.crypto_identity = Identity(target_node_pubkey_path) if self.crypto_identity.has_private(): self.set_decryption_by_identity(self.crypto_identity)
def __init__(self, load_from_folder_path, password, aes_info_path=None): super().__init__() self.load_from_folder_path = load_from_folder_path # set the proper stored AES key, if this isn't a new encryption: if load_from_folder_path != None: if password == None: # do nothing for now return self.load_from_folder(load_from_folder_path) else: self.crypto_identity = Identity() self.stored_password = password
class TargetNodeEncryption(AESinRSAStreamEncryption): """ This handles asymmetric encryption for another information node's public RSA identity information which must be provided. It will then encrypt an AES stream which can only be opened with the target node's information key (unless one day RSA is broken of course). """ def __init__(self, target_node_pubkey_path): super().__init__() self.privkey = None # construct public key-only identity with supplied key file: self.crypto_identity = Identity(target_node_pubkey_path) if self.crypto_identity.has_private(): self.set_decryption_by_identity(self.crypto_identity) def set_decryption_private_key(self, target_node_privkey_path, passphrase=None): """ Provide an RSA private key to allow this to decrypt data. """ identity = Identity(target_node_privkey_path, passphrase=passphrase) self._set_decryption_by_identity(identity) def set_decryption_by_identity(self, identity): self.crypto_identity = identity super().load_encrypted_aes_info(self.crypto_identity) def save_to_folder(self, path): super().save_encrypted_aes_info(self.crypto_identity, os.path.join(path, "aes_info.data")) def load_from_folder(self, path): if not self.crypto_identity.has_private(): raise RuntimeError("no private key provided, cannot decrypt") super().load_encrypted_aes_info(self.crypto_identity, os.path.join(path, "aes_info.data"))
def test_encrypt_decrypt(self): identity = Identity() # test basic encryption / decryption plain_str = b"abc" encrypted_str = identity.encrypt(plain_str) self.assertNotEqual(plain_str, encrypted_str) decrypted_str = identity.decrypt(encrypted_str) self.assertEqual(plain_str, decrypted_str) # ensure it behaves non-deterministically due to PKCS padding: self.assertNotEqual(identity.encrypt(plain_str), identity.encrypt(plain_str)) # ensure a way too long value doesn't give any other error than # a ValueError: too_long_value = os.urandom(identity.size()) got_value_error = False try: result = identity.encrypt(too_long_value) except ValueError: got_value_error = True self.assertTrue(got_value_error)
def load_from_folder(self, path, password=None): self.crypto_identity = Identity(os.path.join(path, "rsa_identity.data"), passphrase=password) super().load_encrypted_aes_info(self.crypto_identity)