def test_container_encryption_and_decryption(container_conf): data = b"abc" # get_random_bytes(random.randint(1, 1000)) keychain_uid = random.choice( [None, uuid.UUID("450fc293-b702-42d3-ae65-e9cc58e5a62a")]) metadata = random.choice([None, dict(a=[123])]) container = encrypt_data_into_container(data=data, conf=container_conf, keychain_uid=keychain_uid, metadata=metadata) # pprint.pprint(container, width=120) assert container["keychain_uid"] if keychain_uid: assert container["keychain_uid"] == keychain_uid result_data = decrypt_data_from_container(container=container) # pprint.pprint(result, width=120) assert result_data == data result_metadata = extract_metadata_from_container(container=container) assert result_metadata == metadata container["container_format"] = "OAJKB" with pytest.raises(ValueError, match="Unknown container format"): decrypt_data_from_container(container=container)
def test_recursive_shamir_secrets_and_strata(): keychain_uid = generate_uuid0() data = b"qssd apk_$82" container = encrypt_data_into_container( data=data, conf=RECURSIVE_CONTAINER_CONF, keychain_uid=keychain_uid, metadata=None ) data_decrypted = decrypt_data_from_container( container=container, ) assert data_decrypted == data
def _offloaded_attempt_container_decryption(self, container_filepath): logger.info("Decryption requested for container %s", container_filepath) target_directory = EXTERNAL_DATA_EXPORTS_DIR.joinpath( os.path.basename(container_filepath) ) target_directory.mkdir( exist_ok=True ) # Double exports would replace colliding files container = load_container_from_filesystem(container_filepath, include_data_ciphertext=True) tarfile_bytes = decrypt_data_from_container( container, key_storage_pool=self._key_storage_pool ) tarfile_bytesio = io.BytesIO(tarfile_bytes) tarfile_obj = tarfile.open( mode="r", fileobj=tarfile_bytesio # TODO add gzip support here one day ) # Beware, as root on unix systems it would apply chown/chmod tarfile_obj.extractall(target_directory) logger.info( "Container content was successfully decrypted into folder %s", target_directory, )
def _do_decrypt(container, key_storage_pool): data = decrypt_data_from_container(container, key_storage_pool=key_storage_pool) return data
def _do_decrypt(container): data = decrypt_data_from_container(container) return data
def test_passphrase_mapping_during_decryption(tmp_path): keychain_uid = generate_uuid0() keychain_uid_escrow = generate_uuid0() local_passphrase = "b^yep&ts" key_storage_uid1 = keychain_uid_escrow # FIXME why mix key and storage uids ? passphrase1 = "tata" key_storage_uid2 = generate_uuid0() passphrase2 = "2çès" key_storage_uid3 = generate_uuid0() passphrase3 = "zaizoadsxsnd123" all_passphrases = [local_passphrase, passphrase1, passphrase2, passphrase3] key_storage_pool = DummyKeyStoragePool() key_storage_pool._register_fake_imported_storage_uids( storage_uids=[key_storage_uid1, key_storage_uid2, key_storage_uid3] ) local_key_storage = key_storage_pool.get_local_key_storage() generate_asymmetric_keypair_for_storage( key_type="RSA_OAEP", key_storage=local_key_storage, keychain_uid=keychain_uid, passphrase=local_passphrase ) key_storage1 = key_storage_pool.get_imported_key_storage(key_storage_uid1) generate_asymmetric_keypair_for_storage( key_type="RSA_OAEP", key_storage=key_storage1, keychain_uid=keychain_uid_escrow, passphrase=passphrase1 ) key_storage2 = key_storage_pool.get_imported_key_storage(key_storage_uid2) generate_asymmetric_keypair_for_storage( key_type="RSA_OAEP", key_storage=key_storage2, keychain_uid=keychain_uid, passphrase=passphrase2 ) key_storage3 = key_storage_pool.get_imported_key_storage(key_storage_uid3) generate_asymmetric_keypair_for_storage( key_type="RSA_OAEP", key_storage=key_storage3, keychain_uid=keychain_uid, passphrase=passphrase3 ) local_escrow_id = get_escrow_id(LOCAL_ESCROW_MARKER) share_escrow1 = dict(escrow_type="authentication_device", authentication_device_uid=key_storage_uid1) share_escrow1_id = get_escrow_id(share_escrow1) share_escrow2 = dict(escrow_type="authentication_device", authentication_device_uid=key_storage_uid2) share_escrow2_id = get_escrow_id(share_escrow2) share_escrow3 = dict(escrow_type="authentication_device", authentication_device_uid=key_storage_uid3) share_escrow3_id = get_escrow_id(share_escrow3) container_conf = dict( data_encryption_strata=[ dict( data_encryption_algo="AES_CBC", key_encryption_strata=[ dict(key_encryption_algo="RSA_OAEP", key_escrow=LOCAL_ESCROW_MARKER), dict( key_encryption_algo=SHARED_SECRET_MARKER, key_shared_secret_threshold=2, key_shared_secret_escrows=[ dict(key_encryption_strata=[ dict(key_encryption_algo="RSA_OAEP", key_escrow=share_escrow1, keychain_uid=keychain_uid_escrow)],), dict(key_encryption_strata=[ dict(key_encryption_algo="RSA_OAEP", key_escrow=share_escrow2)],), dict(key_encryption_strata=[ dict(key_encryption_algo="RSA_OAEP", key_escrow=share_escrow3)],), ], ), ], data_signatures=[ dict( message_digest_algo="SHA256", signature_algo="DSA_DSS", signature_escrow=LOCAL_ESCROW_MARKER, # Uses separate keypair, no passphrase here ) ], ) ] ) data = b"sjzgzj" container = encrypt_data_into_container( data=data, conf=container_conf, keychain_uid=keychain_uid, key_storage_pool=key_storage_pool, metadata=None ) # FIXME we must TEST that keychain_uid_escrow is necessary for decryption for example by deleting it before a decrypt() with pytest.raises(DecryptionError, match="2 valid .* missing for reconstitution"): decrypt_data_from_container(container, key_storage_pool=key_storage_pool) with pytest.raises(DecryptionError, match="2 valid .* missing for reconstitution"): decrypt_data_from_container( container, key_storage_pool=key_storage_pool, passphrase_mapper={local_escrow_id: all_passphrases} ) # Doesn't help share escrows with pytest.raises(DecryptionError, match="1 valid .* missing for reconstitution"): decrypt_data_from_container( container, key_storage_pool=key_storage_pool, passphrase_mapper={share_escrow1_id: all_passphrases} ) # Unblocks 1 share escrow with pytest.raises(DecryptionError, match="1 valid .* missing for reconstitution"): decrypt_data_from_container( container, key_storage_pool=key_storage_pool, passphrase_mapper={share_escrow1_id: all_passphrases, share_escrow2_id: [passphrase3]}, ) # No changes with pytest.raises(DecryptionError, match="Could not decrypt private key"): decrypt_data_from_container( container, key_storage_pool=key_storage_pool, passphrase_mapper={share_escrow1_id: all_passphrases, share_escrow3_id: [passphrase3]}, ) with pytest.raises(DecryptionError, match="Could not decrypt private key"): decrypt_data_from_container( container, key_storage_pool=key_storage_pool, passphrase_mapper={ local_escrow_id: ["qsdqsd"], share_escrow1_id: all_passphrases, share_escrow3_id: [passphrase3], }, ) decrypted = decrypt_data_from_container( container, key_storage_pool=key_storage_pool, passphrase_mapper={ local_escrow_id: [local_passphrase], share_escrow1_id: all_passphrases, share_escrow3_id: [passphrase3], }, ) assert decrypted == data # Passphrases of `None` key are always used decrypted = decrypt_data_from_container( container, key_storage_pool=key_storage_pool, passphrase_mapper={ local_escrow_id: [local_passphrase], share_escrow1_id: ["dummy-passphrase"], share_escrow3_id: [passphrase3], None: all_passphrases, }, ) assert decrypted == data # Proper forwarding of parameters in container storage class storage = ContainerStorage(tmp_path, key_storage_pool=key_storage_pool) storage.enqueue_file_for_encryption( "beauty.txt", data=data, metadata=None, keychain_uid=keychain_uid, encryption_conf=container_conf ) storage.wait_for_idle_state() container_names = storage.list_container_names(as_sorted=True) print(">> container_names", container_names) with pytest.raises(DecryptionError): storage.decrypt_container_from_storage("beauty.txt.crypt") decrypted = storage.decrypt_container_from_storage("beauty.txt.crypt", passphrase_mapper={None: all_passphrases}) assert decrypted == data
def test_shamir_container_encryption_and_decryption(shamir_container_conf, escrow_dependencies_builder): data = b"abc" # get_random_bytes(random.randint(1, 1000)) # FIXME reactivate ??? keychain_uid = random.choice([None, uuid.UUID("450fc293-b702-42d3-ae65-e9cc58e5a62a")]) metadata = random.choice([None, dict(a=[123])]) container = encrypt_data_into_container( data=data, conf=shamir_container_conf, keychain_uid=keychain_uid, metadata=metadata ) assert container["keychain_uid"] if keychain_uid: assert container["keychain_uid"] == keychain_uid escrow_dependencies = gather_escrow_dependencies(containers=[container]) assert escrow_dependencies == escrow_dependencies_builder(container["keychain_uid"]) assert isinstance(container["data_ciphertext"], bytes) result_data = decrypt_data_from_container(container=container) assert result_data == data data_encryption_shamir = {} # Delete 1, 2 and too many share(s) from cipherdict key for data_encryption in container["data_encryption_strata"]: for key_encryption in data_encryption["key_encryption_strata"]: if key_encryption["key_encryption_algo"] == SHARED_SECRET_MARKER: data_encryption_shamir = data_encryption key_ciphertext_shares = load_from_json_bytes(data_encryption_shamir["key_ciphertext"]) # 1 share is deleted del key_ciphertext_shares["shares"][-1] data_encryption_shamir["key_ciphertext"] = dump_to_json_bytes(key_ciphertext_shares) result_data = decrypt_data_from_container(container=container) assert result_data == data # Another share is deleted del key_ciphertext_shares["shares"][-1] data_encryption_shamir["key_ciphertext"] = dump_to_json_bytes(key_ciphertext_shares) result_data = decrypt_data_from_container(container=container) assert result_data == data # Another share is deleted and now there aren't enough valid ones to decipher data del key_ciphertext_shares["shares"][-1] data_encryption_shamir["key_ciphertext"] = dump_to_json_bytes(key_ciphertext_shares) with pytest.raises(DecryptionError, match="share.*missing"): decrypt_data_from_container(container=container) result_metadata = extract_metadata_from_container(container=container) assert result_metadata == metadata container["container_format"] = "OAJKB" with pytest.raises(ValueError, match="Unknown container format"): decrypt_data_from_container(container=container)
def test_standard_container_encryption_and_decryption(container_conf, escrow_dependencies_builder): data = b"abc" # get_random_bytes(random.randint(1, 1000)) keychain_uid = random.choice([None, uuid.UUID("450fc293-b702-42d3-ae65-e9cc58e5a62a")]) key_storage_pool = DummyKeyStoragePool() metadata = random.choice([None, dict(a=[123])]) container = encrypt_data_into_container( data=data, conf=container_conf, keychain_uid=keychain_uid, metadata=metadata, key_storage_pool=key_storage_pool ) assert container["keychain_uid"] if keychain_uid: assert container["keychain_uid"] == keychain_uid local_keypair_identifiers = key_storage_pool.get_local_key_storage()._cached_keypairs print(">>> Test local_keypair_identifiers ->", list(local_keypair_identifiers.keys())) escrow_dependencies = gather_escrow_dependencies(containers=[container]) print("GOTTEN DEPENDENCIES:") pprint(escrow_dependencies) print("THEORETICAL DEPENDENCIES:") pprint(escrow_dependencies_builder(container["keychain_uid"])) assert escrow_dependencies == escrow_dependencies_builder(container["keychain_uid"]) # Check that all referenced keys were really created during encryption (so keychain_uid overriding works fine) for escrow_dependency_structs in escrow_dependencies.values(): for escrow_dependency_struct in escrow_dependency_structs.values(): escrow_conf, keypairs_identifiers = escrow_dependency_struct escrow = get_escrow_proxy(escrow_conf, key_storage_pool=key_storage_pool) for keypairs_identifier in keypairs_identifiers: assert escrow.fetch_public_key(**keypairs_identifier, must_exist=True) all_authorization_results = request_decryption_authorizations( escrow_dependencies=escrow_dependencies, request_message="Decryption needed", key_storage_pool=key_storage_pool ) # Generic check of data structure for authorization_results in all_authorization_results.values(): assert not authorization_results["has_errors"] assert "accepted" in authorization_results["response_message"] keypair_statuses = authorization_results["keypair_statuses"] assert keypair_statuses["accepted"] for keypair_identifiers in keypair_statuses["accepted"]: assert keypair_identifiers["key_type"] in SUPPORTED_ENCRYPTION_ALGOS assert isinstance(keypair_identifiers["keychain_uid"], UUID) assert not keypair_statuses["authorization_missing"] assert not keypair_statuses["missing_passphrase"] assert not keypair_statuses["missing_private_key"] result_data = decrypt_data_from_container(container=container, key_storage_pool=key_storage_pool) # pprint.pprint(result, width=120) assert result_data == data result_metadata = extract_metadata_from_container(container=container) assert result_metadata == metadata container["container_format"] = "OAJKB" with pytest.raises(ValueError, match="Unknown container format"): decrypt_data_from_container(container=container)