コード例 #1
0
def test_key_storage_pool_basics(tmp_path: Path):

    pool = FilesystemKeyStoragePool(tmp_path)

    local_key_storage = pool.get_local_key_storage()
    assert isinstance(local_key_storage, FilesystemKeyStorage)
    assert not local_key_storage.list_keypair_identifiers()

    keypair = generate_asymmetric_keypair_for_storage(
        key_type="RSA_OAEP",
        key_storage=local_key_storage,
        passphrase="xzf".encode())

    assert len(local_key_storage.list_keypair_identifiers()) == 1

    assert pool.list_imported_key_storage_uids() == []

    imported_key_storage_uid = generate_uuid0()
    mirror_path = tmp_path.joinpath(
        pool.IMPORTED_STORAGES_DIRNAME,
        pool.IMPORTED_STORAGE_PREFIX + str(imported_key_storage_uid))
    mirror_path.mkdir(parents=True, exist_ok=False)

    imported_key_storage_uid2 = generate_uuid0()
    mirror_path2 = tmp_path.joinpath(
        pool.IMPORTED_STORAGES_DIRNAME,
        pool.IMPORTED_STORAGE_PREFIX + str(imported_key_storage_uid2))
    mirror_path2.mkdir(parents=True, exist_ok=False)

    assert pool.list_imported_key_storage_uids() == sorted(
        [imported_key_storage_uid, imported_key_storage_uid2])

    with pytest.raises(KeyStorageDoesNotExist, match="not found"):
        pool.get_imported_key_storage(generate_uuid0())

    imported_key_storage = pool.get_imported_key_storage(
        imported_key_storage_uid)
    assert isinstance(imported_key_storage, FilesystemKeyStorage)
    assert not imported_key_storage.list_keypair_identifiers()

    imported_key_storage.set_keys(
        keychain_uid=generate_uuid0(),
        key_type="RSA_OAEP",
        public_key=keypair["public_key"],
        private_key=keypair["private_key"],
    )

    assert len(local_key_storage.list_keypair_identifiers()) == 1  # Unchanged
    assert len(imported_key_storage.list_keypair_identifiers()) == 1

    imported_key_storage2 = pool.get_imported_key_storage(
        imported_key_storage_uid2)
    assert isinstance(imported_key_storage2, FilesystemKeyStorage)
    assert not imported_key_storage2.list_keypair_identifiers()
コード例 #2
0
def check_key_storage_free_keys_api(key_storage):
    """Test the storage regarding the precreation of "free keys", and their subsequent attachment to uids."""
    import pytest

    keychain_uid = generate_uuid0()
    keychain_uid_other = generate_uuid0()

    # This blocks free key attachment to this uid+type
    key_storage.set_keys(keychain_uid=keychain_uid, key_type="type1", public_key=b"whatever1", private_key=b"whatever2")

    key_storage.add_free_keypair(key_type="type1", public_key=b"public_data", private_key=b"private_data")
    key_storage.add_free_keypair(key_type="type1", public_key=b"public_data2", private_key=b"private_data2")
    key_storage.add_free_keypair(
        key_type="type2", public_key=b"public_data_other_type", private_key=b"private_data_other_type"
    )

    assert key_storage.get_free_keypairs_count("type1") == 2
    assert key_storage.get_free_keypairs_count("type2") == 1
    assert key_storage.get_free_keypairs_count("type3") == 0

    with pytest.raises(KeyAlreadyExists, match="Already existing"):
        key_storage.attach_free_keypair_to_uuid(keychain_uid=keychain_uid, key_type="type1")

    with pytest.raises(KeyDoesNotExist, match="not found"):
        key_storage.get_public_key(keychain_uid=keychain_uid, key_type="type2")

    key_storage.attach_free_keypair_to_uuid(keychain_uid=keychain_uid, key_type="type2")
    assert b"public_data" in key_storage.get_public_key(keychain_uid=keychain_uid, key_type="type2")

    assert key_storage.get_free_keypairs_count("type1") == 2
    assert key_storage.get_free_keypairs_count("type2") == 0
    assert key_storage.get_free_keypairs_count("type3") == 0

    key_storage.attach_free_keypair_to_uuid(keychain_uid=keychain_uid_other, key_type="type1")

    assert key_storage.get_free_keypairs_count("type1") == 1
    assert key_storage.get_free_keypairs_count("type2") == 0
    assert key_storage.get_free_keypairs_count("type3") == 0

    with pytest.raises(KeyDoesNotExist, match="No free keypair of type"):
        key_storage.attach_free_keypair_to_uuid(keychain_uid=keychain_uid_other, key_type="type2")

    with pytest.raises(KeyDoesNotExist, match="No free keypair of type"):
        key_storage.attach_free_keypair_to_uuid(keychain_uid=keychain_uid, key_type="type3")

    assert key_storage.get_free_keypairs_count("type1") == 1
    assert key_storage.get_free_keypairs_count("type2") == 0
    assert key_storage.get_free_keypairs_count("type3") == 0

    return locals()
コード例 #3
0
    def _offloaded_initialize_authenticator(self, form_values, authenticator_path):
        success = False

        try:
      
            Clock.schedule_once(partial(self._do_update_progress_bar, 10))

            initialize_authenticator(authenticator_path,
                                     user=form_values["user"],
                                     extra_metadata=dict(passphrase_hint=form_values["passphrase_hint"]))

            filesystem_key_storage = FilesystemKeyStorage(authenticator_path)

            for i in range(1, GENERATED_KEYS_COUNT+1):
                # TODO add some logging here
                key_pair = generate_asymmetric_keypair(
                    key_type="RSA_OAEP",
                    passphrase=form_values["passphrase"]
                )
                filesystem_key_storage.set_keys(
                    keychain_uid=generate_uuid0(),
                    key_type="RSA_OAEP",
                    public_key=key_pair["public_key"],
                    private_key=key_pair["private_key"],
                )

                Clock.schedule_once(partial(self._do_update_progress_bar, 10 + int (i * 90 / GENERATED_KEYS_COUNT)))

            success = True

        except Exception as exc:
            print(">> ERROR IN _offloaded_initialize_authenticator THREAD", exc)  # FIXME add logging AND snackbar

        Clock.schedule_once(partial(self.finish_initialization, success=success))
