diff --git a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md index 6900c1d1ada0..5812490d2289 100644 --- a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md @@ -1,7 +1,8 @@ # Release History ## 4.2.0b2 (Unreleased) - +- `CryptographyClient` will no longer perform encrypt or wrap operations when + its key has expired or is not yet valid. ## 4.2.0b1 (2020-03-10) - Support for Key Vault API version 7.1-preview diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py index f41cb3cc8dc5..17977e4f8419 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_client.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +from datetime import datetime, timedelta, tzinfo + import six from azure.core.exceptions import AzureError, HttpResponseError from azure.core.tracing.decorator import distributed_trace @@ -24,6 +26,43 @@ from ._internal import Key as _Key +class _UTC_TZ(tzinfo): + """from https://docs.python.org/2/library/datetime.html#tzinfo-objects""" + + ZERO = timedelta(0) + + def utcoffset(self, dt): + return self.ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return self.ZERO + + +_UTC = _UTC_TZ() + + +def _enforce_nbf_exp(key): + # type: (KeyVaultKey) -> None + try: + nbf = key.properties.not_before + exp = key.properties.expires_on + except AttributeError: + # we consider the key valid because a user must have deliberately created it + # (if it came from Key Vault, it would have those attributes) + return + + now = datetime.now(_UTC) + if (nbf and exp) and not nbf <= now <= exp: + raise ValueError("This client's key is useable only between {} and {} (UTC)".format(nbf, exp)) + if nbf and nbf >= now: + raise ValueError("This client's key is not useable until {} (UTC)".format(nbf)) + if exp and exp <= now: + raise ValueError("This client's key expired at {} (UTC)".format(exp)) + + class CryptographyClient(KeyVaultClientBase): """Performs cryptographic operations using Azure Key Vault keys. @@ -80,9 +119,7 @@ def __init__(self, key, credential, **kwargs): self._internal_key = None # type: Optional[_Key] - super(CryptographyClient, self).__init__( - vault_url=self._key_id.vault_url, credential=credential, **kwargs - ) + super(CryptographyClient, self).__init__(vault_url=self._key_id.vault_url, credential=credential, **kwargs) @property def key_id(self): @@ -116,7 +153,7 @@ def _get_key(self, **kwargs): return self._key def _get_local_key(self, **kwargs): - # type: () -> Optional[_Key] + # type: (**Any) -> Optional[_Key] """Gets an object implementing local operations. Will be ``None``, if the client was instantiated with a key id and lacks keys/get permission.""" @@ -140,7 +177,6 @@ def _get_local_key(self, **kwargs): @distributed_trace def encrypt(self, algorithm, plaintext, **kwargs): # type: (EncryptionAlgorithm, bytes, **Any) -> EncryptResult - # pylint:disable=line-too-long """Encrypt bytes using the client's key. Requires the keys/encrypt permission. This method encrypts only a single block of data, whose size depends on the key and encryption algorithm. @@ -166,6 +202,7 @@ def encrypt(self, algorithm, plaintext, **kwargs): local_key = self._get_local_key(**kwargs) if local_key: + _enforce_nbf_exp(self._key) if "encrypt" not in self._allowed_ops: raise AzureError("This client doesn't have 'keys/encrypt' permission") result = local_key.encrypt(plaintext, algorithm=algorithm.value) @@ -238,6 +275,7 @@ def wrap_key(self, algorithm, key, **kwargs): local_key = self._get_local_key(**kwargs) if local_key: + _enforce_nbf_exp(self._key) if "wrapKey" not in self._allowed_ops: raise AzureError("This client doesn't have 'keys/wrapKey' permission") result = local_key.wrap_key(key, algorithm=algorithm.value) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/rsa_key.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/rsa_key.py index 41c94fb9f65c..117f0b900687 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/rsa_key.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/rsa_key.py @@ -2,7 +2,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -import codecs import uuid from cryptography.exceptions import InvalidSignature @@ -170,7 +169,7 @@ def default_signature_algorithm(self): def encrypt(self, plain_text, **kwargs): algorithm = self._get_algorithm("encrypt", **kwargs) - encryptor = algorithm.create_encryptor(self._rsa_impl) + encryptor = algorithm.create_encryptor(self.public_key) return encryptor.transform(plain_text) def decrypt(self, cipher_text, **kwargs): @@ -178,7 +177,7 @@ def decrypt(self, cipher_text, **kwargs): raise NotImplementedError("The current RsaKey does not support decrypt") algorithm = self._get_algorithm("decrypt", **kwargs) - decryptor = algorithm.create_decryptor(self._rsa_impl) + decryptor = algorithm.create_decryptor(self.private_key) return decryptor.transform(cipher_text) def sign(self, digest, **kwargs): @@ -186,12 +185,12 @@ def sign(self, digest, **kwargs): raise NotImplementedError("The current RsaKey does not support sign") algorithm = self._get_algorithm("sign", **kwargs) - signer = algorithm.create_signature_transform(self._rsa_impl) + signer = algorithm.create_signature_transform(self.private_key) return signer.sign(digest) def verify(self, digest, signature, **kwargs): algorithm = self._get_algorithm("verify", **kwargs) - signer = algorithm.create_signature_transform(self._rsa_impl) + signer = algorithm.create_signature_transform(self.public_key) try: # cryptography's verify methods return None, and raise when verification fails signer.verify(digest, signature) @@ -201,7 +200,7 @@ def verify(self, digest, signature, **kwargs): def wrap_key(self, key, **kwargs): algorithm = self._get_algorithm("wrapKey", **kwargs) - encryptor = algorithm.create_encryptor(self._rsa_impl) + encryptor = algorithm.create_encryptor(self.public_key) return encryptor.transform(key) def unwrap_key(self, encrypted_key, **kwargs): @@ -209,7 +208,7 @@ def unwrap_key(self, encrypted_key, **kwargs): raise NotImplementedError("The current RsaKey does not support unwrap") algorithm = self._get_algorithm("unwrapKey", **kwargs) - decryptor = algorithm.create_decryptor(self._rsa_impl) + decryptor = algorithm.create_decryptor(self.private_key) return decryptor.transform(encrypted_key) def is_private_key(self): @@ -223,20 +222,3 @@ def _public_key_material(self): def _private_key_material(self): return self.private_key.private_numbers() if self.private_key else None - - -def _bytes_to_int(b): - return int(codecs.encode(b, "hex"), 16) - - -def _int_to_bytes(i): - h = hex(i) - if len(h) > 1 and h[0:2] == "0x": - h = h[2:] - - # need to strip L in python 2.x - h = h.strip("L") - - if len(h) % 2: - h = "0" + h - return codecs.decode(h, "hex") diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py index e0f8d3467dbf..9fae0a14fb76 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/aio/_client.py @@ -4,11 +4,12 @@ # ------------------------------------ from azure.core.exceptions import AzureError, HttpResponseError from azure.core.tracing.decorator_async import distributed_trace_async -from azure.keyvault.keys._shared import AsyncKeyVaultClientBase, parse_vault_id from .. import DecryptResult, EncryptResult, SignResult, VerifyResult, UnwrapResult, WrapResult from .._internal import EllipticCurveKey, RsaKey, SymmetricKey +from ...crypto._client import _enforce_nbf_exp from ..._models import KeyVaultKey +from ..._shared import AsyncKeyVaultClientBase, parse_vault_id try: from typing import TYPE_CHECKING @@ -18,7 +19,7 @@ if TYPE_CHECKING: # pylint:disable=unused-import from typing import Any, Optional, Union - from azure.core.credentials import TokenCredential + from azure.core.credentials_async import AsyncTokenCredential from .. import EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm from .._internal import Key as _Key @@ -57,7 +58,7 @@ class CryptographyClient(AsyncKeyVaultClientBase): """ - def __init__(self, key: "Union[KeyVaultKey, str]", credential: "TokenCredential", **kwargs: "Any") -> None: + def __init__(self, key: "Union[KeyVaultKey, str]", credential: "AsyncTokenCredential", **kwargs: "Any") -> None: if isinstance(key, KeyVaultKey): self._key = key self._key_id = parse_vault_id(key.id) @@ -77,9 +78,7 @@ def __init__(self, key: "Union[KeyVaultKey, str]", credential: "TokenCredential" self._internal_key = None # type: Optional[_Key] - super(CryptographyClient, self).__init__( - vault_url=self._key_id.vault_url, credential=credential, **kwargs - ) + super().__init__(vault_url=self._key_id.vault_url, credential=credential, **kwargs) @property def key_id(self) -> str: @@ -158,6 +157,7 @@ async def encrypt(self, algorithm: "EncryptionAlgorithm", plaintext: bytes, **kw local_key = await self._get_local_key(**kwargs) if local_key: + _enforce_nbf_exp(self._key) if "encrypt" not in self._allowed_ops: raise AzureError("This client doesn't have 'keys/encrypt' permission") result = local_key.encrypt(plaintext, algorithm=algorithm.value) @@ -224,6 +224,7 @@ async def wrap_key(self, algorithm: "KeyWrapAlgorithm", key: bytes, **kwargs: "A local_key = await self._get_local_key(**kwargs) if local_key: + _enforce_nbf_exp(self._key) if "wrapKey" not in self._allowed_ops: raise AzureError("This client doesn't have 'keys/wrapKey' permission") result = local_key.wrap_key(key, algorithm=algorithm.value) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client.test_local_validity_period_enforcement.yaml b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client.test_local_validity_period_enforcement.yaml new file mode 100644 index 000000000000..df83d3511295 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client.test_local_validity_period_enforcement.yaml @@ -0,0 +1,370 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"error":{"code":"Unauthorized","message":"Request is missing a Bearer + or PoP token."}}' + headers: + cache-control: + - no-cache + content-length: + - '87' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:32 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + www-authenticate: + - Bearer authorization="https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47", + resource="https://vault.azure.net" + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 401 + message: Unauthorized +- request: + body: '{"attributes": {"nbf": 32503680000}, "kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '50' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/1460aacb704740988841fafb23bdb01d","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"uJ7KgbHcIg_ltIhsHzfUrTrgLZtSJpDfr3QfmBsa6hEm7kBKGO1NWWRgyeHVWUGNgJHhk6k2CcIuZv7Wux2HaOrMBya7F0XEBNJXduvLdDZkxjTUvCJ7QnvbZyJsiqQpfgsgRNZk61wOSqnEjIIY9VRShrUUtcgNd6SZQ5vK6EvODHSdMQSs91qcYyTIX1pk-V1nzxzQCbEmic6kT7G15KR9XQ7am1YHR5qPHlmu-euOwglJx_UVgtIth1A5RvKaWmvH8KOzUzvBNhvBC-q-5MjLOGGU3P4yCi0n9sIhM3laJkpDZjBoVqzC7VhUBQR60DuTloSdEhcI5eNwQxh-bw","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"created":1584733893,"updated":1584733893,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '715' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"nbf": 32503680000}, "kty": "EC"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '49' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-not-yet-valid/995e8c4ae67943678ba3f8fec888faba","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"OisClUGMm6FO7mGeKuSkpMT2FczG7PEHidbkDN-NJfs","y":"AVZNy28U2_o2ihl-jbbl7izyS0EiBvmV7hAtsC5driM"},"attributes":{"enabled":true,"nbf":32503680000,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '425' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"exp": 946684800}, "kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '48' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-expired/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-expired/64dcda598a884f6d8398f4e23727ed57","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"sLGAAoDiY3gyQ0RJpdohqedKPLe4KGXaI8AyJTgZX5r8btRSmE_i2YTr1uxEoVekjwggiRZTo0owrhSR5MwGzWyJYOm5Xn6Aa5_ZW2Y2geVSckA6wXorRbbSUB17Ebg6qaooEjgl5D_ONk7UPJ-D7V_B3bgY-xq_cvfX6J5kmRBElh-63wfcrWz0C5wdr7SjtfWDAaJ4TyJKoHsgA5uvYyAgigqjiHV2tgnEESMg35hd_O4nSqLNNeRAGwbywXZ0Etm8JiwIRmBVjgSHECtgGO1PvC9hM45CgdS9jLPqLW6JWZLEztFxcwB1VABYXSVs6Nu_MnyNSq0c6vtbM0H9pQ","e":"AQAB"},"attributes":{"enabled":true,"exp":946684800,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '707' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"exp": 946684800}, "kty": "EC"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '47' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-expired/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-expired/85cacb15b40e423f83e0354636fd898c","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"41cGfisihfHqmzlW4VTUY0Aiu4zLj_yHjNPvjBkmVbY","y":"-7erU6kv6KUGZj83f_r_gZkLmjzn0tP_8diwNLtOX0w"},"attributes":{"enabled":true,"exp":946684800,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '417' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"nbf": 32503680000, "exp": 32535216000}, "kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '70' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-valid/b33d044648764ae2a5e918ece13ea090","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"2xuX3gz7of3s_RQTOSLUeisFxIQBF3bp7v1VN-SSGk_h4gc6lTTyI9a5rfCO9FRKUG2VP5r83o_zWUmFIYjSx9vlwhqW8FOOURA7X9ia1Hf-TMPeHG0KXAaDqSRe3R0zTVk6uLzMHJHqgpTvOA_cmVneCGjA4__IdiBiDjP7KTcU40blq7N0wjDrrPvX-hP5CmLko3WgDUUYY6pH_17pPyPOnqN6hGaLPf65o4Ithb4p8z3oHrzmtQY3rAxMGUyHBqf1ek5XQEcsQkbqa6DeMl7RsYacaeDEVWqcIeHk-XzXUlW8_e8BPK76dq-wye_DJu89FEKka5b7XRQdLJf21w","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '725' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"attributes": {"nbf": 32503680000, "exp": 32535216000}, "kty": "EC"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '69' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-valid/e5491c2d9a874e51aed7b11c4831c3da","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"k7NEW0UOGk1HY-iJFEXVauRLZbjcI_qmsBaou7waoZk","y":"UXNWcIQwwSuGiAtBxTkh6S9J6Ahq3IPVrApaKivQUGs"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1584733894,"updated":1584733894,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '435' + content-type: + - application/json; charset=utf-8 + date: + - Fri, 20 Mar 2020 19:51:33 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-IIS/10.0 + strict-transport-security: + - max-age=31536000;includeSubDomains + x-aspnet-version: + - 4.0.30319 + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.1.0.898 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client_async.test_local_validity_period_enforcement.yaml b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client_async.test_local_validity_period_enforcement.yaml new file mode 100644 index 000000000000..ee82be24f57d --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_crypto_client_async.test_local_validity_period_enforcement.yaml @@ -0,0 +1,250 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"error":{"code":"Unauthorized","message":"Request is missing a Bearer + or PoP token."}}' + headers: + cache-control: no-cache + content-length: '87' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:44 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + www-authenticate: Bearer authorization="https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47", + resource="https://vault.azure.net" + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 401 + message: Unauthorized + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview +- request: + body: '{"attributes": {"nbf": 32503680000}, "kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '50' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/7e86762e2e5a41c69ddf46da6443721d","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"n_f1Zxk8H2FYnM6tf9kH3whK721rSOktfV4bgfzS-N6drnAZpuIxiTzcrXYqCbgbsk4Q3mZO46j1mKYEklwcFXsaqV5WMxx-rEvs8yRW4ydBhp-uXhq4sFHZlLd4RbdcCKBuJVVX0v8Z2uv7gsws_LAt7VbBlNGwB3On00pT_L8gPuBG9uzyvXPrOhvs2oxgvou5HGEzMpTQyfSMrQ5gi8cUMBbRksseftPPbCUtJAPX7dxWdj3PtDn1YVgogSyxnyR9jPNqkMS3UvP1ogryz-2KnEgvU-0LYXllNyDPK-ZqnrUitT6ghGbGpIVAEKfEMXR3kGrU0Ie7DfvKIUlLZQ","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"created":1584734384,"updated":1584734384,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '715' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:44 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview +- request: + body: '{"attributes": {"nbf": 32503680000}, "kty": "EC"}' + headers: + Accept: + - application/json + Content-Length: + - '49' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-not-yet-valid/5b250d819b2942d8b97ae31761be4e25","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"MGBrShFjdRDxzUm2q6Nb5VYEWyZ9HtqfJK9hSbRZsyk","y":"epWjy1j8_hhcJ_mGj41vEZOM6CYZN9ppX2lytWrnkLs"},"attributes":{"enabled":true,"nbf":32503680000,"created":1584734384,"updated":1584734384,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '425' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:44 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1-preview +- request: + body: '{"attributes": {"exp": 946684800}, "kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '48' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-expired/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-expired/dd6358e9c6814c5883b3f25e516cfa0b","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"zpnwFfZQXMyhN_83cPqjDfOD543vk2F76cflEAJdmvYvlx-ytb35nB5ZWjEAAmCRJG2wfcX_JLwmsD-TNhJJiBsZruf951HrqKlsY5Cr25-4IJs11jPuajucYXi6P4BWhGt-Mv-qYSLB7k1BiNNJSwwwOVCobFDCO5ffTAz7rnvu4ixPh5GeDyVP2vdmt8US8aBPE8inlatr30FFY_Tu4ZYemOIW4frmq_JMDGV4AF6Gln0eBJLCHlcQYLoWDpzTs4RdgH5rvu9K7wQK2JdM5xVuHX0pUBWriz9MGB8rDZWbbp_uerF_nnYedLB4IDwhzwhweVyrYE6SpzCrQvkEPw","e":"AQAB"},"attributes":{"enabled":true,"exp":946684800,"created":1584734385,"updated":1584734385,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '707' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:44 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/rsa-expired/create?api-version=7.1-preview +- request: + body: '{"attributes": {"exp": 946684800}, "kty": "EC"}' + headers: + Accept: + - application/json + Content-Length: + - '47' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-expired/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-expired/c4d84e4537b84df78f10478cf584f101","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"TZFmYa1LSeR4nd9b82pozBG4xh24I-ATbu4MkzJ0eRI","y":"nYosAlTxNpBVtFviuBzHF4swNRT7JahJoH6_-fIKI6k"},"attributes":{"enabled":true,"exp":946684800,"created":1584734385,"updated":1584734385,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '417' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:45 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/ec-expired/create?api-version=7.1-preview +- request: + body: '{"attributes": {"exp": 32535216000, "nbf": 32503680000}, "kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '70' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/rsa-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-valid/8c594c653af945a88db7a8c4cc8c3e93","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"r9NyKZixLoYzwDtOcTG0KtIf8S-7zuSeUePJ2wvuCprhzVIICqUNu8F-kkLxNOl0ApEVFEtqRmcshpd_h17jySjoWzYCT53pGUEPRt0wTCp2r-O_rzk6p3r9lnb9BxN-FrCRU3At1Ql6HVzo4tP0xHvCuRjolStTB6JqePEm3I6YI_0im8d1AQbMf0EdP-5ot3Fam5BlpVctnm2ES9cfy2RMs5c08VuPNB-SylzxPtMLMYaBbn4uurqKalbpZdtcEPFa4fmRg661l2ZjwVoP6RnWMFE0aIsu3szOK7WhYn5mWX6Wbib0lMpOlx5TKSD_u9qiFKWYAeldOgROakh88w","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1584734385,"updated":1584734385,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '725' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:45 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/rsa-valid/create?api-version=7.1-preview +- request: + body: '{"attributes": {"exp": 32535216000, "nbf": 32503680000}, "kty": "EC"}' + headers: + Accept: + - application/json + Content-Length: + - '69' + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.18362-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/ec-valid/create?api-version=7.1-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-valid/15ded77c448d4541a9db521aff47fe14","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"cyTRkx71JgizNK9kdZqNfS2GIlVhWVXqOQAssKqzy10","y":"tCR7fSTtr60gSzsCRjFCvUAFywN-bjGEEyzVTzrtq8c"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1584734385,"updated":1584734385,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '435' + content-type: application/json; charset=utf-8 + date: Fri, 20 Mar 2020 19:59:45 GMT + expires: '-1' + pragma: no-cache + server: Microsoft-IIS/10.0 + strict-transport-security: max-age=31536000;includeSubDomains + x-aspnet-version: 4.0.30319 + x-content-type-options: nosniff + x-ms-keyvault-network-info: addr=76.121.58.221;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.1.0.898 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://r2gy5gpzzda6sxycp53mdnwx.vault.azure.net/keys/ec-valid/create?api-version=7.1-preview +version: 1 diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py index 6965c8697e93..7ec2f53df1ed 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py @@ -6,12 +6,15 @@ import functools import hashlib import os +from datetime import datetime from azure.core.credentials import AccessToken from azure.keyvault.keys import JsonWebKey, KeyClient, KeyCurveName, KeyVaultKey from azure.keyvault.keys.crypto import CryptographyClient, EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm +from azure.keyvault.keys.crypto._client import _UTC from azure.mgmt.keyvault.models import KeyPermissions, Permissions from devtools_testutils import ResourceGroupPreparer, KeyVaultPreparer +import pytest from _shared.json_attribute_matcher import json_attribute_matcher from _shared.test_case import KeyVaultTestCase @@ -82,7 +85,6 @@ def _to_bytes(hex): @KeyVaultPreparer(permissions=NO_GET) @CryptoClientPreparer() def test_encrypt_and_decrypt(self, key_client, credential, **kwargs): - # TODO: use iv, authentication_data key_name = self.get_resource_name("keycrypt") imported_key = self._import_test_key(key_client, key_name) @@ -100,7 +102,6 @@ def test_encrypt_and_decrypt(self, key_client, credential, **kwargs): @KeyVaultPreparer(permissions=NO_GET) @CryptoClientPreparer() def test_sign_and_verify(self, key_client, credential, **kwargs): - key_name = self.get_resource_name("keysign") md = hashlib.sha256() @@ -229,3 +230,59 @@ def test_ec_verify_local(self, key_client, credential, **kwargs): result = crypto_client.verify(result.algorithm, digest, result.signature) self.assertTrue(result.is_valid) + + @ResourceGroupPreparer(random_name_enabled=True) + @KeyVaultPreparer(permissions=NO_GET) + @CryptoClientPreparer() + def test_local_validity_period_enforcement(self, key_client, credential, **kwargs): + """Local crypto operations should respect a key's nbf and exp properties""" + + 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: + 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: + crypto_client.wrap_key(algorithm, self.plaintext) + for substring in expected_error_substrings: + assert substring in str(ex.value) + + # operations should not succeed with a key whose nbf is in the future + the_year_3000 = datetime(3000, 1, 1, tzinfo=_UTC) + + rsa_wrap_algorithms = [algo for algo in KeyWrapAlgorithm if algo.startswith("RSA")] + rsa_not_yet_valid = key_client.create_rsa_key("rsa-not-yet-valid", not_before=the_year_3000) + test_operations(rsa_not_yet_valid, [str(the_year_3000)], EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_not_yet_valid = key_client.create_ec_key("ec-not-yet-valid", not_before=the_year_3000) + test_operations( + ec_not_yet_valid, [str(the_year_3000)], encrypt_algorithms=[], wrap_algorithms=[KeyWrapAlgorithm.aes_256] + ) + + # nor should they succeed with a key whose exp has passed + the_year_2000 = datetime(2000, 1, 1, tzinfo=_UTC) + + rsa_expired = key_client.create_rsa_key("rsa-expired", expires_on=the_year_2000) + test_operations(rsa_expired, [str(the_year_2000)], EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_expired = key_client.create_ec_key("ec-expired", expires_on=the_year_2000) + test_operations( + ec_expired, [str(the_year_2000)], encrypt_algorithms=[], wrap_algorithms=[KeyWrapAlgorithm.aes_256] + ) + + # when exp and nbf are set, error messages should contain both + the_year_3001 = datetime(3001, 1, 1, tzinfo=_UTC) + + rsa_valid = key_client.create_rsa_key("rsa-valid", not_before=the_year_3000, expires_on=the_year_3001) + test_operations(rsa_valid, (str(the_year_3000), str(the_year_3001)), EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_valid = key_client.create_ec_key("ec-valid", not_before=the_year_3000, expires_on=the_year_3001) + test_operations( + ec_valid, + (str(the_year_3000), str(the_year_3001)), + encrypt_algorithms=[], + wrap_algorithms=[KeyWrapAlgorithm.aes_256], + ) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py index 1f069d86e781..ffb4113b7b7e 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client_async.py @@ -3,11 +3,12 @@ # Licensed under the MIT License. # ------------------------------------ import codecs -import functools +from datetime import datetime import hashlib import os from azure.keyvault.keys import JsonWebKey, KeyCurveName, KeyVaultKey +from azure.keyvault.keys.crypto._client import _UTC from azure.keyvault.keys.crypto.aio import CryptographyClient, EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm from azure.mgmt.keyvault.models import KeyPermissions, Permissions from devtools_testutils import ResourceGroupPreparer, KeyVaultPreparer @@ -15,7 +16,6 @@ from _shared.json_attribute_matcher import json_attribute_matcher from _shared.test_case_async import KeyVaultTestCase - from crypto_client_preparer_async import CryptoClientPreparer # without keys/get, a CryptographyClient created with a key ID performs all ops remotely @@ -81,7 +81,6 @@ def _to_bytes(hex): @KeyVaultPreparer(permissions=NO_GET) @CryptoClientPreparer() async def test_encrypt_and_decrypt(self, key_client, credential, **kwargs): - # TODO: use iv, authentication_data key_name = self.get_resource_name("keycrypt") imported_key = await self._import_test_key(key_client, key_name) @@ -216,14 +215,69 @@ async def test_ec_verify_local(self, key_client, credential, **kwargs): result = await crypto_client.verify(result.algorithm, digest, result.signature) self.assertTrue(result.is_valid) + @ResourceGroupPreparer(random_name_enabled=True) + @KeyVaultPreparer(permissions=NO_GET) + @CryptoClientPreparer() + async def test_local_validity_period_enforcement(self, key_client, credential, **kwargs): + """Local crypto operations should respect a key's nbf and exp properties""" + + 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) + + # operations should not succeed with a key whose nbf is in the future + the_year_3000 = datetime(3000, 1, 1, tzinfo=_UTC) + + rsa_wrap_algorithms = [algo for algo in KeyWrapAlgorithm if algo.startswith("RSA")] + rsa_not_yet_valid = await key_client.create_rsa_key("rsa-not-yet-valid", not_before=the_year_3000) + await test_operations(rsa_not_yet_valid, [str(the_year_3000)], EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_not_yet_valid = await key_client.create_ec_key("ec-not-yet-valid", not_before=the_year_3000) + await test_operations( + ec_not_yet_valid, [str(the_year_3000)], encrypt_algorithms=[], wrap_algorithms=[KeyWrapAlgorithm.aes_256] + ) + + # nor should they succeed with a key whose exp has passed + the_year_2000 = datetime(2000, 1, 1, tzinfo=_UTC) + + rsa_expired = await key_client.create_rsa_key("rsa-expired", expires_on=the_year_2000) + await test_operations(rsa_expired, [str(the_year_2000)], EncryptionAlgorithm, rsa_wrap_algorithms) + + ec_expired = await key_client.create_ec_key("ec-expired", expires_on=the_year_2000) + await test_operations( + ec_expired, [str(the_year_2000)], encrypt_algorithms=[], wrap_algorithms=[KeyWrapAlgorithm.aes_256] + ) + + # when exp and nbf are set, error messages should contain both + the_year_3001 = datetime(3001, 1, 1, tzinfo=_UTC) + + rsa_valid = await key_client.create_rsa_key("rsa-valid", not_before=the_year_3000, expires_on=the_year_3001) + await test_operations( + rsa_valid, (str(the_year_3000), str(the_year_3001)), EncryptionAlgorithm, rsa_wrap_algorithms + ) + + ec_valid = await key_client.create_ec_key("ec-valid", not_before=the_year_3000, expires_on=the_year_3001) + await test_operations( + ec_valid, + (str(the_year_3000), str(the_year_3001)), + encrypt_algorithms=[], + wrap_algorithms=[KeyWrapAlgorithm.aes_256], + ) + @pytest.mark.asyncio async def test_symmetric_wrap_and_unwrap_local(): key = KeyVaultKey( - key_id="http://fake.test.vault/keys/key/version", - k=os.urandom(32), - kty="oct", - key_ops=["unwrapKey", "wrapKey"], + key_id="http://fake.test.vault/keys/key/version", k=os.urandom(32), kty="oct", key_ops=["unwrapKey", "wrapKey"], ) crypto_client = CryptographyClient(key, credential=lambda *_: None)