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
示例#5
0
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)