コード例 #4
0
def test_key_storage_import_key_storage_from_folder(tmp_path: Path):

    pool_path = tmp_path / "pool"
    pool_path.mkdir()
    pool = FilesystemKeyStoragePool(pool_path)
    assert pool.list_imported_key_storage_uids() == []
    assert pool.list_imported_key_storage_metadata() == {}

    authentication_device_path = tmp_path / "device"
    authentication_device_path.mkdir()
    authentication_device = get_fake_authentication_device(
        authentication_device_path)
    initialize_authentication_device(authentication_device,
                                     user="******")

    keychain_uid = generate_uuid0()
    key_type = "RSA_OAEP"

    remote_key_storage_path = _get_key_storage_folder_path(
        authentication_device)
    remote_key_storage = FilesystemKeyStorage(remote_key_storage_path)
    remote_key_storage.set_keys(keychain_uid=keychain_uid,
                                key_type=key_type,
                                public_key=b"555",
                                private_key=b"okj")

    # Still untouched of course
    assert pool.list_imported_key_storage_uids() == []
    assert pool.list_imported_key_storage_metadata() == {}

    pool.import_key_storage_from_folder(remote_key_storage_path)

    (key_storage_uid, ) = pool.list_imported_key_storage_uids()
    metadata_mapper = pool.list_imported_key_storage_metadata()
    assert tuple(metadata_mapper) == (key_storage_uid, )

    metadata = metadata_mapper[key_storage_uid]
    assert metadata["device_uid"] == key_storage_uid
    assert metadata["user"] == "Jean-Jâcques"

    with pytest.raises(KeyStorageAlreadyExists, match=str(key_storage_uid)):
        pool.import_key_storage_from_folder(remote_key_storage_path)

    shutil.rmtree(authentication_device_path)  # Not important anymore

    assert pool.list_imported_key_storage_uids() == [key_storage_uid]
    metadata_mapper2 = pool.list_imported_key_storage_metadata()
    assert metadata_mapper2 == metadata_mapper

    key_storage = pool.get_imported_key_storage(key_storage_uid)
    assert key_storage.list_keypair_identifiers() == [
        dict(keychain_uid=keychain_uid,
             key_type=key_type,
             private_key_present=True)
    ]
    assert key_storage.get_public_key(keychain_uid=keychain_uid,
                                      key_type=key_type) == b"555"
    assert key_storage.get_private_key(keychain_uid=keychain_uid,
                                       key_type=key_type) == b"okj"
コード例 #5
0
def _generate_authenticator_parameter_tree(key_count, key_value=None):
    public_keys = []

    for count in range(key_count):
        public_keys.append({
            "keychain_uid": generate_uuid0(),
            "key_algo": "RSA_OAEP",
            "key_value": key_value or get_random_bytes(20)
        })

    parameters = dict(
        keystore_owner="keystore_owner",
        keystore_secret="keystore_secret",
        keystore_uid=generate_uuid0(),
        public_keys=public_keys
    )
    return parameters
コード例 #6
0
def check_key_storage_basic_get_set_api(key_storage):
    """Test the workflow of getters/setters of the storage API, for uid-attached keys."""

    import pytest

    keychain_uid = generate_uuid0()
    keychain_uid_other = generate_uuid0()
    key_type = "abxz"

    with pytest.raises(KeyDoesNotExist, match="not found"):
        key_storage.get_public_key(keychain_uid=keychain_uid, key_type="abxz")

    key_storage.set_keys(
        keychain_uid=keychain_uid, key_type=key_type, public_key=b"public_data", private_key=b"private_data"
    )

    assert (
        key_storage.get_public_key(keychain_uid=keychain_uid, key_type="abxz") == b"public_data"
    )  # Well readable even without any kind of "commit"

    with pytest.raises(KeyAlreadyExists, match="Already existing"):
        key_storage.set_keys(
            keychain_uid=keychain_uid, key_type=key_type, public_key=b"public_data", private_key=b"private_data"
        )
    with pytest.raises(KeyAlreadyExists, match="Already existing"):
        key_storage.set_keys(
            keychain_uid=keychain_uid, key_type=key_type, public_key=b"public_data2", private_key=b"private_data2"
        )

    assert key_storage.get_public_key(keychain_uid=keychain_uid, key_type=key_type) == b"public_data"
    assert key_storage.get_private_key(keychain_uid=keychain_uid, key_type=key_type) == b"private_data"

    with pytest.raises(KeyDoesNotExist, match="not found"):
        key_storage.get_public_key(keychain_uid=keychain_uid, key_type=key_type + "_")

    with pytest.raises(KeyDoesNotExist, match="not found"):
        key_storage.get_private_key(keychain_uid=keychain_uid, key_type=key_type + "_")

    with pytest.raises(KeyDoesNotExist, match="not found"):
        key_storage.get_public_key(keychain_uid=keychain_uid_other, key_type=key_type)

    with pytest.raises(KeyDoesNotExist, match="not found"):
        key_storage.get_private_key(keychain_uid=keychain_uid_other, key_type=key_type)

    return locals()
コード例 #7
0
def test_generate_uuid0():

    utc = pytz.UTC

    some_date = datetime(year=2000, month=6, day=12, tzinfo=timezone.min)
    some_timestamp = datetime.timestamp(some_date)

    uuid0 = generate_uuid0(some_timestamp)
    assert utc.localize(uuid0.datetime) == some_date
    assert uuid0.datetime_local != some_date.replace(
        tzinfo=None)  # Local TZ is used here
    assert uuid0.unix_ts == some_timestamp

    uuids = [generate_uuid0().int for _ in range(1000)]
    assert len(set(uuids)) == 1000

    uuids = [generate_uuid0(some_timestamp).int for _ in range(1000)]
    assert len(set(uuids)) == 1000

    uuid_test = generate_uuid0(0)
    assert uuid_test.unix_ts != 0  # Can't generate UUIDs with timestamp=0
コード例 #8
0
def _common_authentication_device_initialization(authentication_device: dict,
                                                 user: str,
                                                 extra_metadata: dict):
    assert isinstance(user, str) and user, repr(user)
    metadata_file = _get_metadata_file_path(authentication_device)
    metadata_file.parent.mkdir(parents=True, exist_ok=True)
    metadata = extra_metadata.copy()
    metadata.update({
        "device_uid": generate_uuid0(),
        "user": user
    })  # Override keys!
    dump_to_json_file(metadata_file, metadata)
    return metadata
