async def test_decrypt_argument_validation(): mock_client = mock.Mock() key = mock.Mock( spec=KeyVaultKey, id="https://localhost/fake/key/version", properties=mock.Mock(not_before=datetime(2000, 1, 1, tzinfo=_UTC), expires_on=datetime(3000, 1, 1, tzinfo=_UTC)), ) client = CryptographyClient(key, mock.Mock()) client._client = mock_client with pytest.raises(ValueError) as ex: await client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...", iv=b"...") assert "iv" in str(ex.value) with pytest.raises(ValueError) as ex: await client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...", additional_authenticated_data=b"...") assert "additional_authenticated_data" in str(ex.value) with pytest.raises(ValueError) as ex: await client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...", authentication_tag=b"...") assert "authentication_tag" in str(ex.value) with pytest.raises(ValueError) as ex: await client.decrypt(EncryptionAlgorithm.a128_gcm, b"...", iv=b"...") assert "authentication_tag" in str(ex.value) and "required" in str( ex.value) with pytest.raises(ValueError) as ex: await client.decrypt(EncryptionAlgorithm.a192_cbcpad, b"...") assert "iv" in str(ex.value) and "required" in str(ex.value)
async def test_initialization_get_key_successful(): """If the client is able to get key material, it shouldn't do so again""" key_id = "https://localhost/fake/key/version" mock_key = mock.Mock() mock_key.key.kid = key_id mock_client = mock.Mock() mock_client.get_key.return_value = get_completed_future(mock_key) client = CryptographyClient(key_id, mock.Mock()) client._client = mock_client assert mock_client.get_key.call_count == 0 with mock.patch(CryptographyClient.__module__ + ".get_local_cryptography_provider") as get_provider: await client.verify(SignatureAlgorithm.rs256, b"...", b"...") args, _ = get_provider.call_args assert len(args) == 1 and isinstance(args[0], JsonWebKey) and args[0].kid == key_id for _ in range(3): assert mock_client.get_key.call_count == 1 assert get_provider.call_count == 1 await client.verify(SignatureAlgorithm.rs256, b"...", b"...")
async def test_encrypt_decrypt_async(self, azure_keyvault_url, **kwargs): key_client = self.create_key_client(azure_keyvault_url) credential = self.get_crypto_client_credential() key_name = self.get_resource_name("crypto-test-encrypt-key") await key_client.create_rsa_key(key_name) # [START create_client] # create a CryptographyClient using a KeyVaultKey instance key = await key_client.get_key(key_name) crypto_client = CryptographyClient(key, credential) # or a key's id, which must include a version key_id = "https://<your vault>.vault.azure.net/keys/<key name>/fe4fdcab688c479a9aa80f01ffeac26" crypto_client = CryptographyClient(key_id, credential) # [END create_client] client = CryptographyClient(key, credential) # [START encrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm # the result holds the ciphertext and identifies the encryption key and algorithm used result = await client.encrypt(EncryptionAlgorithm.rsa_oaep, b"plaintext") print(result.key_id) print(result.algorithm) ciphertext = result.ciphertext # [END encrypt] # [START decrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm result = await client.decrypt(EncryptionAlgorithm.rsa_oaep, ciphertext) print(result.plaintext)
async def test_initialization_transient_failure_getting_key(): """If the client is not forbidden to get key material, it should retry after failing to do so""" mock_client = mock.Mock() mock_client.get_key.side_effect = HttpResponseError(response=mock.Mock(status_code=500)) mock_client.verify.return_value = get_completed_future(mock.Mock()) client = CryptographyClient("https://localhost/fake/key/version", mock.Mock()) client._client = mock_client for i in range(3): assert mock_client.get_key.call_count == i await client.verify(SignatureAlgorithm.rs256, b"...", b"...")
async def test_initialization_given_key(): """If the client is given key material, it should not attempt to get this from the vault""" mock_client = mock.Mock() key = mock.Mock(spec=KeyVaultKey, id="https://localhost/fake/key/version") client = CryptographyClient(key, mock.Mock()) client._client = mock_client mock_client.get_key.return_value = get_completed_future() with mock.patch(CryptographyClient.__module__ + ".get_local_cryptography_provider") as get_provider: await client.verify(SignatureAlgorithm.rs256, b"...", b"...") get_provider.assert_called_once_with(key) assert mock_client.get_key.call_count == 0
async def test_ec_key_id(self, key_client, credential, **kwargs): """When initialized with a key ID, the client should retrieve the key and perform public operations locally""" key = await key_client.create_ec_key(self.create_random_name("eckey")) crypto_client = CryptographyClient(key.id, credential) await crypto_client._initialize() assert crypto_client.key_id == key.id # ensure all remote crypto operations will fail crypto_client._client = None await crypto_client.verify(SignatureAlgorithm.es256, hashlib.sha256(self.plaintext).digest(), self.plaintext)
async def test_initialization_forbidden_to_get_key(): """If the client is forbidden to get key material, it should try to do so exactly once""" mock_client = mock.Mock() mock_client.get_key.side_effect = HttpResponseError(response=mock.Mock(status_code=403)) mock_client.verify.return_value = get_completed_future(mock.Mock()) client = CryptographyClient("https://localhost/fake/key/version", mock.Mock()) client._client = mock_client assert mock_client.get_key.call_count == 0 for _ in range(3): await client.verify(SignatureAlgorithm.rs256, b"...", b"...") assert mock_client.get_key.call_count == 1
async def test_encrypt_local_from_jwk(self, **kwargs): """Encrypt locally, decrypt with Key Vault""" is_hsm = kwargs.pop("is_hsm") self._skip_if_not_configured(is_hsm) endpoint_url = self.managed_hsm_url if is_hsm else self.vault_url key_client = self.create_key_client(endpoint_url, is_async=True) key_name = self.get_resource_name("encrypt-local") key = await self._create_rsa_key(key_client, key_name, size=4096, hardware_protected=is_hsm) crypto_client = self.create_crypto_client(key, is_async=True) local_client = CryptographyClient.from_jwk(key.key) rsa_encrypt_algorithms = [ algo for algo in EncryptionAlgorithm if algo.startswith("RSA") ] for encrypt_algorithm in rsa_encrypt_algorithms: result = await local_client.encrypt(encrypt_algorithm, self.plaintext) self.assertEqual(result.key_id, key.id) result = await crypto_client.decrypt(result.algorithm, result.ciphertext) self.assertEqual(result.plaintext, self.plaintext)
async def test_ec_verify_local(self, key_client, credential, **kwargs): """Sign with Key Vault, verify locally""" matrix = { KeyCurveName.p_256: (SignatureAlgorithm.es256, hashlib.sha256), KeyCurveName.p_256_k: (SignatureAlgorithm.es256_k, hashlib.sha256), KeyCurveName.p_384: (SignatureAlgorithm.es384, hashlib.sha384), KeyCurveName.p_521: (SignatureAlgorithm.es512, hashlib.sha512), } for curve, (signature_algorithm, hash_function) in sorted(matrix.items()): key = await key_client.create_ec_key("ec-verify-{}".format( curve.value), curve=curve) crypto_client = CryptographyClient(key, credential) digest = hash_function(self.plaintext).digest() result = await crypto_client.sign(signature_algorithm, digest) self.assertEqual(result.key_id, key.id) result = await crypto_client.verify(result.algorithm, digest, result.signature) self.assertTrue(result.is_valid)
async def test_local_only_mode_no_service_calls(): """A local-only CryptographyClient shouldn't call the service if an operation can't be performed locally""" mock_client = mock.Mock() jwk = JsonWebKey(kty="RSA", key_ops=[], n=b"10011", e=b"10001") client = CryptographyClient.from_jwk(jwk=jwk) client._client = mock_client with pytest.raises(NotImplementedError): await client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.decrypt.call_count == 0 with pytest.raises(NotImplementedError): await client.encrypt(EncryptionAlgorithm.a256_gcm, b"...") assert mock_client.encrypt.call_count == 0 with pytest.raises(NotImplementedError): await client.sign(SignatureAlgorithm.rs256, b"...") assert mock_client.sign.call_count == 0 with pytest.raises(NotImplementedError): await client.verify(SignatureAlgorithm.es256, b"...", b"...") assert mock_client.verify.call_count == 0 with pytest.raises(NotImplementedError): await client.unwrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.unwrap_key.call_count == 0 with pytest.raises(NotImplementedError): await client.wrap_key(KeyWrapAlgorithm.aes_256, b"...") assert mock_client.wrap_key.call_count == 0
async def test_encrypt_decrypt_async(self, key_client, credential, **kwargs): key_name = self.get_resource_name("crypto-test-encrypt-key") key = await key_client.create_rsa_key(key_name) client = CryptographyClient(key, credential) # [START encrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm # encrypt returns a tuple with the ciphertext and the metadata required to decrypt it result = await client.encrypt(EncryptionAlgorithm.rsa_oaep, b"plaintext") print(result.key_id) print(result.algorithm) ciphertext = result.ciphertext # [END encrypt] # [START decrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm result = await client.decrypt(EncryptionAlgorithm.rsa_oaep, ciphertext) print(result.plaintext) # [END decrypt] pass
async def test_wrap_unwrap_async(self, key_client, **kwargs): credential = self.get_credential(CryptographyClient, is_async=True) key_name = self.get_resource_name("crypto-test-wrapping-key") key = await key_client.create_rsa_key(key_name) client = CryptographyClient(key, credential, api_version=key_client.api_version) key_bytes = b"5063e6aaa845f150200547944fd199679c98ed6f99da0a0b2dafeaf1f4684496fd532c1c229968cb9dee44957fcef7ccef59ceda0b362e56bcd78fd3faee5781c623c0bb22b35beabde0664fd30e0e824aba3dd1b0afffc4a3d955ede20cf6a854d52cfd" # [START wrap_key] from azure.keyvault.keys.crypto import KeyWrapAlgorithm # wrap returns a tuple with the wrapped bytes and the metadata required to unwrap the key result = await client.wrap_key(KeyWrapAlgorithm.rsa_oaep, key_bytes) print(result.key_id) print(result.algorithm) encrypted_key = result.encrypted_key # [END wrap_key] # [START unwrap_key] from azure.keyvault.keys.crypto import KeyWrapAlgorithm result = await client.unwrap_key(KeyWrapAlgorithm.rsa_oaep, encrypted_key)
async def test_sign_verify_async(self, key_client, credential, **kwargs): key_name = self.get_resource_name("crypto-test-wrapping-key") key = await key_client.create_rsa_key(key_name) client = CryptographyClient(key, credential) # [START sign] import hashlib from azure.keyvault.keys.crypto import SignatureAlgorithm digest = hashlib.sha256(b"plaintext").digest() # sign returns a tuple with the signature and the metadata required to verify it result = await client.sign(SignatureAlgorithm.rs256, digest) print(result.key_id) print(result.algorithm) signature = result.signature # [END sign] # [START verify] from azure.keyvault.keys.crypto import SignatureAlgorithm verified = await client.verify(SignatureAlgorithm.rs256, digest, signature) assert verified.is_valid
async def test_rsa_verify_local_from_jwk(self, key_client, is_hsm, **kwargs): """Sign with Key Vault, verify locally""" for size in (2048, 3072, 4096): key_name = self.get_resource_name("rsa-verify-{}".format(size)) key = await self._create_rsa_key(key_client, key_name, size=size, hardware_protected=is_hsm) crypto_client = self.create_crypto_client( key, is_async=True, api_version=key_client.api_version) local_client = CryptographyClient.from_jwk(key.key) for signature_algorithm, hash_function in ( (SignatureAlgorithm.ps256, hashlib.sha256), (SignatureAlgorithm.ps384, hashlib.sha384), (SignatureAlgorithm.ps512, hashlib.sha512), (SignatureAlgorithm.rs256, hashlib.sha256), (SignatureAlgorithm.rs384, hashlib.sha384), (SignatureAlgorithm.rs512, hashlib.sha512), ): digest = hash_function(self.plaintext).digest() result = await crypto_client.sign(signature_algorithm, digest) self.assertEqual(result.key_id, key.id) result = await local_client.verify(result.algorithm, digest, result.signature) self.assertTrue(result.is_valid)
async def test_ec_verify_local_from_jwk(self, key_client, is_hsm, **kwargs): """Sign with Key Vault, verify locally""" matrix = { KeyCurveName.p_256: (SignatureAlgorithm.es256, hashlib.sha256), KeyCurveName.p_256_k: (SignatureAlgorithm.es256_k, hashlib.sha256), KeyCurveName.p_384: (SignatureAlgorithm.es384, hashlib.sha384), KeyCurveName.p_521: (SignatureAlgorithm.es512, hashlib.sha512), } for curve, (signature_algorithm, hash_function) in sorted(matrix.items()): key_name = self.get_resource_name("ec-verify-{}".format( curve.value)) key = await self._create_ec_key(key_client, key_name, curve=curve, hardware_protected=is_hsm) crypto_client = self.create_crypto_client( key, is_async=True, api_version=key_client.api_version) local_client = CryptographyClient.from_jwk(key.key) digest = hash_function(self.plaintext).digest() result = await crypto_client.sign(signature_algorithm, digest) self.assertEqual(result.key_id, key.id) result = await local_client.verify(result.algorithm, digest, result.signature) self.assertTrue(result.is_valid)
async def test_aes_cbc_iv_validation(): """The client should raise an error when an iv is not provided""" jwk = JsonWebKey(kty="oct-HSM", key_ops=["encrypt", "decrypt"], k=os.urandom(32)) client = CryptographyClient.from_jwk(jwk=jwk) with pytest.raises(ValueError) as ex: await client.encrypt(EncryptionAlgorithm.a256_cbcpad, b"...") assert "iv" in str(ex.value).lower()
async def test_crypto_client_close(): transport = AsyncMockTransport() client = CryptographyClient(key="https://localhost/a/b/c", credential=object(), transport=transport) await client.close() assert transport.__aenter__.call_count == 0 assert transport.__aexit__.call_count == 1
async def test_crypto_client_context_manager(): transport = AsyncMockTransport() client = CryptographyClient(key="https://localhost/a/b/c", credential=object(), transport=transport) async with client: assert transport.__aenter__.call_count == 1 assert transport.__aenter__.call_count == 1 assert transport.__aexit__.call_count == 1
def test_custom_hook_policy(): class CustomHookPolicy(SansIOHTTPPolicy): pass client = CryptographyClient("https://localhost/fake/key/version", object(), custom_hook_policy=CustomHookPolicy()) assert isinstance(client._client._config.custom_hook_policy, CustomHookPolicy)
async def test_encrypt_argument_validation(): """The client should raise an error when arguments don't work with the specified algorithm""" mock_client = mock.Mock() key = mock.Mock( spec=KeyVaultKey, id="https://localhost/fake/key/version", properties=mock.Mock(not_before=datetime(2000, 1, 1, tzinfo=_UTC), expires_on=datetime(3000, 1, 1, tzinfo=_UTC)), ) client = CryptographyClient(key, mock.Mock()) client._client = mock_client with pytest.raises(ValueError) as ex: await client.encrypt(EncryptionAlgorithm.rsa_oaep, b"...", iv=b"...") assert "iv" in str(ex.value) with pytest.raises(ValueError) as ex: await client.encrypt(EncryptionAlgorithm.rsa_oaep, b"...", additional_authenticated_data=b"...") assert "additional_authenticated_data" in str(ex.value)
async def test_initialization_get_key_successful(): """If the client is able to get key material, it shouldn't do so again""" mock_client = mock.Mock() mock_client.get_key.return_value = mock.Mock(spec=KeyVaultKey) client = CryptographyClient("https://localhost/fake/key/version", mock.Mock()) client._client = mock_client mock_key = mock.Mock() mock_client.get_key.return_value = get_completed_future(mock_key) assert mock_client.get_key.call_count == 0 with mock.patch(CryptographyClient.__module__ + ".get_local_cryptography_provider") as get_provider: await client.verify(SignatureAlgorithm.rs256, b"...", b"...") get_provider.assert_called_once_with(mock_key) for _ in range(3): assert mock_client.get_key.call_count == 1 assert get_provider.call_count == 1 await client.verify(SignatureAlgorithm.rs256, b"...", b"...")
async def test_symmetric_wrap_and_unwrap_local(): key = KeyVaultKey( key_id="http://localhost/keys/key/version", k=os.urandom(32), kty="oct", key_ops=["unwrapKey", "wrapKey"], ) crypto_client = CryptographyClient(key, credential=lambda *_: None) # Wrap a key with the created key, then unwrap it. The wrapped key's bytes should round-trip. key_bytes = os.urandom(32) wrap_result = await crypto_client.wrap_key(KeyWrapAlgorithm.aes_256, key_bytes) unwrap_result = await crypto_client.unwrap_key(wrap_result.algorithm, wrap_result.encrypted_key) assert unwrap_result.key == key_bytes
async def test_encrypt_local(self, key_client, credential, **kwargs): """Encrypt locally, decrypt with Key Vault""" key = await key_client.create_rsa_key("encrypt-local", size=4096) crypto_client = CryptographyClient(key, credential) for encrypt_algorithm in EncryptionAlgorithm: result = await crypto_client.encrypt(encrypt_algorithm, self.plaintext) self.assertEqual(result.key_id, key.id) result = await crypto_client.decrypt(result.algorithm, result.ciphertext) self.assertEqual(result.plaintext, self.plaintext)
async def test_wrap_local(self, key_client, credential, **kwargs): """Wrap locally, unwrap with Key Vault""" key = await key_client.create_rsa_key("wrap-local", size=4096) crypto_client = CryptographyClient(key, credential) for wrap_algorithm in (algo for algo in KeyWrapAlgorithm if algo.value.startswith("RSA")): result = await crypto_client.wrap_key(wrap_algorithm, self.plaintext) self.assertEqual(result.key_id, key.id) result = await crypto_client.unwrap_key(result.algorithm, result.encrypted_key) self.assertEqual(result.key, self.plaintext)
async def test_operations(key, expected_error_substrings, encrypt_algorithms, wrap_algorithms): crypto_client = CryptographyClient(key, credential) for algorithm in encrypt_algorithms: with pytest.raises(ValueError) as ex: await crypto_client.encrypt(algorithm, self.plaintext) for substring in expected_error_substrings: assert substring in str(ex.value) for algorithm in wrap_algorithms: with pytest.raises(ValueError) as ex: await crypto_client.wrap_key(algorithm, self.plaintext) for substring in expected_error_substrings: assert substring in str(ex.value)
async def test_encrypt_decrypt_async(self, key_client, **kwargs): credential = self.get_credential(CryptographyClient, is_async=True) key_name = self.get_resource_name("crypto-test-encrypt-key") await key_client.create_rsa_key(key_name) # [START create_client] # create a CryptographyClient using a KeyVaultKey instance key = await key_client.get_key(key_name) crypto_client = CryptographyClient(key, credential) # or a key's id, which must include a version key_id = "https://<your vault>.vault.azure.net/keys/<key name>/fe4fdcab688c479a9aa80f01ffeac26" crypto_client = CryptographyClient(key_id, credential) # the client and credential should be closed when no longer needed # (both are also async context managers) await crypto_client.close() await credential.close() # [END create_client] client = CryptographyClient(key, credential, api_version=key_client.api_version) # [START encrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm # the result holds the ciphertext and identifies the encryption key and algorithm used result = await client.encrypt(EncryptionAlgorithm.rsa_oaep, b"plaintext") print(result.key_id) print(result.algorithm) ciphertext = result.ciphertext # [END encrypt] # [START decrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm result = await client.decrypt(EncryptionAlgorithm.rsa_oaep, ciphertext) print(result.plaintext)
async def test_prefers_local_provider(): """The client should complete operations locally whenever possible""" mock_client = mock.Mock() key = mock.Mock( spec=KeyVaultKey, id="https://localhost/fake/key/version", properties=mock.Mock(not_before=datetime(2000, 1, 1, tzinfo=_UTC), expires_on=datetime(3000, 1, 1, tzinfo=_UTC)), ) client = CryptographyClient(key, mock.Mock()) client._client = mock_client supports_everything = mock.Mock(supports=mock.Mock(return_value=True)) with mock.patch( CryptographyClient.__module__ + ".get_local_cryptography_provider", lambda *_: supports_everything): await client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.decrypt.call_count == 0 assert supports_everything.decrypt.call_count == 1 await client.encrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.encrypt.call_count == 0 assert supports_everything.encrypt.call_count == 1 await client.sign(SignatureAlgorithm.rs256, b"...") assert mock_client.sign.call_count == 0 assert supports_everything.sign.call_count == 1 await client.verify(SignatureAlgorithm.rs256, b"...", b"...") assert mock_client.verify.call_count == 0 assert supports_everything.verify.call_count == 1 await client.unwrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.unwrap_key.call_count == 0 assert supports_everything.unwrap_key.call_count == 1 await client.wrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.wrap_key.call_count == 0 assert supports_everything.wrap_key.call_count == 1
async def test_encrypt_and_decrypt(self, key_client, credential, **kwargs): key_name = self.get_resource_name("keycrypt") imported_key = await self._import_test_key(key_client, key_name) crypto_client = CryptographyClient(imported_key.id, credential) result = await crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep, self.plaintext) self.assertEqual(result.key_id, imported_key.id) result = await crypto_client.decrypt(result.algorithm, result.ciphertext) self.assertEqual(result.key_id, imported_key.id) self.assertEqual(EncryptionAlgorithm.rsa_oaep, result.algorithm) self.assertEqual(self.plaintext, result.plaintext)
async def test_wrap_local_from_jwk(self, key_client, is_hsm, **kwargs): """Wrap locally, unwrap with Key Vault""" key_name = self.get_resource_name("wrap-local") key = await self._create_rsa_key(key_client, key_name, size=4096, hardware_protected=is_hsm) crypto_client = self.create_crypto_client(key, is_async=True, api_version=key_client.api_version) local_client = CryptographyClient.from_jwk(key.key) for wrap_algorithm in (algorithm for algorithm in KeyWrapAlgorithm if algorithm.startswith("RSA")): result = await local_client.wrap_key(wrap_algorithm, self.plaintext) self.assertEqual(result.key_id, key.id) result = await crypto_client.unwrap_key(result.algorithm, result.encrypted_key) self.assertEqual(result.key, self.plaintext)
async def test_calls_service_for_operations_unsupported_locally(): """When an operation can't be performed locally, the client should request Key Vault perform it""" class _AsyncMock(mock.Mock): async def __call__(self, *args, **kwargs): return super().__call__(*args, **kwargs) mock_client = _AsyncMock() key = mock.Mock(spec=KeyVaultKey, id="https://localhost/fake/key/version") client = CryptographyClient(key, mock.Mock()) client._client = mock_client supports_nothing = mock.Mock(supports=mock.Mock(return_value=False)) with mock.patch( CryptographyClient.__module__ + ".get_local_cryptography_provider", lambda *_: supports_nothing): await client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.decrypt.call_count == 1 assert supports_nothing.decrypt.call_count == 0 await client.encrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.encrypt.call_count == 1 assert supports_nothing.encrypt.call_count == 0 await client.sign(SignatureAlgorithm.rs256, b"...") assert mock_client.sign.call_count == 1 assert supports_nothing.sign.call_count == 0 await client.verify(SignatureAlgorithm.rs256, b"...", b"...") assert mock_client.verify.call_count == 1 assert supports_nothing.verify.call_count == 0 await client.unwrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.unwrap_key.call_count == 1 assert supports_nothing.unwrap_key.call_count == 0 await client.wrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.wrap_key.call_count == 1 assert supports_nothing.wrap_key.call_count == 0