コード例 #9
0
def fuzz():
    jsonrpc_url = sys.argv[1] if len(
        sys.argv) > 1 else "http://localhost:8000/json/"
    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    for i in range(100):
        keychain_uid = generate_uuid0()
        key_cipher_algo = random.choice(SUPPORTED_ASYMMETRIC_KEY_ALGOS)
        print("Fetching key of type %s... " % key_cipher_algo, end="")
        trustee_proxy.fetch_public_key(keychain_uid=keychain_uid,
                                       key_algo=key_cipher_algo)
        print("DONE")
コード例 #10
0
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
コード例 #11
0
def test_jsonrpc_trustee_signature(live_server):
    jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    keychain_uid = generate_uuid0()
    payload_signature_algo = "DSA_DSS"
    secret = get_random_bytes(101)
    secret_too_big = get_random_bytes(150)

    public_key_signature_pem = trustee_proxy.fetch_public_key(
        keychain_uid=keychain_uid, key_algo=payload_signature_algo)
    public_key_signature = load_asymmetric_key_from_pem_bytestring(
        key_pem=public_key_signature_pem, key_algo=payload_signature_algo)

    signature = trustee_proxy.get_message_signature(
        keychain_uid=keychain_uid,
        message=secret,
        signature_algo=payload_signature_algo)

    with pytest.raises(ValidationError, match="too big"):
        trustee_proxy.get_message_signature(
            keychain_uid=keychain_uid,
            message=secret_too_big,
            signature_algo=payload_signature_algo,
        )

    verify_message_signature(
        message=secret,
        signature=signature,
        key=public_key_signature,
        signature_algo=payload_signature_algo,
    )

    signature["signature_value"] += b"xyz"
    with pytest.raises(SignatureVerificationError,
                       match="not authentic|Incorrect signature"):
        verify_message_signature(
            message=secret,
            signature=signature,
            key=public_key_signature,
            signature_algo=payload_signature_algo,
        )
コード例 #12
0
def generate_asymmetric_keypair_for_storage(
    key_type: str, *, key_storage, keychain_uid: Optional[UUID] = None, passphrase: Optional[AnyStr] = None
) -> dict:
    """
    Shortcut to generate an asymmetric keypair and store it into a key storage.

    `keychain_uid` is auto-generated if not provided.

    Returns the generated keypair dict.
    """
    from wacryptolib.key_generation import generate_asymmetric_keypair
    from wacryptolib.key_storage import KeyStorageBase

    keychain_uid = keychain_uid or generate_uuid0()
    keypair = generate_asymmetric_keypair(key_type=key_type, serialize=True, passphrase=passphrase)
    key_storage.set_keys(
        keychain_uid=keychain_uid,
        key_type=key_type,
        public_key=keypair["public_key"],
        private_key=keypair["private_key"],
    )
    return keypair
コード例 #13
0
def test_escrow_api_workflow():

    key_storage = DummyKeyStorage()
    escrow_api = EscrowApi(key_storage=key_storage)

    keychain_uid = generate_uuid0()
    keychain_uid_other = generate_uuid0()
    keychain_uid_unexisting = generate_uuid0()
    secret = get_random_bytes(127)
    secret_too_big = get_random_bytes(140)

    for _ in range(2):
        generate_free_keypair_for_least_provisioned_key_type(
            key_storage=key_storage,
            max_free_keys_per_type=10,
            key_types=["RSA_OAEP", "DSA_DSS"])
    assert key_storage.get_free_keypairs_count("DSA_DSS") == 1
    assert key_storage.get_free_keypairs_count("ECC_DSS") == 0
    assert key_storage.get_free_keypairs_count("RSA_OAEP") == 1
    assert key_storage.get_free_keypairs_count(
        "RSA_PSS") == 0  # Different from other RSA keys

    # Keypair is well auto-created by get_public_key(), by default
    public_key_rsa_oaep_pem = escrow_api.fetch_public_key(
        keychain_uid=keychain_uid, key_type="RSA_OAEP")

    with pytest.raises(KeyDoesNotExist,
                       match="not found"):  # Key NOT autogenerated
        escrow_api.fetch_public_key(keychain_uid=generate_uuid0(),
                                    key_type="RSA_OAEP",
                                    must_exist=True)

    _public_key_rsa_oaep_pem2 = escrow_api.fetch_public_key(
        keychain_uid=keychain_uid, key_type="RSA_OAEP")
    assert _public_key_rsa_oaep_pem2 == public_key_rsa_oaep_pem  # Same KEYS!

    _public_key_rsa_pss_pem = escrow_api.fetch_public_key(
        keychain_uid=keychain_uid, key_type="RSA_PSS")
    assert _public_key_rsa_pss_pem != public_key_rsa_oaep_pem  # Different KEYS!

    public_key_rsa_oaep = load_asymmetric_key_from_pem_bytestring(
        key_pem=public_key_rsa_oaep_pem, key_type="RSA_OAEP")

    assert key_storage.get_free_keypairs_count("DSA_DSS") == 1
    assert key_storage.get_free_keypairs_count("ECC_DSS") == 0
    assert key_storage.get_free_keypairs_count("RSA_OAEP") == 0  # Taken
    assert key_storage.get_free_keypairs_count("RSA_PSS") == 0

    signature = escrow_api.get_message_signature(keychain_uid=keychain_uid,
                                                 message=secret,
                                                 signature_algo="DSA_DSS")

    with pytest.raises(ValueError, match="too big"):
        escrow_api.get_message_signature(keychain_uid=keychain_uid,
                                         message=secret_too_big,
                                         signature_algo="DSA_DSS")

    assert key_storage.get_free_keypairs_count("DSA_DSS") == 0  # Taken
    assert key_storage.get_free_keypairs_count("ECC_DSS") == 0
    assert key_storage.get_free_keypairs_count("RSA_OAEP") == 0
    assert key_storage.get_free_keypairs_count("RSA_PSS") == 0

    public_key_dsa_pem = escrow_api.fetch_public_key(keychain_uid=keychain_uid,
                                                     key_type="DSA_DSS")
    public_key_dsa = load_asymmetric_key_from_pem_bytestring(
        key_pem=public_key_dsa_pem, key_type="DSA_DSS")

    verify_message_signature(message=secret,
                             signature=signature,
                             key=public_key_dsa,
                             signature_algo="DSA_DSS")
    signature["digest"] += b"xyz"
    with pytest.raises(SignatureVerificationError,
                       match="Failed.*verification"):
        verify_message_signature(message=secret,
                                 signature=signature,
                                 key=public_key_dsa,
                                 signature_algo="DSA_DSS")

    # Keypair is well auto-created by get_message_signature(), even when no more free keys
    signature = escrow_api.get_message_signature(
        keychain_uid=keychain_uid_other,
        message=secret,
        signature_algo="RSA_PSS")
    assert signature

    # Keypair well autocreated by get_public_key(), even when no more free keys
    public_key_pem = escrow_api.fetch_public_key(
        keychain_uid=keychain_uid_other, key_type="DSA_DSS")
    assert public_key_pem

    cipherdict = _encrypt_via_rsa_oaep(plaintext=secret,
                                       key=public_key_rsa_oaep)

    # Works even without decryption authorization request, by default:
    decrypted = escrow_api.decrypt_with_private_key(keychain_uid=keychain_uid,
                                                    encryption_algo="RSA_OAEP",
                                                    cipherdict=cipherdict)
    assert decrypted == secret

    # NO auto-creation of keypair in decrypt_with_private_key()
    with pytest.raises(KeyDoesNotExist, match="not found"):
        escrow_api.decrypt_with_private_key(
            keychain_uid=keychain_uid_unexisting,
            encryption_algo="RSA_OAEP",
            cipherdict=cipherdict)

    wrong_cipherdict = copy.deepcopy(cipherdict)
    wrong_cipherdict["digest_list"].append(b"aaabbbccc")
    with pytest.raises(ValueError, match="Ciphertext with incorrect length"):
        escrow_api.decrypt_with_private_key(keychain_uid=keychain_uid,
                                            encryption_algo="RSA_OAEP",
                                            cipherdict=wrong_cipherdict)

    with pytest.raises(ValueError, match="empty"):
        escrow_api.request_decryption_authorization(
            keypair_identifiers=[], request_message="I need this decryption!")
    # Authorization always granted for now, in dummy implementation
    result = escrow_api.request_decryption_authorization(
        keypair_identifiers=[
            dict(keychain_uid=keychain_uid, key_type="RSA_OAEP")
        ],
        request_message="I need this decryption!",
    )
    assert "accepted" in result["response_message"]
    assert not result["has_errors"]
    assert result["keypair_statuses"]["accepted"]

    # TEST PASSPHRASE PROTECTIONS

    keychain_uid_passphrased = generate_uuid0()
    good_passphrase = "good_passphrase"

    keypair_cipher_passphrased = generate_asymmetric_keypair_for_storage(
        key_type="RSA_OAEP",
        key_storage=key_storage,
        keychain_uid=keychain_uid_passphrased,
        passphrase=good_passphrase)

    result = escrow_api.request_decryption_authorization(
        keypair_identifiers=[
            dict(keychain_uid=keychain_uid_passphrased, key_type="RSA_OAEP")
        ],
        request_message="I need this decryption too!",
    )
    assert "denied" in result["response_message"]
    assert result["has_errors"]
    assert result["keypair_statuses"]["missing_passphrase"]

    result = escrow_api.request_decryption_authorization(
        keypair_identifiers=[
            dict(keychain_uid=keychain_uid_passphrased, key_type="RSA_OAEP")
        ],
        request_message="I need this decryption too!",
        passphrases=["aaa"],
    )
    assert "denied" in result["response_message"]
    assert result["has_errors"]
    assert result["keypair_statuses"]["missing_passphrase"]

    result = escrow_api.request_decryption_authorization(
        keypair_identifiers=[
            dict(keychain_uid=keychain_uid_passphrased, key_type="RSA_OAEP")
        ],
        request_message="I need this decryption too!",
        passphrases=["dsd", good_passphrase],
    )
    assert "accepted" in result["response_message"]
    assert not result["has_errors"]
    assert result["keypair_statuses"]["accepted"]

    public_key_rsa_oaep2 = load_asymmetric_key_from_pem_bytestring(
        key_pem=keypair_cipher_passphrased["public_key"], key_type="RSA_OAEP")
    cipherdict = _encrypt_via_rsa_oaep(plaintext=secret,
                                       key=public_key_rsa_oaep2)

    with pytest.raises(DecryptionError, match="not decrypt"):
        escrow_api.decrypt_with_private_key(
            keychain_uid=keychain_uid_passphrased,
            encryption_algo="RSA_OAEP",
            cipherdict=cipherdict)

    with pytest.raises(DecryptionError, match="not decrypt"):
        escrow_api.decrypt_with_private_key(
            keychain_uid=keychain_uid_passphrased,
            encryption_algo="RSA_OAEP",
            cipherdict=cipherdict,
            passphrases=["something"],
        )

    decrypted = escrow_api.decrypt_with_private_key(
        keychain_uid=keychain_uid_passphrased,
        encryption_algo="RSA_OAEP",
        cipherdict=cipherdict,
        passphrases=[good_passphrase],
    )
    assert decrypted == secret

    assert key_storage.get_free_keypairs_count("DSA_DSS") == 0
    assert key_storage.get_free_keypairs_count("ECC_DSS") == 0
    assert key_storage.get_free_keypairs_count("RSA_OAEP") == 0
    assert key_storage.get_free_keypairs_count("RSA_PSS") == 0
コード例 #14
0
def test_readonly_escrow_api_behaviour():

    key_storage = DummyKeyStorage()
    escrow_api = ReadonlyEscrowApi(key_storage=key_storage)

    keychain_uid = generate_uuid0()
    key_type_cipher = "RSA_OAEP"
    key_type_signature = "RSA_PSS"
    secret = get_random_bytes(127)

    for must_exist in (True, False):
        with pytest.raises(KeyDoesNotExist, match="not found"):
            escrow_api.fetch_public_key(keychain_uid=keychain_uid,
                                        key_type=key_type_signature,
                                        must_exist=must_exist)

    with pytest.raises(KeyDoesNotExist, match="not found"):
        escrow_api.get_message_signature(keychain_uid=keychain_uid,
                                         message=secret,
                                         signature_algo="RSA_PSS")

    # Always accepted for now, dummy implementation
    result = escrow_api.request_decryption_authorization(
        keypair_identifiers=[
            dict(keychain_uid=keychain_uid, key_type=key_type_cipher)
        ],
        request_message="I need this decryption!",
    )
    assert "denied" in result["response_message"]
    assert result["has_errors"]
    assert result["keypair_statuses"]["missing_private_key"]

    # Still no auto-creation of keypair in decrypt_with_private_key()
    with pytest.raises(KeyDoesNotExist, match="not found"):
        escrow_api.decrypt_with_private_key(keychain_uid=keychain_uid,
                                            encryption_algo=key_type_cipher,
                                            cipherdict={})

    # Now we generate wanted keys #

    keypair_cipher = generate_asymmetric_keypair_for_storage(
        key_type=key_type_cipher,
        key_storage=key_storage,
        keychain_uid=keychain_uid)

    keypair_signature = generate_asymmetric_keypair_for_storage(
        key_type=key_type_signature,
        key_storage=key_storage,
        keychain_uid=keychain_uid)

    public_key2 = escrow_api.fetch_public_key(keychain_uid=keychain_uid,
                                              key_type=key_type_signature,
                                              must_exist=must_exist)
    assert public_key2 == keypair_signature["public_key"]

    signature = escrow_api.get_message_signature(keychain_uid=keychain_uid,
                                                 message=secret,
                                                 signature_algo="RSA_PSS")
    assert signature and isinstance(signature, dict)

    private_key_cipher = load_asymmetric_key_from_pem_bytestring(
        key_pem=keypair_cipher["private_key"], key_type=key_type_cipher)
    cipherdict = _encrypt_via_rsa_oaep(plaintext=secret,
                                       key=private_key_cipher)
    decrypted = escrow_api.decrypt_with_private_key(
        keychain_uid=keychain_uid,
        encryption_algo=key_type_cipher,
        cipherdict=cipherdict)
    assert decrypted == secret
コード例 #15
0
    def encrypt_data(self,
                     data: bytes,
                     *,
                     conf: dict,
                     keychain_uid=None,
                     metadata=None) -> dict:
        assert metadata is None or isinstance(metadata, dict), metadata
        container_format = CONTAINER_FORMAT
        container_uid = generate_uuid0()  # ALWAYS UNIQUE!
        keychain_uid = (keychain_uid or generate_uuid0()
                        )  # Might be shared by lots of containers

        conf = copy.deepcopy(conf)  # So that we can manipulate it

        assert isinstance(data, bytes), data
        assert isinstance(conf, dict), conf

        data_current = data  # Initially unencrypted, might remain so if no strata
        result_data_encryption_strata = []

        for data_encryption_stratum in conf["data_encryption_strata"]:
            data_encryption_algo = data_encryption_stratum[
                "data_encryption_algo"]

            logger.debug("Generating symmetric key of type %r",
                         data_encryption_algo)
            symmetric_key = generate_symmetric_key(
                encryption_algo=data_encryption_algo)

            logger.debug("Encrypting data with symmetric key of type %r",
                         data_encryption_algo)

            data_cipherdict = encrypt_bytestring(
                plaintext=data_current,
                encryption_algo=data_encryption_algo,
                key=symmetric_key,
            )
            assert isinstance(data_cipherdict, dict), data_cipherdict
            data_current = dump_to_json_bytes(data_cipherdict)

            symmetric_key_data = (
                symmetric_key
            )  # Initially unencrypted, might remain so if no strata

            result_key_encryption_strata = []
            for key_encryption_stratum in data_encryption_stratum[
                    "key_encryption_strata"]:
                symmetric_key_cipherdict = self._encrypt_symmetric_key(
                    keychain_uid=keychain_uid,
                    symmetric_key_data=symmetric_key_data,
                    conf=key_encryption_stratum,
                )
                symmetric_key_data = dump_to_json_bytes(
                    symmetric_key_cipherdict)  # Remain as bytes all along
                result_key_encryption_strata.append(
                    key_encryption_stratum)  # Unmodified for now

            data_signatures = []
            for signature_conf in data_encryption_stratum["data_signatures"]:
                signature_value = self._generate_signature(
                    keychain_uid=keychain_uid,
                    data_ciphertext=data_current,
                    conf=signature_conf,
                )
                signature_conf["signature_value"] = signature_value
                data_signatures.append(signature_conf)

            result_data_encryption_strata.append(
                dict(
                    data_encryption_algo=data_encryption_algo,
                    key_ciphertext=symmetric_key_data,
                    key_encryption_strata=result_key_encryption_strata,
                    data_signatures=data_signatures,
                ))

        data_ciphertext = (
            data_current
        )  # New fully encrypted (unless data_encryption_strata is empty)

        return dict(
            container_format=container_format,
            container_uid=container_uid,
            keychain_uid=keychain_uid,
            data_ciphertext=data_ciphertext,
            data_encryption_strata=result_data_encryption_strata,
            metadata=metadata,
        )
コード例 #16
0
def test_key_storage_list_keypair_identifiers(tmp_path: Path):
    def _check_key_dict_format(key):
        print(">> public key detected:", key)

        assert isinstance(key["keychain_uid"], UUID)
        assert key["key_type"] in SUPPORTED_ASYMMETRIC_KEY_TYPES
        assert isinstance(key["private_key_present"], bool)

    key_storage = FilesystemKeyStorage(tmp_path)
    assert key_storage.list_keypair_identifiers() == []

    # CASE 1 : only one key in storage

    key_type = random.choice(SUPPORTED_ASYMMETRIC_KEY_TYPES)

    keychain_uid = generate_uuid0()
    generate_asymmetric_keypair_for_storage(key_type=key_type,
                                            key_storage=key_storage,
                                            keychain_uid=keychain_uid)

    keys_list = key_storage.list_keypair_identifiers()
    assert isinstance(keys_list, list)
    assert len(keys_list) == 1

    single_key = keys_list[0]
    _check_key_dict_format(single_key)
    assert single_key["keychain_uid"] == keychain_uid
    assert single_key["key_type"] == key_type
    assert single_key["private_key_present"]

    # CASE 2 : multiple public keys, with or without private keys

    for i in range(3):
        _key_type = random.choice(SUPPORTED_ASYMMETRIC_KEY_TYPES)
        generate_asymmetric_keypair_for_storage(key_type=_key_type,
                                                key_storage=key_storage,
                                                passphrase="xzf".encode())

    for bad_filename in (
            "0e896f1d-a4d0-67d6-7286-056f1ec342e8_RSA_OAEP_public_key.dot",
            "0e896f1d-a4d0-67d6-7286-056f1ec342e8_RSA_OAEP_publicX_key.pem",
            "a4d0-67d6-7286-056f1ec342e8_RSA_OAEP_public_key.pem",
            "WRONGPREFIX_public_key.pem",
    ):
        tmp_path.joinpath(
            bad_filename).touch()  # These will be ignored thanks to Regex

    keys_list = key_storage.list_keypair_identifiers()
    assert isinstance(keys_list, list)
    assert len(keys_list) == 4
    assert keys_list == sorted(
        keys_list, key=lambda x:
        (x["keychain_uid"], x["key_type"]))  # Well sorted

    for some_key in keys_list:
        _check_key_dict_format(some_key)
        assert single_key["private_key_present"]  # ALWAYS for now

    for filepath in tmp_path.glob("*" +
                                  FilesystemKeyStorage._private_key_suffix):
        filepath.unlink()

    keys_list = key_storage.list_keypair_identifiers()
    assert isinstance(keys_list, list)
    assert len(keys_list) == 4

    for some_key in keys_list:
        _check_key_dict_format(some_key)
        assert not some_key["private_key_present"]  # Private keys were deleted

    # CASE 3 : keys all deleted

    for filepath in tmp_path.glob("*.pem"):
        filepath.unlink()

    assert key_storage.list_keypair_identifiers() == []
コード例 #17
0
def test_jsonrpc_trustee_request_decryption_authorization_for_normal_keys(
        live_server):
    jsonrpc_url = jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    key_cipher_algo = "RSA_OAEP"

    with freeze_time(
    ) as frozen_datetime:  # TEST AUTHORIZATION REQUEST HANDLING

        keychain_uid1 = generate_uuid0()
        keychain_uid2 = generate_uuid0()
        keychain_uid3 = generate_uuid0()
        keychain_uid4 = generate_uuid0()
        keychain_uid_unexisting = generate_uuid0()

        all_keypair_identifiers = [
            dict(keychain_uid=keychain_uid1, key_algo=key_cipher_algo),
            dict(keychain_uid=keychain_uid2, key_algo=key_cipher_algo),
            dict(keychain_uid=keychain_uid3, key_algo=key_cipher_algo),
            dict(keychain_uid=keychain_uid4, key_algo=key_cipher_algo),
            dict(keychain_uid=keychain_uid_unexisting,
                 key_algo=key_cipher_algo),
        ]

        public_key_pem = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid1, key_algo=key_cipher_algo)
        assert public_key_pem
        assert not _fetch_key_object_or_raise(
            keychain_uid=keychain_uid1,
            key_algo=key_cipher_algo).decryption_authorized_at

        # Non-pregenerated keys don't have that field set!
        assert not _fetch_key_object_or_raise(
            keychain_uid=keychain_uid1, key_algo=key_cipher_algo).attached_at

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=[], request_message="I want decryption!")
        assert result["success_count"] == 0
        assert result["too_old_count"] == 0
        assert result["not_found_count"] == 0

        frozen_datetime.tick(delta=timedelta(minutes=2))

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_keypair_identifiers,
            request_message="I want decryption!",
        )
        assert result["success_count"] == 1
        assert result["too_old_count"] == 0
        assert (result["not_found_count"] == 4
                )  # keychain_uid2 and keychain_uid3 not created yet

        old_decryption_authorized_at = _fetch_key_object_or_raise(
            keychain_uid=keychain_uid1,
            key_algo=key_cipher_algo).decryption_authorized_at
        assert old_decryption_authorized_at

        public_key_pem = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid2, key_algo=key_cipher_algo)
        assert public_key_pem
        public_key_pem = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid3, key_algo=key_cipher_algo)
        assert public_key_pem

        frozen_datetime.tick(delta=timedelta(minutes=4))

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_keypair_identifiers,
            request_message="I want decryption!",
        )
        assert result["success_count"] == 2
        assert result["too_old_count"] == 1
        assert result["not_found_count"] == 2

        assert (_fetch_key_object_or_raise(
            keychain_uid=keychain_uid1,
            key_algo=key_cipher_algo).decryption_authorized_at ==
                old_decryption_authorized_at)  # Unchanged
        assert _fetch_key_object_or_raise(
            keychain_uid=keychain_uid2,
            key_algo=key_cipher_algo).decryption_authorized_at
        assert _fetch_key_object_or_raise(
            keychain_uid=keychain_uid3,
            key_algo=key_cipher_algo).decryption_authorized_at

        with pytest.raises(KeyDoesNotExist, match="not found"):
            _fetch_key_object_or_raise(keychain_uid=keychain_uid_unexisting,
                                       key_algo=key_cipher_algo)

        public_key_pem = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid4, key_algo=key_cipher_algo)
        assert public_key_pem

        frozen_datetime.tick(delta=timedelta(minutes=6))

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_keypair_identifiers,
            request_message="I want decryption!",
        )
        assert result["success_count"] == 0
        assert result["too_old_count"] == 4
        assert result["not_found_count"] == 1

        assert (_fetch_key_object_or_raise(
            keychain_uid=keychain_uid1,
            key_algo=key_cipher_algo).decryption_authorized_at ==
                old_decryption_authorized_at)  # Unchanged

    del all_keypair_identifiers
コード例 #18
0
def test_jsonrpc_trustee_request_decryption_authorization_for_free_keys(
        live_server):
    jsonrpc_url = jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    keychain_uid_free = generate_uuid0()
    free_key_algo1 = "RSA_OAEP"
    free_key_algo2 = "ECC_DSS"
    free_key_algo3 = "DSA_DSS"

    all_requested_keypair_identifiers = [
        dict(keychain_uid=keychain_uid_free, key_algo=free_key_algo1),
        dict(keychain_uid=keychain_uid_free, key_algo=free_key_algo2),
    ]

    sql_keystore = SqlKeystore()

    with freeze_time(
    ) as frozen_datetime:  # TEST RELATION WITH FREE KEYS ATTACHMENT

        for i in range(3):  # Generate 1 free keypair per type
            has_generated = generate_free_keypair_for_least_provisioned_key_algo(
                keystore=sql_keystore,
                max_free_keys_per_algo=1,
                key_algos=[free_key_algo1, free_key_algo2, free_key_algo3])
            assert has_generated

        keys_generated_before_datetime = timezone.now()

        public_key_pem1 = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo1)
        assert public_key_pem1

        # This key will not have early-enough request for authorization
        public_key_pem3 = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo3)
        assert public_key_pem3

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_requested_keypair_identifiers,
            request_message="I want early decryption!",
        )
        assert result["success_count"] == 1
        assert result["too_old_count"] == 0
        assert result[
            "not_found_count"] == 1  # free_key_algo2 is not attached yet

        frozen_datetime.tick(delta=timedelta(minutes=6))

        public_key_pem2 = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo2)
        assert public_key_pem2

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_requested_keypair_identifiers,
            request_message="I want later decryption!",
        )
        assert result[
            "success_count"] == 1  # It's attachment time which counts!
        assert result["too_old_count"] == 1  # First key is too old now
        assert result["not_found_count"] == 0

        keypair_obj = TrusteeKeypair.objects.get(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo1)
        assert keypair_obj.created_at <= keys_generated_before_datetime
        assert keypair_obj.attached_at
        assert keypair_obj.decryption_authorized_at
        first_authorized_at = keypair_obj.decryption_authorized_at

        keypair_obj = TrusteeKeypair.objects.get(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo2)
        assert keypair_obj.created_at <= keys_generated_before_datetime
        assert keypair_obj.attached_at
        assert keypair_obj.decryption_authorized_at
        assert keypair_obj.decryption_authorized_at >= first_authorized_at + timedelta(
            minutes=5)

        keypair_obj = TrusteeKeypair.objects.get(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo3)
        assert keypair_obj.created_at <= keys_generated_before_datetime
        assert keypair_obj.attached_at
        assert not keypair_obj.decryption_authorized_at  # Never requested
コード例 #19
0
def test_jsonrpc_trustee_encrypt_decrypt_cryptainer(live_server):
    jsonrpc_url = jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    cryptoconf = dict(payload_cipher_layers=[
        # First we encrypt with local key and sign via main remote trustee
        dict(
            payload_cipher_algo="AES_EAX",
            key_cipher_layers=[
                dict(key_cipher_algo="RSA_OAEP",
                     key_cipher_trustee=dict(
                         trustee_type=CRYPTAINER_TRUSTEE_TYPES.
                         JSONRPC_API_TRUSTEE,
                         url=jsonrpc_url))
            ],
            payload_signatures=[
                dict(
                    payload_digest_algo="SHA512",
                    payload_signature_algo="DSA_DSS",
                    payload_signature_trustee=dict(
                        trustee_type=CRYPTAINER_TRUSTEE_TYPES.
                        JSONRPC_API_TRUSTEE,
                        url=jsonrpc_url),
                )
            ],
        )
    ])

    # CASE 1: authorization request well sent a short time after creation of "keychain_uid" keypair, so decryption is accepted

    with freeze_time() as frozen_datetime:
        keychain_uid = generate_uuid0()
        payload = get_random_bytes(101)

        cryptainer = encrypt_payload_into_cryptainer(
            payload=payload,
            cryptoconf=cryptoconf,
            cryptainer_metadata=None,
            keychain_uid=keychain_uid,
            keystore_pool=None,  # Unused by this config actually
        )

        frozen_datetime.tick(delta=timedelta(minutes=3))

        with pytest.raises(AuthorizationError):
            decrypt_payload_from_cryptainer(cryptainer=cryptainer,
                                            keystore_pool=None)

        # Access automatically granted for now, with this trustee, when keys are young
        trustee_dependencies = gather_trustee_dependencies(
            cryptainers=[cryptainer])
        decryption_authorization_requests_result = request_decryption_authorizations(
            trustee_dependencies,
            keystore_pool=None,
            request_message="I need access to this")
        print(">>>>> request_decryption_authorizations is",
              decryption_authorization_requests_result)

        decrypted_data = decrypt_payload_from_cryptainer(cryptainer=cryptainer,
                                                         keystore_pool=None)
        assert decrypted_data == payload

        frozen_datetime.tick(
            delta=timedelta(hours=23)
        )  # Once authorization is granted, it stays so for a long time
        decrypted_data = decrypt_payload_from_cryptainer(cryptainer=cryptainer,
                                                         keystore_pool=None)
        assert decrypted_data == payload

        frozen_datetime.tick(
            delta=timedelta(hours=2)
        )  # Authorization has expired, and grace period to get one has long expired
        with pytest.raises(
                AuthorizationError,
                match="Decryption authorization is only valid from"):
            decrypt_payload_from_cryptainer(cryptainer=cryptainer,
                                            keystore_pool=None)

    # CASE 2: authorization request sent too late after creation of "keychain_uid" keypair, so decryption is rejected

    with freeze_time() as frozen_datetime:
        keychain_uid = generate_uuid0()
        data = get_random_bytes(101)
        local_keystore = DummyKeystore()

        cryptainer = encrypt_payload_into_cryptainer(
            payload=payload,
            cryptoconf=cryptoconf,
            cryptainer_metadata=None,
            keychain_uid=keychain_uid,
            keystore_pool=None,  # Unused by this config actually
        )

        frozen_datetime.tick(
            delta=timedelta(minutes=6))  # More than the 5 minutes grace period
        with pytest.raises(AuthorizationError,
                           match="Decryption not authorized"):
            decrypt_payload_from_cryptainer(cryptainer=cryptainer,
                                            keystore_pool=None)
コード例 #20
0
def test_escrow_api_workflow():

    key_storage = DummyKeyStorage()
    escrow_api = EscrowApi(key_storage=key_storage)

    keychain_uid = generate_uuid0()
    keychain_uid_other = generate_uuid0()
    keychain_uid_unexisting = generate_uuid0()
    secret = get_random_bytes(127)
    secret_too_big = get_random_bytes(140)

    for _ in range(2):
        generate_free_keypair_for_least_provisioned_key_type(
            key_storage=key_storage,
            max_free_keys_per_type=10,
            key_types=["RSA_OAEP", "DSA_DSS"],
        )
    assert key_storage.get_free_keypairs_count("DSA_DSS") == 1
    assert key_storage.get_free_keypairs_count("ECC_DSS") == 0
    assert key_storage.get_free_keypairs_count("RSA_OAEP") == 1
    assert (
        key_storage.get_free_keypairs_count("RSA_PSS") == 0
    )  # Different from other RSA keys

    # Keypair is well auto-created by get_public_key()
    public_key_rsa_oaep_pem = escrow_api.get_public_key(
        keychain_uid=keychain_uid, key_type="RSA_OAEP"
    )

    _public_key_rsa_oaep_pem2 = escrow_api.get_public_key(
        keychain_uid=keychain_uid, key_type="RSA_OAEP"
    )
    assert _public_key_rsa_oaep_pem2 == public_key_rsa_oaep_pem  # Same KEYS!

    _public_key_rsa_pss_pem = escrow_api.get_public_key(
        keychain_uid=keychain_uid, key_type="RSA_PSS"
    )
    assert _public_key_rsa_pss_pem != public_key_rsa_oaep_pem  # Different KEYS!

    public_key_rsa_oaep = load_asymmetric_key_from_pem_bytestring(
        key_pem=public_key_rsa_oaep_pem, key_type="RSA_OAEP"
    )

    assert key_storage.get_free_keypairs_count("DSA_DSS") == 1
    assert key_storage.get_free_keypairs_count("ECC_DSS") == 0
    assert key_storage.get_free_keypairs_count("RSA_OAEP") == 0  # Taken
    assert key_storage.get_free_keypairs_count("RSA_PSS") == 0

    signature = escrow_api.get_message_signature(
        keychain_uid=keychain_uid, message=secret, signature_algo="DSA_DSS"
    )

    with pytest.raises(ValueError, match="too big"):
        escrow_api.get_message_signature(
            keychain_uid=keychain_uid, message=secret_too_big, signature_algo="DSA_DSS"
        )

    assert key_storage.get_free_keypairs_count("DSA_DSS") == 0  # Taken
    assert key_storage.get_free_keypairs_count("ECC_DSS") == 0
    assert key_storage.get_free_keypairs_count("RSA_OAEP") == 0
    assert key_storage.get_free_keypairs_count("RSA_PSS") == 0

    public_key_dsa_pem = escrow_api.get_public_key(
        keychain_uid=keychain_uid, key_type="DSA_DSS"
    )
    public_key_dsa = load_asymmetric_key_from_pem_bytestring(
        key_pem=public_key_dsa_pem, key_type="DSA_DSS"
    )

    verify_message_signature(
        message=secret,
        signature=signature,
        key=public_key_dsa,
        signature_algo="DSA_DSS",
    )
    signature["digest"] += b"xyz"
    with pytest.raises(ValueError, match="not authentic"):
        verify_message_signature(
            message=secret,
            signature=signature,
            key=public_key_dsa,
            signature_algo="DSA_DSS",
        )

    # Keypair is well auto-created by get_message_signature(), even when no more free keys
    signature = escrow_api.get_message_signature(
        keychain_uid=keychain_uid_other, message=secret, signature_algo="RSA_PSS"
    )
    assert signature

    # Keypair well autocreated by get_public_key(), even when no more free keys
    public_key_pem = escrow_api.get_public_key(
        keychain_uid=keychain_uid_other, key_type="DSA_DSS"
    )
    assert public_key_pem

    cipherdict = _encrypt_via_rsa_oaep(plaintext=secret, key=public_key_rsa_oaep)

    # Works even without decryption authorization request, by default:
    decrypted = escrow_api.decrypt_with_private_key(
        keychain_uid=keychain_uid, encryption_algo="RSA_OAEP", cipherdict=cipherdict
    )

    # NO auto-creation of keypair in decrypt_with_private_key()
    with pytest.raises(ValueError, match="Unexisting"):
        escrow_api.decrypt_with_private_key(
            keychain_uid=keychain_uid_unexisting,
            encryption_algo="RSA_OAEP",
            cipherdict=cipherdict,
        )

    cipherdict["digest_list"].append(b"aaabbbccc")
    with pytest.raises(ValueError, match="Ciphertext with incorrect length"):
        escrow_api.decrypt_with_private_key(
            keychain_uid=keychain_uid, encryption_algo="RSA_OAEP", cipherdict=cipherdict
        )

    assert decrypted == secret

    result = escrow_api.request_decryption_authorization(
        keypair_identifiers=[(keychain_uid, "RSA_OAEP")],
        request_message="I need this decryption!",
    )
    assert result["response_message"]

    with pytest.raises(ValueError, match="empty"):
        escrow_api.request_decryption_authorization(
            keypair_identifiers=[], request_message="I need this decryption!"
        )

    assert key_storage.get_free_keypairs_count("DSA_DSS") == 0
    assert key_storage.get_free_keypairs_count("ECC_DSS") == 0
    assert key_storage.get_free_keypairs_count("RSA_OAEP") == 0
    assert key_storage.get_free_keypairs_count("RSA_PSS") == 0
コード例 #21
0
def test_jsonrpc_trustee_decryption_authorization_flags(live_server):
    jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    keychain_uid = generate_uuid0()
    keychain_uid_bad = generate_uuid0()
    key_cipher_algo = "RSA_OAEP"
    secret = get_random_bytes(101)

    public_encryption_key_pem = trustee_proxy.fetch_public_key(
        keychain_uid=keychain_uid, key_algo=key_cipher_algo)
    public_encryption_key = load_asymmetric_key_from_pem_bytestring(
        key_pem=public_encryption_key_pem, key_algo=key_cipher_algo)

    cipherdict = _encrypt_via_rsa_oaep(
        plaintext=secret, key_dict=dict(key=public_encryption_key))

    def _attempt_decryption():
        return trustee_proxy.decrypt_with_private_key(
            keychain_uid=keychain_uid,
            cipher_algo=key_cipher_algo,
            cipherdict=cipherdict,
        )

    with freeze_time() as frozen_datetime:
        with pytest.raises(AuthorizationError,
                           match="Decryption not authorized"):
            _attempt_decryption()

        keypair_obj = TrusteeKeypair.objects.get(keychain_uid=keychain_uid,
                                                 key_algo=key_cipher_algo)
        keypair_obj.decryption_authorized_at = timezone.now() + timedelta(
            hours=2)
        keypair_obj.save()

        with pytest.raises(
                AuthorizationError,
                match="Decryption authorization is only valid from"):
            _attempt_decryption()  # Too early

        frozen_datetime.tick(delta=timedelta(hours=3))

        decrypted = _attempt_decryption()
        assert decrypted == secret  # It works!

        with pytest.raises(KeyDoesNotExist, match="not found"):
            trustee_proxy.decrypt_with_private_key(
                keychain_uid=keychain_uid_bad,
                cipher_algo=key_cipher_algo,
                cipherdict=cipherdict,
            )

        cipherdict["digest_list"].append(b"aaabbbccc")
        with pytest.raises(DecryptionError,
                           match="Ciphertext with incorrect length"):
            trustee_proxy.decrypt_with_private_key(
                keychain_uid=keychain_uid,
                cipher_algo=key_cipher_algo,
                cipherdict=cipherdict,
            )

        frozen_datetime.tick(delta=timedelta(
            hours=24))  # We hardcode DECRYPTION_AUTHORIZATION_LIFESPAN_H here

        with pytest.raises(
                AuthorizationError,
                match="Decryption authorization is only valid from"):
            _attempt_decryption(
            )  # Too late, cipherdict is not even used so no ValueError

        keypair_obj.decryption_authorized_at = None
        keypair_obj.save()

        with pytest.raises(AuthorizationError,
                           match="Decryption not authorized"):
            _attempt_decryption()  # No more authorization at all
コード例 #22
0
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