diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/_internal.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/_internal.py index 1b7670efc43a..ba08b7863dbb 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/_internal.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/_internal.py @@ -2,7 +2,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ - import codecs from base64 import b64encode, b64decode @@ -67,29 +66,41 @@ def _b64_to_str(b64str): return _b64_to_bstr(b64str).decode("utf8") -def _int_to_bigendian_8_bytes(i): +def _int_to_fixed_length_bigendian_bytes(i, length): + """Convert an integer to a bigendian byte string left-padded with zeroes to a fixed length.""" + b = _int_to_bytes(i) - if len(b) > 8: - raise ValueError("the specified integer is to large to be represented by 8 bytes") + if len(b) > length: + raise ValueError("{} is too large to be represented by {} bytes".format(i, length)) - if len(b) < 8: - b = (b"\0" * (8 - len(b))) + b + if len(b) < length: + b = (b"\0" * (length - len(b))) + b return b -def encode_key_vault_ecdsa_signature(signature): - """ - ASN.1 DER encode a Key Vault ECDSA signature. - - Key Vault returns ECDSA signatures as the concatenated bytes of two equal-size integers. ``cryptography`` expects - ECDSA signatures be ASN.1 DER encoded. +def ecdsa_to_asn1_der(signature): + """ASN.1 DER encode an ECDSA signature. - :param bytes signature: ECDSA signature returned by Key Vault - :return: signature encoded for use by ``cryptography`` + :param bytes signature: ECDSA signature encoded according to RFC 7518, i.e. the concatenated big-endian bytes of + two integers (as produced by Key Vault) + :return: signature, ASN.1 DER encoded (as expected by ``cryptography``) """ mid = len(signature) // 2 r = _bytes_to_int(signature[:mid]) s = _bytes_to_int(signature[mid:]) return utils.encode_dss_signature(r, s) + + +def asn1_der_to_ecdsa(signature, algorithm): + """Convert an ASN.1 DER encoded signature to ECDSA encoding. + + :param bytes signature: an ASN.1 DER encoded ECDSA signature (as produced by ``cryptography``) + :param _Ecdsa algorithm: signing algorithm which produced ``signature`` + :return: signature encoded according to RFC 7518 (as expected by Key Vault) + """ + r, s = utils.decode_dss_signature(signature) + r_bytes = _int_to_fixed_length_bigendian_bytes(r, algorithm.coordinate_length) + s_bytes = _int_to_fixed_length_bigendian_bytes(s, algorithm.coordinate_length) + return r_bytes + s_bytes diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/aes_cbc_hmac.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/aes_cbc_hmac.py index a6016bd30503..f27bdd83c08c 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/aes_cbc_hmac.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/aes_cbc_hmac.py @@ -10,7 +10,7 @@ from ..algorithm import AuthenticatedSymmetricEncryptionAlgorithm from ..transform import AuthenticatedCryptoTransform -from .._internal import _int_to_bigendian_8_bytes +from .._internal import _int_to_fixed_length_bigendian_bytes class _AesCbcHmacCryptoTransform(AuthenticatedCryptoTransform): @@ -24,7 +24,7 @@ def __init__(self, key, iv, auth_data, auth_tag): self._cipher = Cipher(algorithms.AES(self._aes_key), modes.CBC(iv), backend=default_backend()) self._tag = auth_tag or bytearray() self._hmac = hmac.HMAC(self._hmac_key, hash_algo, backend=default_backend()) - self._auth_data_length = _int_to_bigendian_8_bytes(len(auth_data) * 8) + self._auth_data_length = _int_to_fixed_length_bigendian_bytes(len(auth_data) * 8, 8) # prime the hash self._hmac.update(auth_data) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/aes_kw.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/aes_kw.py index 4f0b24e1598b..e2a3845fd9d1 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/aes_kw.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/aes_kw.py @@ -7,6 +7,7 @@ from ..algorithm import AsymmetricEncryptionAlgorithm from ..transform import CryptoTransform +from ..._enums import KeyWrapAlgorithm class _AesKeyWrapTransform(CryptoTransform): @@ -59,7 +60,7 @@ class AesKw192(_AesKeyWrap): class AesKw256(_AesKeyWrap): _key_size = 256 - _name = "A256KW" + _name = KeyWrapAlgorithm.aes_256 AesKw128.register() diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/ecdsa.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/ecdsa.py index cff7410a6b44..968fcdbf4375 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/ecdsa.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/ecdsa.py @@ -2,12 +2,23 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +import abc +import sys + from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import utils from ..algorithm import SignatureAlgorithm from ..transform import SignatureTransform +from ..._enums import SignatureAlgorithm as KeyVaultSignatureAlgorithm + +if sys.version_info < (3, 3): + abstractproperty = abc.abstractproperty +else: # abc.abstractproperty is deprecated as of 3.3 + import functools + + abstractproperty = functools.partial(property, abc.abstractmethod) class _EcdsaSignatureTransform(SignatureTransform): @@ -28,25 +39,33 @@ class _Ecdsa(SignatureAlgorithm): def create_signature_transform(self, key): return _EcdsaSignatureTransform(key, self.default_hash_algorithm) + @abstractproperty + def coordinate_length(self): + pass + class Ecdsa256(_Ecdsa): - _name = "ES256K" + _name = KeyVaultSignatureAlgorithm.es256_k _default_hash_algorithm = hashes.SHA256() + coordinate_length = 32 class Es256(_Ecdsa): - _name = "ES256" + _name = KeyVaultSignatureAlgorithm.es256 _default_hash_algorithm = hashes.SHA256() + coordinate_length = 32 class Es384(_Ecdsa): - _name = "ES384" + _name = KeyVaultSignatureAlgorithm.es384 _default_hash_algorithm = hashes.SHA384() + coordinate_length = 48 class Es512(_Ecdsa): - _name = "ES512" + _name = KeyVaultSignatureAlgorithm.es512 _default_hash_algorithm = hashes.SHA512() + coordinate_length = 66 Ecdsa256.register() diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/rsa_encryption.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/rsa_encryption.py index e3b72fa8db59..df83c67365d2 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/rsa_encryption.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/rsa_encryption.py @@ -7,6 +7,7 @@ from ..algorithm import AsymmetricEncryptionAlgorithm from ..transform import CryptoTransform +from ..._enums import EncryptionAlgorithm class _Rsa1_5Encryptor(CryptoTransform): @@ -20,7 +21,7 @@ def transform(self, data): class Rsa1_5(AsymmetricEncryptionAlgorithm): # pylint:disable=client-incorrect-naming-convention - _name = "RSA1_5" + _name = EncryptionAlgorithm.rsa1_5 def create_encryptor(self, key): return _Rsa1_5Encryptor(key) @@ -54,7 +55,7 @@ def transform(self, data): class RsaOaep(AsymmetricEncryptionAlgorithm): - _name = "RSA-OAEP" + _name = EncryptionAlgorithm.rsa_oaep def create_encryptor(self, key): return _RsaOaepEncryptor(key, hashes.SHA1) @@ -64,7 +65,7 @@ def create_decryptor(self, key): class RsaOaep256(AsymmetricEncryptionAlgorithm): - _name = "RSA-OAEP-256" + _name = EncryptionAlgorithm.rsa_oaep_256 def create_encryptor(self, key): return _RsaOaepEncryptor(key, hashes.SHA256) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/rsa_signing.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/rsa_signing.py index 885070631b85..984befca583a 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/rsa_signing.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/algorithms/rsa_signing.py @@ -7,6 +7,7 @@ from ..algorithm import SignatureAlgorithm from ..transform import SignatureTransform +from ..._enums import SignatureAlgorithm as KeyVaultSignatureAlgorithm class RsaSignatureTransform(SignatureTransform): @@ -17,7 +18,7 @@ def __init__(self, key, padding_function, hash_algorithm): self._hash_algorithm = hash_algorithm def sign(self, digest): - return self._key.sign(digest, self._padding_function(digest), self._hash_algorithm) + return self._key.sign(digest, self._padding_function(digest), utils.Prehashed(self._hash_algorithm)) def verify(self, digest, signature): self._key.verify(signature, digest, self._padding_function(digest), utils.Prehashed(self._hash_algorithm)) @@ -37,32 +38,32 @@ def _get_padding(self, digest): class Ps256(RsaSsaPss): - _name = "PS256" + _name = KeyVaultSignatureAlgorithm.ps256 _default_hash_algorithm = hashes.SHA256() class Ps384(RsaSsaPss): - _name = "PS384" + _name = KeyVaultSignatureAlgorithm.ps384 _default_hash_algorithm = hashes.SHA384() class Ps512(RsaSsaPss): - _name = "PS512" + _name = KeyVaultSignatureAlgorithm.ps512 _default_hash_algorithm = hashes.SHA512() class Rs256(RsaSsaPkcs1v15): - _name = "RS256" + _name = KeyVaultSignatureAlgorithm.rs256 _default_hash_algorithm = hashes.SHA256() class Rs384(RsaSsaPkcs1v15): - _name = "RS384" + _name = KeyVaultSignatureAlgorithm.rs384 _default_hash_algorithm = hashes.SHA384() class Rs512(RsaSsaPkcs1v15): - _name = "RS512" + _name = KeyVaultSignatureAlgorithm.rs512 _default_hash_algorithm = hashes.SHA512() diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/ec_key.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/ec_key.py index 324ee606f31b..4c88ee5dff20 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/ec_key.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/ec_key.py @@ -7,6 +7,8 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric.ec import ( + EllipticCurvePrivateKey, + EllipticCurvePrivateNumbers, EllipticCurvePublicNumbers, SECP256R1, SECP384R1, @@ -14,37 +16,49 @@ SECP256K1, ) -from ._internal import _bytes_to_int, encode_key_vault_ecdsa_signature +from ._internal import _bytes_to_int, asn1_der_to_ecdsa, ecdsa_to_asn1_der from .key import Key from .algorithms.ecdsa import Es256, Es512, Es384, Ecdsa256 +from ... import KeyCurveName -_crypto_crv_to_kv_crv = {"secp256r1": "P-256", "secp384r1": "P-384", "secp521r1": "P-521", "secp256k1": "P-256K"} +_crypto_crv_to_kv_crv = { + "secp256r1": KeyCurveName.p_256, + "secp384r1": KeyCurveName.p_384, + "secp521r1": KeyCurveName.p_521, + "secp256k1": KeyCurveName.p_256_k, +} _kv_crv_to_crypto_cls = { - "P-256": SECP256R1, - "P-256K": SECP256K1, - "P-384": SECP384R1, - "P-521": SECP521R1, - "SECP256K1": SECP256K1, + KeyCurveName.p_256: SECP256R1, + KeyCurveName.p_256_k: SECP256K1, + KeyCurveName.p_384: SECP384R1, + KeyCurveName.p_521: SECP521R1, + "SECP256K1": SECP256K1, # "SECP256K1" is from Key Vault 2016-10-01 } _curve_to_default_algo = { - "P-256": Es256.name(), - "P-256K": Ecdsa256.name(), - "P-384": Es384.name(), - "P-521": Es512.name(), - "SECP256K1": Ecdsa256.name(), + KeyCurveName.p_256: Es256.name(), + KeyCurveName.p_256_k: Ecdsa256.name(), + KeyCurveName.p_384: Es384.name(), + KeyCurveName.p_521: Es512.name(), + "SECP256K1": Ecdsa256.name(), # "SECP256K1" is from Key Vault 2016-10-01 } class EllipticCurveKey(Key): - _supported_signature_algorithms = _curve_to_default_algo.values() + _supported_signature_algorithms = frozenset(_curve_to_default_algo.values()) - def __init__(self, x, y, kid=None, curve=None): + def __init__(self, x, y, d=None, kid=None, curve=None): super(EllipticCurveKey, self).__init__() self._kid = kid or str(uuid.uuid4()) self._default_algo = _curve_to_default_algo[curve] curve_cls = _kv_crv_to_crypto_cls[curve] - self._ec_impl = EllipticCurvePublicNumbers(x, y, curve_cls()).public_key(default_backend()) + + public_numbers = EllipticCurvePublicNumbers(x, y, curve_cls()) + self._public_key = public_numbers.public_key(default_backend()) + self._private_key = None + if d is not None: + private_numbers = EllipticCurvePrivateNumbers(d, public_numbers) + self._private_key = private_numbers.private_key(default_backend()) @classmethod def from_jwk(cls, jwk): @@ -54,33 +68,39 @@ def from_jwk(cls, jwk): if not jwk.x or not jwk.y: raise ValueError("jwk must have values for 'x' and 'y'") - return cls(_bytes_to_int(jwk.x), _bytes_to_int(jwk.y), kid=jwk.kid, curve=jwk.crv) + x = _bytes_to_int(jwk.x) + y = _bytes_to_int(jwk.y) + d = _bytes_to_int(jwk.d) if jwk.d is not None else None + return cls(x, y, d, kid=jwk.kid, curve=jwk.crv) def is_private_key(self): - return False + return isinstance(self._private_key, EllipticCurvePrivateKey) def decrypt(self, cipher_text, **kwargs): - raise NotImplementedError() + raise NotImplementedError("Local decryption isn't supported with elliptic curve keys") def encrypt(self, plain_text, **kwargs): - raise NotImplementedError() + raise NotImplementedError("Local encryption isn't supported with elliptic curve keys") def wrap_key(self, key, **kwargs): - raise NotImplementedError() + raise NotImplementedError("Local key wrapping isn't supported with elliptic curve keys") def unwrap_key(self, encrypted_key, **kwargs): - raise NotImplementedError() + raise NotImplementedError("Local key unwrapping isn't supported with elliptic curve keys") def sign(self, digest, **kwargs): - raise NotImplementedError() + algorithm = self._get_algorithm("sign", **kwargs) + signer = algorithm.create_signature_transform(self._private_key) + signature = signer.sign(digest) + ecdsa_signature = asn1_der_to_ecdsa(signature, algorithm) + return ecdsa_signature def verify(self, digest, signature, **kwargs): algorithm = self._get_algorithm("verify", **kwargs) - signer = algorithm.create_signature_transform(self._ec_impl) - dss_signature = encode_key_vault_ecdsa_signature(signature) + signer = algorithm.create_signature_transform(self._public_key) + asn1_signature = ecdsa_to_asn1_der(signature) try: - # cryptography's verify methods return None, and raise when verification fails - signer.verify(digest, dss_signature) + signer.verify(digest, asn1_signature) return True except InvalidSignature: return False diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/jose.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/jose.py deleted file mode 100644 index b6b7fb2d3190..000000000000 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/jose.py +++ /dev/null @@ -1,129 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ - -import json -import os -from ._internal import _str_to_b64url, _b64_to_str -from .symmetric_key import SymmetricKey -from .key import Key - - -def protect(plaintext, kek=None, alg=None, cek=None, enc=None): # pylint:disable=unused-argument - # if neither the kek or the cek is specified raise an error - if not kek and not cek: - raise ValueError( - "Either kek must be specified for key encryption sharing, " - "or cek must be specified for direct encryption key sharing" - ) - - # key encryption key can be any key (symmetric or asymmetric) - if kek and not isinstance(kek, Key): - raise TypeError("The specified kek must be a valid Key") - - # only symmetric keys are valid for the content encryption key - if cek and not isinstance(cek, SymmetricKey): - raise TypeError("The specified cek must be a valid SymmetricKey") - - # create the cek if not specified - cek = cek or SymmetricKey() - - # kid is the kek id if were using key encryption sharing else it's the cek id for direct sharing - kid = kek.kid if kek else cek.kid - - # alg is 'dir' for direct sharing otherwise use the specified alg - # or the default key wrap algorithm if not specified - alg = "dir" if not kek else alg or kek.default_key_wrap_algorithm - - # use the specified enc or the default encryption algorithm of the cek if not specified - enc = enc or cek.default_encryption_algorithm - - jwe = JweObject() - jwe.unprotected = JweHeader(alg=alg, kid=kid, enc=enc) - jwe.iv = os.urandom(16) - jwe.ciphertext = cek.encrypt() - - # add the encrypted key if we're using key encryption sharing - if kek: - jwe.encrypted_key = kek.wrap_key(key=cek) - - -class JoseObject(object): - def deserialize(self, s): - d = json.loads(s) - self.__dict__ = d # pylint:disable=attribute-defined-outside-init - return self - - def deserialize_b64(self, s): - self.deserialize(_b64_to_str(s)) - return self - - def serialize(self): - return json.dumps(self.__dict__) - - def serialize_b64url(self): - return _str_to_b64url(self.serialize()) - - -class JoseHeader(JoseObject): - def to_compact_header(self): - return _str_to_b64url(json.dumps(self.__dict__)) - - -class JweHeader(JoseHeader): - def __init__(self, alg=None, kid=None, enc=None): - self.alg = alg - self.kid = kid - self.enc = enc - - @staticmethod - def from_compact_header(compact): - header = JweHeader() - header.__dict__ = json.loads(_b64_to_str(compact)) # pylint:disable=attribute-defined-outside-init - return header - - -class JweObject(JoseObject): - def __init__(self): - self.unprotected = None - self.protected = None - self.encrypted_key = None - self.iv = None - self.ciphertext = None - self.tag = None - - def to_flattened_jwe(self): - if not (self.protected, self.encrypted_key, self.iv, self.ciphertext, self.tag): - raise ValueError("JWE is not complete.") - - return json.dumps(self.__dict__) - - -class JwsHeader(JoseHeader): - def __init__(self): - self.alg = None - self.kid = None - self.at = None - self.ts = None - self.p = None - self.typ = None - - @staticmethod - def from_compact_header(compact): - header = JwsHeader() - header.__dict__ = json.loads(_b64_to_str(compact)) # pylint:disable=attribute-defined-outside-init - return header - - -class JwsObject(JoseObject): - def __init__(self): - self.protected = None - self.payload = None - self.signature = None - - def to_flattened_jws(self): - if not (self.protected, self.payload, self.signature): - raise ValueError("JWS is not complete.") - - return json.dumps(self.__dict__) 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 117f0b900687..06852817717d 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 @@ -7,6 +7,7 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKey, RSAPrivateNumbers, RSAPublicNumbers, generate_private_key, @@ -15,27 +16,26 @@ rsa_crt_iqmp, ) -from azure.keyvault.keys._models import JsonWebKey from ._internal import _bytes_to_int, _int_to_bytes from .key import Key from .algorithms import Ps256, Ps384, Ps512, Rsa1_5, RsaOaep, RsaOaep256, Rs256, Rs384, Rs512 +from ... import JsonWebKey, KeyOperation class RsaKey(Key): # pylint:disable=too-many-public-methods - PUBLIC_KEY_DEFAULT_OPS = ["encrypt", "wrapKey", "verify"] - PRIVATE_KEY_DEFAULT_OPS = ["encrypt", "decrypt", "wrapKey", "unwrapKey", "verify", "sign"] - - _supported_encryption_algorithms = [Rsa1_5.name(), RsaOaep.name(), RsaOaep256.name()] - _supported_key_wrap_algorithms = [Rsa1_5.name(), RsaOaep.name(), RsaOaep256.name()] - _supported_signature_algorithms = [ - Ps256.name(), - Ps384.name(), - Ps512.name(), - Rs256.name(), - Rs384.name(), - Rs512.name(), + PUBLIC_KEY_DEFAULT_OPS = [KeyOperation.encrypt, KeyOperation.wrap_key, KeyOperation.verify] + PRIVATE_KEY_DEFAULT_OPS = PUBLIC_KEY_DEFAULT_OPS + [ + KeyOperation.decrypt, + KeyOperation.unwrap_key, + KeyOperation.sign, ] + _supported_encryption_algorithms = frozenset((Rsa1_5.name(), RsaOaep.name(), RsaOaep256.name())) + _supported_key_wrap_algorithms = frozenset((Rsa1_5.name(), RsaOaep.name(), RsaOaep256.name())) + _supported_signature_algorithms = frozenset( + (Ps256.name(), Ps384.name(), Ps512.name(), Rs256.name(), Rs384.name(), Rs512.name(),) + ) + def __init__(self, kid=None): super(RsaKey, self).__init__() self._kid = kid @@ -212,10 +212,7 @@ def unwrap_key(self, encrypted_key, **kwargs): return decryptor.transform(encrypted_key) def is_private_key(self): - # return isinstance(self._rsa_impl, RSAPrivateKey) - # TODO returning False here even if someone sneaked in private key material because - # currently we don't want to perform decrypt/unwrap/sign locally - return False + return isinstance(self._rsa_impl, RSAPrivateKey) def _public_key_material(self): return self.public_key.public_numbers() diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/symmetric_key.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/symmetric_key.py index 2e4d7e73422f..e2cfdb4d6610 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/symmetric_key.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_internal/symmetric_key.py @@ -55,6 +55,26 @@ def __init__(self, kid=None, key_bytes=None, key_size=None): self._key = key_bytes + supported_encryption_algorithms = [] + supported_key_wrap_algorithms = [] + key_size = len(self._key) + if key_size >= key_size_128: + supported_encryption_algorithms.append(Aes128Cbc.name()) + supported_key_wrap_algorithms.append(AesKw128.name()) + if key_size >= key_size_192: + supported_encryption_algorithms.append(Aes192Cbc.name()) + supported_key_wrap_algorithms.append(AesKw192.name()) + if key_size >= key_size_256: + supported_encryption_algorithms.append(Aes256Cbc.name()) + supported_encryption_algorithms.append(Aes128CbcHmacSha256.name()) + supported_key_wrap_algorithms.append(AesKw256.name()) + if key_size >= key_size_384: + supported_encryption_algorithms.append(Aes192CbcHmacSha384.name()) + if key_size >= key_size_512: + supported_encryption_algorithms.append(Aes256CbcHmacSha512.name()) + self._supported_encryption_algorithms = frozenset(supported_encryption_algorithms) + self._supported_key_wrap_algorithms = frozenset(supported_key_wrap_algorithms) + def is_private_key(self): return True @@ -74,39 +94,6 @@ def default_encryption_algorithm(self): def default_key_wrap_algorithm(self): return _default_kw_alg_by_size[len(self._key)] - @property - def supported_encryption_algorithms(self): - supported = [] - key_size = len(self._key) - - if key_size >= key_size_128: - supported.append(Aes128Cbc.name()) - if key_size >= key_size_192: - supported.append(Aes192Cbc.name()) - if key_size >= key_size_256: - supported.append(Aes256Cbc.name()) - supported.append(Aes128CbcHmacSha256.name()) - if key_size >= key_size_384: - supported.append(Aes192CbcHmacSha384.name()) - if key_size >= key_size_512: - supported.append(Aes256CbcHmacSha512.name()) - - return supported - - @property - def supported_key_wrap_algorithms(self): - supported = [] - key_size = len(self._key) - - if key_size >= key_size_128: - supported.append(AesKw128.name()) - if key_size >= key_size_192: - supported.append(AesKw192.name()) - if key_size >= key_size_256: - supported.append(AesKw256.name()) - - return supported - def encrypt(self, plain_text, iv, **kwargs): # pylint:disable=arguments-differ algorithm = self._get_algorithm("encrypt", **kwargs) encryptor = algorithm.create_encryptor(key=self._key, iv=iv) @@ -128,7 +115,7 @@ def unwrap_key(self, encrypted_key, **kwargs): return decryptor.transform(encrypted_key) def sign(self, digest, **kwargs): - raise NotImplementedError() + raise NotImplementedError("Local signing isn't supported with symmetric keys") def verify(self, digest, signature, **kwargs): - raise NotImplementedError() + raise NotImplementedError("Local signature verification isn't supported with symmetric keys") diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/__init__.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/__init__.py new file mode 100644 index 000000000000..6648d5f19fa3 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/__init__.py @@ -0,0 +1,26 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import TYPE_CHECKING + +from .ec import EllipticCurveCryptographyProvider +from .rsa import RsaCryptographyProvider +from .symmetric import SymmetricCryptographyProvider +from ... import KeyType + +if TYPE_CHECKING: + from .local_provider import LocalCryptographyProvider + from ... import KeyVaultKey + + +def get_local_cryptography_provider(key): + # type: (KeyVaultKey) -> LocalCryptographyProvider + if key.key_type in (KeyType.ec, KeyType.ec_hsm): + return EllipticCurveCryptographyProvider(key) + if key.key_type in (KeyType.rsa, KeyType.rsa_hsm): + return RsaCryptographyProvider(key) + if key.key_type == KeyType.oct: + return SymmetricCryptographyProvider(key) + + raise ValueError('Unsupported key type "{}"'.format(key.key_type)) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/ec.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/ec.py new file mode 100644 index 000000000000..1534e1da9927 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/ec.py @@ -0,0 +1,37 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import TYPE_CHECKING + +from .local_provider import LocalCryptographyProvider +from .._internal import EllipticCurveKey +from ... import KeyOperation, KeyType + +if TYPE_CHECKING: + # pylint:disable=unused-import + from .local_provider import Algorithm + from .._internal import Key + from ... import KeyVaultKey + +_PRIVATE_KEY_OPERATIONS = frozenset((KeyOperation.decrypt, KeyOperation.sign, KeyOperation.unwrap_key)) + + +class EllipticCurveCryptographyProvider(LocalCryptographyProvider): + def _get_internal_key(self, key): + # type: (KeyVaultKey) -> Key + if key.key_type not in (KeyType.ec, KeyType.ec_hsm): + raise ValueError('"key" must be an EC or EC-HSM key') + return EllipticCurveKey.from_jwk(key.key) + + def supports(self, operation, algorithm): + # type: (KeyOperation, Algorithm) -> bool + if operation in _PRIVATE_KEY_OPERATIONS and not self._internal_key.is_private_key(): + return False + if operation in (KeyOperation.decrypt, KeyOperation.encrypt): + return algorithm in self._internal_key.supported_encryption_algorithms + if operation in (KeyOperation.unwrap_key, KeyOperation.wrap_key): + return algorithm in self._internal_key.supported_key_wrap_algorithms + if operation in (KeyOperation.sign, KeyOperation.verify): + return algorithm in self._internal_key.supported_signature_algorithms + return False diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/local_provider.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/local_provider.py new file mode 100644 index 000000000000..1d81dd897cc6 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/local_provider.py @@ -0,0 +1,97 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import abc +from typing import TYPE_CHECKING + +from azure.core.exceptions import AzureError + +from .. import DecryptResult, EncryptResult, SignResult, UnwrapResult, VerifyResult, WrapResult +from ... import KeyOperation + +try: + ABC = abc.ABC +except AttributeError: # Python 2.7 + ABC = abc.ABCMeta("ABC", (object,), {"__slots__": ()}) # type: ignore + +if TYPE_CHECKING: + # pylint:disable=unused-import + from typing import Union + from .._internal.key import Key + from .. import EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm + from ... import KeyVaultKey + + Algorithm = Union[EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm] + + +class LocalCryptographyProvider(ABC): + def __init__(self, key): + # type: (KeyVaultKey) -> None + self._allowed_ops = frozenset(key.key_operations) + self._internal_key = self._get_internal_key(key) + self._key = key + + @abc.abstractmethod + def _get_internal_key(self, key): + # type: (KeyVaultKey) -> Key + pass + + @abc.abstractmethod + def supports(self, operation, algorithm): + # type: (KeyOperation, Algorithm) -> bool + pass + + @property + def key_id(self): + # type: () -> str + """The full identifier of the provider's key. + + :rtype: str + """ + return self._key.id + + def _raise_if_unsupported(self, operation, algorithm): + # type: (KeyOperation, Algorithm) -> None + if not self.supports(operation, algorithm): + raise NotImplementedError( + 'This key does not support the "{}" operation with algorithm "{}"'.format(operation, algorithm) + ) + if operation not in self._allowed_ops: + raise AzureError('This key does not allow the "{}" operation'.format(operation)) + + def encrypt(self, algorithm, plaintext): + # type: (EncryptionAlgorithm, bytes) -> EncryptResult + self._raise_if_unsupported(KeyOperation.encrypt, algorithm) + ciphertext = self._internal_key.encrypt(plaintext, algorithm=algorithm.value) + return EncryptResult(key_id=self._key.id, algorithm=algorithm, ciphertext=ciphertext) + + def decrypt(self, algorithm, ciphertext): + # type: (EncryptionAlgorithm, bytes) -> DecryptResult + self._raise_if_unsupported(KeyOperation.decrypt, algorithm) + plaintext = self._internal_key.decrypt(ciphertext, iv=None, algorithm=algorithm.value) + return DecryptResult(key_id=self._key.id, algorithm=algorithm, plaintext=plaintext) + + def wrap_key(self, algorithm, key): + # type: (KeyWrapAlgorithm, bytes) -> WrapResult + self._raise_if_unsupported(KeyOperation.wrap_key, algorithm) + encrypted_key = self._internal_key.wrap_key(key, algorithm=algorithm.value) + return WrapResult(key_id=self._key.id, algorithm=algorithm, encrypted_key=encrypted_key) + + def unwrap_key(self, algorithm, encrypted_key): + # type: (KeyWrapAlgorithm, bytes) -> UnwrapResult + self._raise_if_unsupported(KeyOperation.unwrap_key, algorithm) + unwrapped_key = self._internal_key.unwrap_key(encrypted_key, algorithm=algorithm.value) + return UnwrapResult(key_id=self._key.id, algorithm=algorithm, key=unwrapped_key) + + def sign(self, algorithm, digest): + # type: (SignatureAlgorithm, bytes) -> SignResult + self._raise_if_unsupported(KeyOperation.sign, algorithm) + signature = self._internal_key.sign(digest, algorithm=algorithm.value) + return SignResult(key_id=self._key.id, algorithm=algorithm, signature=signature) + + def verify(self, algorithm, digest, signature): + # type: (SignatureAlgorithm, bytes, bytes) -> VerifyResult + self._raise_if_unsupported(KeyOperation.verify, algorithm) + is_valid = self._internal_key.verify(digest, signature, algorithm=algorithm.value) + return VerifyResult(key_id=self._key.id, algorithm=algorithm, is_valid=is_valid) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/rsa.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/rsa.py new file mode 100644 index 000000000000..b7d913a7f7ed --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/rsa.py @@ -0,0 +1,37 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import TYPE_CHECKING + +from .local_provider import LocalCryptographyProvider +from .._internal import RsaKey +from ... import KeyOperation, KeyType + +if TYPE_CHECKING: + # pylint:disable=unused-import + from .local_provider import Algorithm + from .._internal import Key + from ... import KeyVaultKey + +_PRIVATE_KEY_OPERATIONS = frozenset((KeyOperation.decrypt, KeyOperation.sign, KeyOperation.unwrap_key)) + + +class RsaCryptographyProvider(LocalCryptographyProvider): + def _get_internal_key(self, key): + # type: (KeyVaultKey) -> Key + if key.key_type not in (KeyType.rsa, KeyType.rsa_hsm): + raise ValueError('"key" must be an RSA or RSA-HSM key') + return RsaKey.from_jwk(key.key) + + def supports(self, operation, algorithm): + # type: (KeyOperation, Algorithm) -> bool + if operation in _PRIVATE_KEY_OPERATIONS and not self._internal_key.is_private_key(): + return False + if operation in (KeyOperation.decrypt, KeyOperation.encrypt): + return algorithm in self._internal_key.supported_encryption_algorithms + if operation in (KeyOperation.unwrap_key, KeyOperation.wrap_key): + return algorithm in self._internal_key.supported_key_wrap_algorithms + if operation in (KeyOperation.sign, KeyOperation.verify): + return algorithm in self._internal_key.supported_signature_algorithms + return False diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/symmetric.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/symmetric.py new file mode 100644 index 000000000000..ef34238ff395 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/crypto/_providers/symmetric.py @@ -0,0 +1,30 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import TYPE_CHECKING + +from .local_provider import LocalCryptographyProvider +from .._internal import SymmetricKey +from ... import KeyOperation, KeyType + +if TYPE_CHECKING: + # pylint:disable=unused-import + from .local_provider import Algorithm + from .._internal import Key + from ... import KeyVaultKey + + +class SymmetricCryptographyProvider(LocalCryptographyProvider): + def _get_internal_key(self, key): + # type: (KeyVaultKey) -> Key + if key.key_type != KeyType.oct: + raise ValueError('"key" must be an oct (symmetric) key') + return SymmetricKey.from_jwk(key.key) + + def supports(self, operation, algorithm): + # type: (KeyOperation, Algorithm) -> bool + return ( + operation in (KeyOperation.unwrap_key, KeyOperation.wrap_key) + and algorithm in self._internal_key.supported_key_wrap_algorithms + ) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/keys.py b/sdk/keyvault/azure-keyvault-keys/tests/keys.py new file mode 100644 index 000000000000..80db438ae5f2 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/keys.py @@ -0,0 +1,103 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Keys for use in test cases""" + +from azure.keyvault.keys import KeyCurveName, KeyVaultKey + +EC_KEYS = { + KeyCurveName.p_256: KeyVaultKey( + key_id="https://localhost/keys/key-name/version", + jwk={ + "kty": "EC", + "key_ops": ["encrypt", "decrypt", "wrapKey", "unwrapKey", "sign", "verify"], + "d": b"^T\xf9F\r\xb5\xa8\x1a \n\xa6[\xfa\x12-0\n\xb7\x92\t\xee\xd1\x03\x9d\xd5\xca\x9c\n\xc4\x10\xefI", + "crv": "P-256", + "x": b"\xba\xb3Z\\\xea\x01\x10\xb4\xbe\x81\xb0\xc4h\xc2\xeb1\xa3\xbfb\x11A\xd939\xcc\x8aY\x05\xf3\x83\x12E", + "y": b"\x1fs\xbe\xfa\xc7\xef\xd3{H\xea\x1a\xcb:r\xc2\x8c\xf2\xde#z2B]\xc3\xa6>\xc0\xa6.pUN", + }, + ), + KeyCurveName.p_384: KeyVaultKey( + key_id="https://localhost/keys/key-name/version", + jwk={ + "kty": "EC", + "key_ops": ["encrypt", "decrypt", "wrapKey", "unwrapKey", "sign", "verify"], + "d": b"\xe1\x13g}\xdeV\xad(`\x85\xed\x8c\xc4}\x9f\x9eE\x14\x1c\x12\xff\xa4s\xa5L\x05j*W\xb3X\x88A\xe2?\x1f\x80\xd6\xf1\x0fR\x91\x0fh\xf3EH\xcf", + "crv": "P-384", + "x": b"\xa5\xd1\tFX\x12\xd5\xa8\xb5\x00\xe4p\xf8S\x8e\x8d\x80q\xdc.\xbe\x86\xef\xf1\x99\x15\xaa\xde0\xb1'(\xc0\xcb;43\xca\x14\x17,\xe1T\x10\xc2iK\x08", + "y": b"H6\x19\x8c\xe6\xcc\x0brhJ\x98\xd3\x9b\xf2\x08\xf5\xf0z&/S\xd2\xb3x \x18[\xcc\xb9E\xb6M\x87\xfb\x06z\xd9\x95\xcbj\x84\x8aPq$\xf3t\x9c", + }, + ), + KeyCurveName.p_256_k: KeyVaultKey( + key_id="https://localhost/keys/key-name/version", + jwk={ + "kty": "EC", + "key_ops": ["encrypt", "decrypt", "wrapKey", "unwrapKey", "sign", "verify"], + "d": b"\x84\x9f\xb9\x07\x91\x1c\xb9V#\x8d\xb8\xec+\x8e\xba\x17%\xc9[\xb1\xd0J\x94\xfd\xcb\x1c\x89\xd1[\x10\x86R", + "crv": "P-256K", + "x": b"~\xa5i\xa0\n\xe5wo\xf7\x08of\xb8\xf1\xee\xc8\x0b\x18\xde\x99t\x95\x06\x9c\xccb&L\xe4|;\xf6", + "y": b"t\xe9\x9d\x07^\x81\xb3\xdb(8\x90\x95\xdd \xa7\xf6\x0ep\xec$\x1cq\xc7\xcf\x08\xf8\xb5r\x9e04\xf3", + }, + ), + KeyCurveName.p_521: KeyVaultKey( + key_id="https://localhost/keys/key-name/version", + jwk={ + "kty": "EC", + "key_ops": ["encrypt", "decrypt", "wrapKey", "unwrapKey", "sign", "verify"], + "d": b"\xb55`\xbe\xd7\x0f'0D\xdf\xe0 \xd4\xed?\xf4\xee\xe0\xf0l\xd7\x08\x87l0\x04R\x01l1\x85\xb7\xf5\x14H\xde\x19\xeb\x13\xab_\xf0\t\xbe\x88\x18\x1b\xbd.\x89\xe9\xda3\xdc\x97\xc5\x18\x0f8\xa4\xbeb\x83z\xf0", + "crv": "P-521", + "x": b"\x83\x95Q\x1e\xa3\xf3\xad\xde\xc6\xb4\xdd\xe8\x1d^}\x15\x85\\\xcc\x95\xe4\xfc\xea\x89\x7f\x8d\xd2\xe5\x81/u\xbb^\xd0W\t\x93P\xdb3\xd6T\x8f\x9c\x90\xa1\x95\xffQ\xa0\x96\xd7\x11a.C\x03?\x81\x02D\xb2x\xf3\xaa", + "y": b'\x01\x8c\xda\xc9\x1ej\xe5B7\xcc\xaaFD\x0e\xb2\xe9\xaad\xf9\n\xc5\x93:\xcaBE\xbc\x04\x94\x03\xe0\xb8h\x00K3L"\xfa\r\x91\x8fJ\xbfJF2\x17\xd0\xfb\xbb@9\xbe\x87+\x05I\xab\x9cu\xafV\xaa\x1c\x99', + }, + ), +} + + +RSA_KEYS = { + 2048: KeyVaultKey( + key_id="https://localhost/keys/key-name/version", + jwk={ + "kty": "RSA", + "key_ops": ["encrypt", "decrypt", "wrapKey", "unwrapKey", "sign", "verify"], + "n": b"\xeb\xc8z\xdb\xa4\xa3U;\xb7Bn\xcfP\x9dA\x89\xdcq\xc7\x89\xe2I\x8dI9\xd5\x90b\xf0\xe5\t\xaf\x90\xf2\x8aC`5\xb1h\xbb\x19U\x86\xe1\xc2\x9f\xee_N\x93\xdec*\xc8J\x1bS3\xe4\xa0\xe5\x82\x0e\xd7P\xba\x1d\xbbb\xfbF\x85\x80\x9c\xb8)\xfb\xbb\xc0Y\xbasb\xc9-AZ\x85\xeajt\xcc\xf1\x96\x14\xaeD\x88,\xa3\x13\xcaE\xb3\xb2\xd7\x152P\xee\xf6lS\x1c\xdbp\xe4^\xd3\xf1\x18\xe6?\x94\xc9\x1dS\xdc\xc9\x08s\xd1bA\xa5\x1a?\x0cT\x7fAs\xb8\xfc\xdb\x0c\x82\xad+\x80\xb8=\xa2:\x85~\xe1\xef\xdc@w\x04\\j\x9c\x18h\x14j?*\xa8g\xd5\x91+\xe1\t!:\x0b\x10\xe0\x99\xc9\xe3g\x1e\xfao\xb5\x8fD\xd6r\x7f\x94\x7fX\x00\x9e#]\xcf@z\xae\x82\xa1\xa2)\x00\x8e\xb3\xdc\xc1\xdczNm\xb5', + "dp": b"\x91|\xbd\xa3\xf6\x01\x08\xb7\x92\x1c\x1a}\xe3\xc4\xb1#\x97\x0bxT\xb6\x13\xecx\x8f\xf6\xf3=B=\x80\xb2@\xf2\xce6rSDtB\x04u\x8d\x0c\xb3J\x84\xa3\xedz>\x0fW\xdd\xeeu_[\xf2X-\xdc\xb5}\xd2\xd0b\x11\x9e\x16l\xbc\xcdGZ}h\xa8&\x12 \x08Y\x9d}\x08\x06\xd3c\x93\x106\x8a\x1a\x19\x9f\xf5\x95P\xcce#\x05N\x01\xb8\xbb\x07\x04@\x19\x8e\xb3\x81WzIow\xff\xe1a\xe9\xd0`e\xff", + "dq": b"\x0eV\x80:\xd9k\xa8\x02U(Ew\x14\x16\x01C\xd8\xe2\x06\x10\x9a\x9aZ\xf1\xf3\x0f\xbe&E\xaa<\t\xb2\xbf&\x0c1\xf3B\xf0j\xc4\x8d\xf7\xa2^\x8f9\x9fY\x0f\xf8\x0f\xa7\x9c\xbd\xb9\xb6<\xaf\xc6\x9b\xb8wJ\x9d\x0c\xa0\xd0|\x97?b\xb0\x0e\x19\xf9\x96\xf3\x1e\x96\xfcp\xe7\x7f\x0f\xad(h\x878'\xccJ\x022\xc7\x06\x89|\x99DH\x9e\x8d\x97\xb2\xbe#(\xb9\xef\xb3\xc9\x00!\\8\x98WVO\x1e\xb0u\xb1l\x11", + "qi": b"ny\xfb\x87\x05-\xd6\x8dvt\xfd\xe4\x7f$7\xe0\xe3aU(\xa0b[\xc9\xb1PN\xfe\xfd\xf1\x80\xe4?:\x1eA\x8f\x86\xdd\xfd)\xcf\x93C\xac\x14\xbf\xe7\xd9#\x0e\x9e\x14\x15\xd7\xc9\x0b\xf4\xdd<\xe6\xe1,/u\xeb,\x0b\xaaX\xadMo\x86Q\xb00\xd4A\x97\x03\x8d\x86E\x0f;\xcc\xf0\x88\x0f\x90\xd2\xfdc]\xdd\xed\xd8\xfd\xd3\x8d\x99\xc7Uz\xa4\x8d)\x03\xd1\xf7\xb1\xdf\x85\x05Uvsc2W\xa8\x19\x8eS55u", + "p": b'\xf8\xa8\x80\x8f\xe9\x11.\xc8\xcc\xbe\x91\xfe\xe8&S\xdea6\x85E\xa7\xe40\xc444qq\xa1\xf3KrI\xb9\xda\x9b\xae\r\xd6\x9e\x18\xa3\xdb\xc5\x81*\xd2\xa2r\xf0Q\x94\x97\x0eMd\x9c\xe0v\xb0"\x13\xac\x91\xcc\x8e\x15\xba\xf7\xc1\x82\xc2k\x83\x9c\n\xbd\x8f$^\xcaz4r3\xd6\xab\xc0p:\xe6\x95\t\x98\x89<\x17\xd7\x9a\x9e\t\xaa\xaf\xefW\xceR\x07v[\xa8\xa22\x99|\x9eM\xe4\x17{\xb3\x12\x86p\xc6\xdc\r\x0f', + "q": b"\xf2.Z\xe0pT\x8cE|\xbd\xaar\xa4}\xfaP\xe6\x86\x00,a\xd8@&\xed\xa3.Q\x89\xccbo\xd2\"\\\x97}\x0f\xa1;~{\x08\xb8\xc3\xb5'\x12aL}\xb2\xe9H\x13Z\xaf\x80\xaf\xa3Q\x84l\xee\xc8\xdd\x1c\xe3\xd5\x9a\x1c<\xd4'\x1c\xa3\x14\xa5\x85\xe8b\xe7\xb6\\\xe5\x8d\xbc\xf8u'\xa1\xfb\x03\xd0e!\xdc\xb8\xc6\xacA\x82\x7f\xecZ$\xf8w\xae\x1cO\xbb;)\xf5\x12;\xc6=9q\xe9\x1a\xe4PZ\xe7[", + }, + ), + 3072: KeyVaultKey( + key_id="https://localhost/keys/key-name/version", + jwk={ + "kty": "RSA", + "key_ops": ["encrypt", "decrypt", "wrapKey", "unwrapKey", "sign", "verify"], + "n": b"\xd4\x17\x03\t\xed\x10q\x96~\xaf\xf1\xa2\xd6Cb\xbf5\x9a\t\xda\xe0\xfe\x0c\xfa8C\x94\xad\x84\xdb\x90B\xb17\xf4\xb4\x16\x1b\x8d\xdc\xd2\t3\xae\x86\xffP\xfa\x07\x8cm\xb2?ZH\x0e\xb3\xdf\x18y`\x95\xf1\xe0\xf5\xa80O\xfdZ\x07\x00}\xd7g@\xd7\xce\x1a3|\xeb\x80(\xf5\xb9XR\x82`\xd9m\xd3\x9d\xf4\x02BPN\xdf\xfb\xcd\xad\r\xe7mM\xf9'\xdd\x1b9vK\x82!\xb6\x8b\x04\xc3\xc9b\xe1\xe6\x9b\n\xe1\xb6\x82\xf3\x93k\xba\xd0g\x91\x08\x80\x8eR\xd5*\xe0\x85\x1a\xe6\xe7\x94\xb4O]\x07\x14\x92\xc70\x807J\x00i\x19A \x878\xaf%C\x17\xb9\x0e\x01\x9f\x9b\x1a7um\xd1\x1c\xb0\xd9\tyR\xaf\xac\xc80C\xe4Ks-Q#\x07 5\xd0s\xa0c2\x9b-[b\x93\x00\x96\x0bWRN\xde\x0eF\xb6%\xdf\xfc`\xfa[r\xa7\x99\xcd\xfaX\xa1\x17\xd8JA\xbbE>\x1c\xe6\xe8\xf6\x8f\xcdE\x88\xae\xc8zc\xf1\xc2N\x9f+x\xab\x08\xde4Jc\xdc6\xac\xba\x1bh\xaav\xdbR\x96\x1a\xe3\xb2x\xb3x\xc3\xf0\x19\xfav\x0e\x1c\xcc\x8f\xfb\xbe!\xa8\x13\xc8\xa8\xa0\xa3\xcc\xda3?\x05,%\x10_t\x92\x80D\xec&UY1\x91\x9bV\x95\xc9\x80\xeaF\x14\xdb\x87ORp\x0b\xff1Eh\xb7?\xf2\xdbX\xbax\xc3G\xd0\x8fms#\x82]0@N\x17\xd7\xe4i\x11:C\x1b\x0bJ\x11\xda\x19s/\x7f\xbfi`\x8b\xf5\x91\x19d< \xdc\x1f\xef", + "e": b"\x01\x00\x01", + "d": b"r\xa5\xf9\xe3\x8c\xbd\xfd\xd9\x0f\xa4\xc6\xa7r\xd1\xbcx\x86^=\x83\x8fje\xd3\x81D\xaf\x03Y\xb5b\xf7Z[X1]^\t\xc2\xdb\xdbS\x8f\xbd\xb4\x18\xaf\x00\x13\x9e\\\xe3\xb8G\t\x83\x9cN\xdb\x96%\x98:61s\xe4Y\x9d'\xf9\xd1\x0c\xf6\xe8\xeb\x7f\xa3T?\xec\x02<\xa2\x10N9S\xf7L\xb1\xfc\xd9\xfe\xc4\x95\xdaY\xb8\xe4\xa2[1\x0f\xf0[]\x1bz\x7f\xb4\x01Ir\x10\r`z\xf3\xe7RG\xb1d\x10n\xd1=Z\xb4Oa\x0e5^\x06M\x90\" \xcfk\xcd(,\xb7\xf4\xff\xbf\x83\x17G\xbc\xccf`y\x02\xcc\xd4\x03m\xdcJ\x9c\x8e\xb9B\xday\x13\xa47\x06\x1e\x16\xea\xa8\xb1\x88\xa3\xe4\xbf%QX[\xbb_\xb3\xe2\xa1\xbb~\n\xb8X\xbbv\xe2\x8d`\xb6^\xe2\xdb\x85L\xff\x12|\x8b\xb0\x07\x8d\x97\xcb\xbe\x1e9<\xc1Aq\xc8)%\xd44\xd4\xd7\xbc\x90\x1b\xae\x8f\xf0\xc4\x86O\xc6Y@\xfa\x89\xfb\xcfc\xba,\x83\xe9\x1bR\\\x8b\x9c\xec4\xc7\xcd\xfb$\x9d\xdau\x0cy\xcci\xbd\x04S\x13\xea\xd1\xf0-<\x8c\xd7\xd5\x16\x88f\t\xfa\x9c\x95\xda\xf9\xffePZ\x94{[1\xaa\x9c\xf9\x9c\x12|\xf5\xe2\xb2\xd4\x114Q\x9f\x0c\xed6\x06\x1e\xbb\x9d&Z\xa2\xf5\x1c\x1c\xc1M\xbb\x7f\xaf\xb5~\xec\x13\xef ;\x8a\x85\x0f\x93\xfb+\xafJ\x8e\xb5\xb8\x02\xa8|*7\x8c\x7f\t0\xb8\xc4\xcb\x8f\xbf<~\x958Y\xd1\xdf\xc6\x893\xe5\x17\xcb\xca\xeaSp\x894q", + "dp": b"\x824'\xf8\xaf\x99\xbf\xa0\x05\x1a\xca\x1a\xf8\xf3\x8ed\xd5\x8a\xa9/+\\\xf8\xfa\x11\xd7\x00\xd1\t\x95\xbb\x8f\x1e\xdd\x03\xf4[\xa3E\xb3Q\xc1\x9cG\x83t\x00\xe0\xcf}'\xafj\x94sB\xde\xab\xf1J\xcb\x13F\xd3[jf1\xec\xf4\xcdG.#\xe7\xb5)1\xa7{\x8cQ\xc4GI\x81\x8c']\xc9\xb2\x9by\x93\x82\x8cD\xc1\x8e\x12\x04\xef\xb7w5\xd7F\xea\xcf\x93tu\x86kM\xf1\xd5\xdd\x8d\x88j\x9f\x85F\xa7\xf6\xe2\n\xbc\xdey\xcak\xa0(\xb3\x18Ci\xc0\xfd\xaa\xda\x01\xee\xc1>\xb33\xb0\xe9-\xe0B\xe4\xc8\x81\xeb\xd8\x19\xa8G\x05\x93T\xccx\xaa\xe1h\xd4\x8f\xf5\xf3\xdfTb\x1cUkE1\xaap\x89\xe0\xddR<\xfa\xfb1", + "dq": b'xs\x81\x0bF\xa1\xf6\x05j\x03V~GD\x1dnHq{\x88b\xfd\xc5p\x07U\x8c\xd6o\x93N\xc8I\xc2\x1d\xc3\x16v\xf5\x0b\x94\xd3\x04b\xb2\xb3rkpO\xdfv,\xd5\x1e\xe6\x0b\x98\xeap\xc9\x84\xbc\xcf\x1f\x8b\x91\xc5tY\xa3w+/^\x15%\x18\x12@\xb5\xb6\xa8\'}\xd7w\xb6\x84\xaaQp\x06o\xce@]p\xa4!\xf5\x1bQ5\xe1=PMkA\x8f\x95\xe7\xdd-\x16\x00p.e\xd0l\xe6\r\xbb\x9cI\xcd\x18i\x9e\xdb\xe6\x86\xec\xad[\xee"\x8a\x12\xb2\xc7\xcc\xdcV\xc9\xce\xbdG\xbf\xa7K \xa8\xec\xf6\xecF\x98\x9cI\xde\x18\x12tjQ2+\xaa_\xef\xf1\xaaWZf\xdd\x0c\x1e\xaf"xq', + "qi": b'8\xa1B\xe2@\xae\x10(_z\xfa\x13\xfd\xd6\x85b\xc3\x8d\x0e!\xfe\x18\x8f4\x80d[ p`-\xfc)\xfc\xa2\x93\x18\x1f\xe3=8\x08qxa9\x99i\xc6\x99\x1e\xb9f\x93\xdc"\x10\x1f\x89\x9a\x85\x89\xb0.\x9c\x04=\xd1`L+\xc8ZR\x07\xd7\x96u\\rzH\xc4\x1b\x13\x97\xda\x89\x8eB&\xe3\x07\x8bNv\xb2\xbe\xe2\xb2\x98\xfb4\x8b*\\\x17\xe5\xf2\xb7-9c\xee&r1\xd1\xd4\xcfR\xfa\xa7\x0b\x95\xb0\x15\x15\x1c\x11"\x1fX\xed\x86\xe7\x15\xce\xe6\x87n\x9b\xe2>z*i\xe0\xcc\xa1\xa0z\x18h\xa8R @\xd1M.\x9bG|V\x12T\xdc\xf2o\x8c\xc9\x1b\xd2\xebu\xf8\xf7\xcd\xbf\xea\x99\x0b\xc0tt\x17\xc5\xd0\xa0\x03\x8d', + "p": b"\xf6\xd9\xfa\xff\xff0G\x85\xc0\x91\x9e\xa4\x9bT\xb7^+\x99\xd9\xeb-\xe3\x00w\x84\xe5L\xc8S\xb4p\xd6\xe6\xca\xc2U\xe7,b\x83\xd1;\x86\xf8<\xa2]h\x18\xde9S\x05\x10\x83u\xf8\xe1\xb9\xaa\x8a\x80uZ\xf5`\xe0\xden]r_\x96\xa0\x01\x9b\x86\xb716[7\xdf{d^>\to66l\xfb\xc9:yE=\xee\xba\r\xa3\xab60\x91\xf4\xfe\xec#\xd0n\xde\xa6\xa1{o\xca\xb4D\xe7Zw\x9an\xc5\\\x9c\xdf\xc7\x87q#\xcd\xac^\x90\"#\xefwYW\x02\x05\x1e_\xc6\x13X1\x851\xd4\xc9\xc1\x9a\xd7A\x08y\x93\xae\xe9\x07\xc2{\xff\x02\x19\xe0\x12\x8f\xfe\xbcW\xeeY\xa2\x91\x1c\x97\xc8\xcc\xaa\x84\xe1\xbf\x9d\xfc\xbe'", + "q": b"\xdb\xf3:~\xd2\x1d\x9e\xd8i\xa1\"\xceK\xd3Z\\\xccW\x81\x9eD\xc6\xcf\xb9\x1a\xbb+.\xb96\x11\xea;\xf6K\xbbD\xd8\xdd\x89\xa2\xb8Y\xf8\xf28b\xf0\x91\xd1\xfa\x14|\xe0\xbd\xdf<&\xc9\xfc\xcd\xdd\xd5\xf1\x82*\x8c\xcf\x95+\xcf\x1a@\xf4\xc0\xd9Q\xcbi[\xcc\xdd\xe7]/+l\x05G\xf2\xce\x0b'\xac\tMrg\x9c\x0b1w\xfaM]\xe0\xa7\x9d?\xb7\xd5\xd3\x92\x84\x81\xf4\xa7\xc0\xc3\xffz`\x91\x96M]\x180\xbfp#k)\x10\x19\xb5\x82\x1dg\xbak\x91\xff\xf8\niZ\xa4@\x03\xa1SB\xc6`e\xa7\xd1\xde\xec\x03\xf7\x94\x9b\x1bTr\x17\n\xee*\xd4s0\x0bc9\x91\x02W\x84\x96\xa7|f@\xdf\xa8Q&\xf4\xf9", + }, + ), + 4096: KeyVaultKey( + key_id="https://localhost/keys/key-name/version", + jwk={ + "kty": "RSA", + "key_ops": ["encrypt", "decrypt", "wrapKey", "unwrapKey", "sign", "verify"], + "n": b"\xea\x1e\x81=\n\x98\x81\xdf'T\x9e\xbaH\x19\n\x1d\x8b\x9a\xf1DJ\xe9\x7f6Q\x84o\x9f\x13}5p/\xce\xf7\xc5;\xef\x1a\x88\xb6\nvm\x94\xc1_N\xc4\xbd)\x80Z)\x12\x1e\xa5&\x8a]\xff\xbe\xfc1\x8e\xc8\xfa\x0b\x14\x0c\xeen\xa4S\xc5P\x08\x0e|\x1b\x01/\x98\xe1\xd8A\xa5\x88\x9b\xbd\xb9i\xe83A(p\x9f\xbf3Gp\xc7\xb7\xcc\xf7\x86\x08\xff\x10\x9f)=W\xe8\x8a\xa4H\xb6\x0c\xd44\xe3K\xa7L\x11.\xdd\x1d\xe5\xc9\xcf\xed.4mo\xe2\x03\x8e\xc9\xc2F\xc1\xf3\xdc\x18\\3\xe7\xe2Z\xf8\xccr\xea\xc4\x0c\x82I\x01E\x8e\x875\x87\x8f\xb4r\xd6\x1fZts\x1f\x05\xfa\xa9\xec\xe6z5\xd7\x19V*\xfeY\t$\xeegG\xfdz\x92\x10\xcb\xbb\x19$\xaa\xeb\x0c\t\xd1Nk6\x84c[`\x16\xd2!\x97\x19\xcc\x08\x9f\xc4\x1c\xed\x12k3{\xa1`\x97\x97\xa7\x05:L\x11t\xfa\xc4I\xab\xber\xd0M\xf4Fi\r\xaa-\x9b\xdb\xdaX?\x98w\xf1\xc4O\x01\xd2\x9b\xb0\x1a\x8d`Wmh3h@\x08\rP)\x9bt0t\xad4$\xfc\xd8\xe2\xd8M3\x91\xa2Y\x91\xcb\xeb\x1e\x12\xe2\xc2\x84\xa8Zs\xf3o\xa5\xa7\x00\x14>~<\x81\x10\x8d\x9d\xe4\\%\xc1\xf2\xd8~[\xb7!\xb9\x96]tb\xf7\xddt\x9f\xc1o\xc8\x99\xb3=\xe1+g\x90\xc6\xb9\xe2$wu\xca|\x97v\x13\x980\xa2\xa5\xb7\xe8u\xb1\x05/w\xafg?\xc3\x18\xaf\xfc\xe6;A\x84 %s\xabw\x83\xd7\x7fD\x8a\xca\xab\xb48\x8c\x06\xe6\xf5\xf1\xfb&&\x10h\x18(\x08d\x0ej\xe077W\xb3_\xd7\xa6Ds0\x13\xf2t\x92V\xceV\xce/O\xbeA*b \xb4\xc0\x01s\xa3M\xe1\x0c2 <\xc2\x80\xd9b\xeb>\xa8\xac\xba\x80v\xfd\x86\x12\xe0\xd2\xec\xe5\xcc\xd9\x10C\xbab\xcc\xcb\xf1\xa0\x11\x03\xcauP[~^LjB\x01\xc3\xa65\x9f\xfb\x83\xdb\xe0\xf1\xd2\xe3\xd1\x06\x17N\x90\x04\x02\x0b\xa4f\xc4\x01\xd5\x02\x85\xa7l\xd3\x17\x1c}\"hf\x16\xf2\xfc\xcfl\x9e\x99t\x82{9\x8cH\xa98\xbe\xd1|y\xc6a\xa2\x81#*\x84\xb8N\xb2gM\xd8i\x03\xcf\xaf\xd8v\x80\x04\\\x98\xbf\xe4\xca\x85\xe7\xb9C)\xff\x8bN\x07\xd2\xa9d\xafB\xaesi\x12\xf9\xddx\xeb{|1\xd3tH\xc6\xfc0~\xd8BB\xd0\x0b\xa1\x7fS\x8e\xf5\x88\xc4\xe8\xfa2\xf2\xff~\x07\xfe\x08\xab\x80\xd6,\xdfx>\x898W\x98+\xaf\x84\xf1\xd7\x01q\xbf\xdd\xf9\xee\"M\xa8\xf7\xc5\xf5`\x05\xde\xf9TS\x07\xd6\xc7\x90\xf0+\xa0`r\xc7\x8f\xd9\x0b\x12W\x17\xb9\xa3\x02\xf6\x02K\xfb6[-82\x14^Iz\t\x80.\x13gL\x17\xa3=\x9e\xad\xf7\xder\x85P\xcb\xb9\xcb\xa8\xf4\xee\x87e\x93\xe2\x0f\xf7]m\xb94N\xd3\xc6\xb0\xcc"\x90s\xeb\xf5\xb0\x19', + "dq": b'\xed\xdbX\xa5\xbbx\xd0\xb8UI\x80\xc6\xd0k\xe0*\xa4\xa3\x9ahk;\xb3\xc6L\xce\xd3N\x0e\xc3\xce\x00\x9e\xe4-\xc7H=\xd1\x05y\xef\xb1\xe8\xd9\xb9\xdd\x0e\x85#\x95\xa7\xfcyM\x97?\xbas\x03\xc3\x1a1\xee\xd7-\x841\xc5\x1e\xc5.R\xe2\x86\xa0\xbcM\xcd\xcalt \x06#\r\xfc\xe2\xb9\xd6\x91\xcbzGv\x8eD\xdd\xb7\xf8\xe7\\7\xaf"\xf9q\x9fK\x17u\xd2S1\xef\xe5\xb4\xd9u\x0f;\x81\xb4\x88<\x9bRj\xb3\xec\x1f\xef\x7f\x01#J\xf2iO"\x8e\xe9\xa7\xff\x93\xaa,Z\x07\x8f}\xabS\\\x97a\xa2\x1f\xde\xcb\xc2\x8f^tW\x00L\xa51\nD\xc79y\x11\x11\x03\xa5Vy8R\xf3IZ\xa4\xda\xa8\xa2\x16\x03y#\x1b\xe2|o<\x04\xdeZ\xa2j#/\xdd\xe2\xc13P\xe0\x94;\x97}\x83\x0c\xe7[p\x143\x08\xf0\xd4FH\xa8=(\xd5\x1b\xd1\xbb\xdeb\xd1]B\xcf\xbe\xb8=iS o\x8fF\xa6V\x89^\xf5\x00\x99', + "qi": b'.\x988y\xe9:1Q\x06\x89\xb3\x85\xdc\xfd\xe9]\xb1\xd0\xadb\xef\xbb\x8d\x18.\xa1\xc9\x92m\xe2b\x97\xea\xcf\xd0\xcc\xc8f\x9e\x9a\x9f\xae\x05\x16\x8dD?\xd3\xed}\xfcMA\x97x\xc4\xf5\xa1i\x14\xda\xfcr\x7f\xddN\xefT\x1a\xe5\xf3t\xa4z\x1f\xef\xf8\x8d\x1a7sJ\xfc\xfc\x19\x99Z\x1aR\x9a\xdch\x91,qo\x13\xc3\xf3\xd2\xe13O\x12\xa7t\xbfk\xa2\x84\xd2x\xec\x18M\n\xc6\xf7;\x9a!:"\xa2\nmtsV\xe2\xb8\xdf\xdfzI\xcf7g\xa9\xe7h\x10V%\x9f\x88x*`Gf\xe99\x86\xa8\x8e\x1e\xf2\xf7\xeb\xfd\xf0\xca\x84\xa1?\x8f\x18\xbb\x07{DR+\x86\xdeA\x13V\x1ad`\x05W\x88\x98\x86\xb2Ti25\xfa\xb9\x81?g\x13\xd4\xbb\xbe\xde\xef\x04\'\xd5\xef\xf7!\xb6\x06\n\x9b\xc7d\x90Wk\xb2\x82e3\xa8\t\xc2a\xb5\x1e\xcf\xd6rWa" \xd1\xde\xa8"\x1aBB\x1a0\xaf\x90bjv\xbe\xa9\x83\xfdk$\xb5', + "p": b"\xfa\xf4[T\xb5<\x06t\xfaN\x96!2\x12,\x87\x82$y\xadHsBX\xe9\xc7\xed\x8a6\xc0\x14\x87\xed\x8a\xd0\xad\x86\x05\xc1or^\x87\xb2\xcc\xa2\x08_\xed\xe2N\x91\xa0#\xba\xaa\x03\xba\xffv\xad\x9az\xcej\x8et\x96S\x1b\xbd\x93\x99\x94Q\xac\xdd4\x07\x9b\x17x8\xaf\x9b\xdd\xadM6\x1fk\xfa \xdd\xdf\xcbp]b|f6n\xd0U\xc0\xee0\x94\xf6!\x93}\xa3\nE\xa5\xc3\x86\xff\x82YO\x0c%\xfe\xeb_T\xa8\x8e\xd3\xe3\x80\x8d\x06\xca\xdf\xf1J5\xf1\x87\xc7g\x17\xe3\xba\xd24\x0b\xa6\xd6\x1bR\x9d_!$\x8c \xc0\xcdH\x16y\xb9\xdd\x9a\x9c!\xc9'SU\x91\x9a\xd1\x8b\xb9n\xb2\xaa\xd15JO\xf0\x08c\xe0`!Y(\xf8K{\xbb\x8d\xdf\x05\xaac\xc7\xbaeo\x1d\xb4\x11\x01r\xc8`'\x02\x92f\xc1^\xc5\xf7H\r\xfd\xd2.A\xb8&\xd7*>\xe8[;\xd5\xa0Z*\x8b^{\xc7\x8bw\x8d\x1c\xbe6\xd4}\x12\xd5+", + "q": b'\xee\xd3\x7fo\xff\r\x91<}\x9f\xe0\x8f\xc6\x06\xad\x80\x1em\xbe\xa6_\xdd[%Z?\x8b\xe4\xb1\xcd\xea\xec\x9b\xd2\x19\'\x7f\xe8\x80\xf0\xc40A\xcc\xa8} N\xd3\xcbXI\xf6\xb1\xa7\xf7lp\xf3\xaaq\x8d\x80\x83\x10\xc8\xbb)\x06\xc8\x8f\xea\x11\\\xd2\x99C\x95\x7f\xd9\x92w)\xd4\x90\x1dNF\xbd\xf3\xed"\x84\xcej\x81\xfb\xf9\xd2qv\xc9\x152K\xb8\xf7\x10|\xc7\xeb\xdc\xf0\xb9s\x0f\xc0\x0c\xf7T\xb7B\xa4\x05\xbb\x124\xde\xc0\xec\x88\xb5"qI\x91=\x0cso\\R\xc8\x9a\xe3\xff\xc3\xc5\xdb\xb9\xf8\xbdL\xfc\x9c;\xf3\x90\xa1\xa2\xf3\x08\xb5\x07\x19\xceZ@\xea\xe2\x9acO\r\x1f\x1fL\x1c#?\x0b\x0e\x9d\xbd\x97/\xb5\xb0\x90\x84>s\xccs\xa9R\x988\x06\x18\xaf\r\xfa=\xa3\xcf\xa4\x90o\x95\x95\xbd\x1c\xee\xac*\xc2\xad\xeec4THb\xcb{\xc0\x08Nj>?\x16%\x9d\x0eC\x83\xf7\xc8\x80\x14\x91l\x19p\x17\xb8C\xf3\xf4\xb8\x07\xfe\xa6)', + }, + ), +} 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 index 5f4889d2e452..da7e35b7bcec 100644 --- 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 @@ -13,7 +13,7 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.19041-SP0) method: POST uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1 response: @@ -28,7 +28,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Wed, 29 Jul 2020 16:12:00 GMT + - Wed, 08 Jul 2020 23:56:23 GMT expires: - '-1' pragma: @@ -43,11 +43,11 @@ interactions: x-content-type-options: - nosniff x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; + - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=InterNetwork; x-ms-keyvault-region: - westus x-ms-keyvault-service-version: - - 1.1.10.0 + - 1.1.8.0 x-powered-by: - ASP.NET status: @@ -67,12 +67,12 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.19041-SP0) method: POST uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1 response: body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/0aaab98564734fcdbe66073de241c33b","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"7x35JTvGBCTQP2XUsojh2QMLZ98Qq7C4499vFHVazyQwT3zw4joAmaOrHpxtirVuZKo1ifnBHPsr-vJbZlzWbWzVmkuhgWIt3xQY7NvQpZAG-5zUk9wQxLWuyOr1jaVQzRjpxfRqsnilnTrCjmJz7LMxT02iy8pht39aKQ_qAhDceERqQSgBZN3KpqhgUiWZ37gyXsEjiiGAVTBNVhzUyd68q04yQoDv3DeKajCKoHcxCEwf7N_WSdVlBKJhl2NNR18Hgn0YOJz3gK_uL-0HPwLFg_TdgTqPBOwn1KmZuzbL57QSoiYS10EUrFtOKidD3pRUg0jci0b5e9Q9GXGX2w","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"created":1596039122,"updated":1596039122,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/7ea0933149864a4b963beaf5fd82c0f3","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"jzPKCMGd6gllD7wMFu6taNREuGaiP0D3p-OgXD6OJhrSurq4e2t8QgTU6oHsWCScEOpsOJ4PgSehSULfRYUeGOOd8jXWMqzo2INHhQcF1izccfvSK2pBlAlsWm5WzVIum_plFCxXapucrttrYuzsbzwdUO0i4dPevlQNYCQAuu08qMwoQYpC78A6JZ4cKU_nBP3rvMfMoJ88sHl7xUE9CjhauOjEUfeQgSq9DjmSgHCWSYRZgHIipW3UUymlMClfCGVIqIcKfKnwV03aqb6w76mF0u7-PkS7yfx7Uf9T8xxUYdbp5qRwhE3KHWpAkOwwjaQZZU1YHvb5btUCUcfrGw","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"created":1594252583,"updated":1594252583,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' headers: cache-control: - no-cache @@ -81,7 +81,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Wed, 29 Jul 2020 16:12:01 GMT + - Wed, 08 Jul 2020 23:56:24 GMT expires: - '-1' pragma: @@ -93,61 +93,11 @@ interactions: x-content-type-options: - nosniff x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; + - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=InterNetwork; x-ms-keyvault-region: - westus x-ms-keyvault-service-version: - - 1.1.10.0 - x-powered-by: - - ASP.NET - status: - code: 200 - message: OK -- request: - body: '{"kty": "EC", "attributes": {"nbf": 32503680000}}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '49' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) - method: POST - uri: https://vaultname.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1 - response: - body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-not-yet-valid/a9488190f47643bb88b3079d546a47de","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"dbcDNoxjBhTAWa2yRE6t55WO02WswmuS62kUQaHQDvQ","y":"vKqwWNFGsMjZ_b8RM4tZBdEEIyErmB9e4buTmh9tCIU"},"attributes":{"enabled":true,"nbf":32503680000,"created":1596039122,"updated":1596039122,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' - headers: - cache-control: - - no-cache - content-length: - - '425' - content-type: - - application/json; charset=utf-8 - date: - - Wed, 29 Jul 2020 16:12:01 GMT - expires: - - '-1' - pragma: - - no-cache - strict-transport-security: - - max-age=31536000;includeSubDomains - x-aspnet-version: - - 4.0.30319 - x-content-type-options: - - nosniff - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; - x-ms-keyvault-region: - - westus - x-ms-keyvault-service-version: - - 1.1.10.0 + - 1.1.8.0 x-powered-by: - ASP.NET status: @@ -167,12 +117,12 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.19041-SP0) method: POST uri: https://vaultname.vault.azure.net/keys/rsa-expired/create?api-version=7.1 response: body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-expired/a9d9ebdcaa6e463cb81dcbac014791bf","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"xjCQJAlehYTUozcODbTjSgsfp2DzXDPwAPqgawNzJx7WSRnAqWYXtPI_oMyoSu2e3zPqQHILoHQTzRtMmt0kyKC1N6Rmxi-XPC20Tw39Lx4A-P0izw4GUgRAyWTWK4QmSwLEFLQYQS0tQtZLIiOuvHX-lvOz6joL2KFrXNnv56ENs9pZlnJ5gOznjt-eFVfZA1zo0lHjYbNtnFWhd9TuKJnXrRYJDPobg6xjoJSNPg6U_brV01fO6uOceK4sRlnmR7quItPMzwVG51y7Ysl4Y97yZsyev2NtOF56PMYIf-Ko5t5OUIMvxV__131NnRNItcX8iZny-Pap1W3pKUMWbQ","e":"AQAB"},"attributes":{"enabled":true,"exp":946684800,"created":1596039122,"updated":1596039122,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-expired/e635d043959b47919761cd2afa48d8ae","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"j6jR55imX613PlsKicrY79HVu9MptCV6uAZJzdb-z7-pGDwyxSWRx0MDDcyKEWPj7D0ccXa6GscQi3WmhgaOa6R9p6-0XzOcFqJjIguQJ1Qa4-L-yUb2DQA6nSX6-O5qmrHR0VdmbM0SZSDSktjqqTi13jMiaj41bEQcYbbqNm35j_osMk-h5UzracaVOgm-wyW4NrCVCddyEZM4fumt_e0cDBC9Ffz7XcprCFaZ3m6Jn0IzG3X-mr6EubLNAZ4hvIalWacJQo4Clh2WAIZEb-9KvbXBO6l8ufac4iosDlRtdP7BgTkj5Opx2RIgOYyAFX6Zld9hY2DzvimRopUv_w","e":"AQAB"},"attributes":{"enabled":true,"exp":946684800,"created":1594252584,"updated":1594252584,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' headers: cache-control: - no-cache @@ -181,7 +131,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Wed, 29 Jul 2020 16:12:02 GMT + - Wed, 08 Jul 2020 23:56:24 GMT expires: - '-1' pragma: @@ -193,61 +143,11 @@ interactions: x-content-type-options: - nosniff x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; + - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=InterNetwork; x-ms-keyvault-region: - westus x-ms-keyvault-service-version: - - 1.1.10.0 - x-powered-by: - - ASP.NET - status: - code: 200 - message: OK -- request: - body: '{"kty": "EC", "attributes": {"exp": 946684800}}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '47' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) - method: POST - uri: https://vaultname.vault.azure.net/keys/ec-expired/create?api-version=7.1 - response: - body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-expired/77f6482d7a364e7ebddb1faa115c7812","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"6ZzntLSyQUMZryICePHIv6eKxOo0ifP3Oza-DImTF30","y":"ovJC2ZXaTrjU6eP2k9qkLMXJoCnmlJzf0gT-9Fv1ed0"},"attributes":{"enabled":true,"exp":946684800,"created":1596039123,"updated":1596039123,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' - headers: - cache-control: - - no-cache - content-length: - - '417' - content-type: - - application/json; charset=utf-8 - date: - - Wed, 29 Jul 2020 16:12:02 GMT - expires: - - '-1' - pragma: - - no-cache - strict-transport-security: - - max-age=31536000;includeSubDomains - x-aspnet-version: - - 4.0.30319 - x-content-type-options: - - nosniff - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; - x-ms-keyvault-region: - - westus - x-ms-keyvault-service-version: - - 1.1.10.0 + - 1.1.8.0 x-powered-by: - ASP.NET status: @@ -267,12 +167,12 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.19041-SP0) method: POST uri: https://vaultname.vault.azure.net/keys/rsa-valid/create?api-version=7.1 response: body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-valid/aed3e4a2ed634e6f8a0c5a4484cf01cf","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"naE2briqe_cgB4RYvFD9qX1oSsSAL85-IJoaSbXNS6ilpOpSGSXp1dFPwAY6g3PlpFpsdCrXu8nCsf5ZFamycYi_YGkC6gxAHDq_qIzkLmlCOkuqiSuMQpREboL3XOop78Lu7rFBu7KDrM6UgyT8SnDPCs02cu_vl33zcy0wL5AomfPf1gvo8tgK1N8megrQ0qKZ1tez_cH291Coifn-6r6m9SZ7wjMsJ3Gmg1LO1u6-rSMGTydBPd3GVGdXF6ED7_HuXH0vZGIZPdNcaz6oPfhGsuA0nX3DZiCEZQF8ourQp7BV52mJ1kFs4nIgCubwx0vbEbjnhU-SG7okRPNwEQ","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1596039123,"updated":1596039123,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-valid/aa5a5a00dd6544a0b6b6fcce751f09e1","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"qRYTtMlU78wP2vgWJ8mzQUQU2iiQ9NIw9omBJUjO4IJ0sAZo0YoQ6NUv-xOUadQ6JDMQJwAUbnh2fgp8xLU9_4grrX4bNAcJ9AroTveOaDxWh1llesKO2tBPA9gusX7hJ5PMJ7HKaYNqkFxSPTzqe64I28b4ANc6RJy345GwPldofOIAg0d7jBEwgT8K4rU5smgUEkhvX3GTKaovhhQMP69EzuqdMSck6MDmdkIhfqg7u3Y779RD7TASFaj2JE83ki4mizDrVr_WfPDLtxJLbh12G1fI7yauVu6La_1MGumJLOy581tFhse7EFIbKYZR3DmAFczETUhLrQMrtRU3rQ","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1594252584,"updated":1594252584,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' headers: cache-control: - no-cache @@ -281,57 +181,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Wed, 29 Jul 2020 16:12:02 GMT - expires: - - '-1' - pragma: - - no-cache - strict-transport-security: - - max-age=31536000;includeSubDomains - x-aspnet-version: - - 4.0.30319 - x-content-type-options: - - nosniff - x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; - x-ms-keyvault-region: - - westus - x-ms-keyvault-service-version: - - 1.1.10.0 - x-powered-by: - - ASP.NET - status: - code: 200 - message: OK -- request: - body: '{"kty": "EC", "attributes": {"nbf": 32503680000, "exp": 32535216000}}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '69' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) - method: POST - uri: https://vaultname.vault.azure.net/keys/ec-valid/create?api-version=7.1 - response: - body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-valid/ef5fab0fdcd74cb4adabb6b3b8741937","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"0jeZ6wM0UwWXDhXDc-nQtXSunbURtvm8w8aBlF70t58","y":"YRoP3FMyUNScK-RyNkHu2vDdCjTD89O-vir186q-ikQ"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1596039123,"updated":1596039123,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' - headers: - cache-control: - - no-cache - content-length: - - '435' - content-type: - - application/json; charset=utf-8 - date: - - Wed, 29 Jul 2020 16:12:02 GMT + - Wed, 08 Jul 2020 23:56:24 GMT expires: - '-1' pragma: @@ -343,11 +193,11 @@ interactions: x-content-type-options: - nosniff x-ms-keyvault-network-info: - - conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; + - conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=InterNetwork; x-ms-keyvault-region: - westus x-ms-keyvault-service-version: - - 1.1.10.0 + - 1.1.8.0 x-powered-by: - ASP.NET status: 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 index 75f0df28bd17..6b51c3912e90 100644 --- 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 @@ -9,7 +9,7 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.19041-SP0) method: POST uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1 response: @@ -20,7 +20,7 @@ interactions: cache-control: no-cache content-length: '87' content-type: application/json; charset=utf-8 - date: Wed, 29 Jul 2020 16:12:43 GMT + date: Wed, 08 Jul 2020 23:56:24 GMT expires: '-1' pragma: no-cache strict-transport-security: max-age=31536000;includeSubDomains @@ -28,14 +28,14 @@ interactions: resource="https://vault.azure.net" x-aspnet-version: 4.0.30319 x-content-type-options: nosniff - x-ms-keyvault-network-info: conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=InterNetwork; x-ms-keyvault-region: westus - x-ms-keyvault-service-version: 1.1.10.0 + x-ms-keyvault-service-version: 1.1.8.0 x-powered-by: ASP.NET status: code: 401 message: Unauthorized - url: https://pemiijtsjrmvajb4aefm7sap.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1 + url: https://pb3ak4szgezegnx3inn6gur4.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview - request: body: '{"kty": "RSA", "attributes": {"nbf": 32503680000}}' headers: @@ -46,64 +46,30 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.19041-SP0) method: POST uri: https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1 response: body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/887f8910cff94b61969b9808a4790383","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"lHRf_JqnhLO-D3pNSQd7bO_vuEeZZFxihwkOtoztcEpcuWREGtSzVbM0s0EzlNegizM1tbXSTwnz_N2oUfoY1PtrDPSahUAkc8IRkha_T5Mgl5Hd2dw99n0U3Zw6CzoiWyL0flJJ6xV-gTB1JR6sizaXXSqwJbapyH2gjCgOcu1Ede5kbCdyyG2VM0FyZD_SBc-QKkIrQv9X4MSREPtJSKPhd9ab5S5o313jZ8K2IEaRpBouRRlt0EsSaO9o5lHWcoYb4lMy31YFzIp_HYL8m7wIR4HkFEiWCS00EypJHlKqWs5pQcRrk18HQQ-J_LrUI4fo3ugn8XIBWfFd44LZZw","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"created":1596039164,"updated":1596039164,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-not-yet-valid/f33f7d6a308d47dc9df59da4a2600ed9","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"t2WQni7-1EvYosrt66t5l2ksuslLKcffo2ku5IbvMlgiTVOotmCwV6ifKXKlFskOXC_RLk97dZcNYzYksGYJsbVb4uXH5rEYBerHHaJlEGIaKsYw2RM-409EKMyS8e6FHllx_Llcu07bHiNKuvoT--KQcdqflymGrdYHWFueEy8q2Cpbx1WmXNd7L0ac9kdYcvB6j0YL1UMjSKYI13PUpZRADmTEGag63VBucfsxqedVqjEyTqLxyTtwcSsYyBPen8frnlICOOp-_QG_ZNokZk6CFZ6_Rpq891AasXQK-CSakINgdfz6SJtKwecrCknDPi2rFkT7TLgDdc_a9SScsw","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"created":1594252584,"updated":1594252584,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' headers: cache-control: no-cache content-length: '715' content-type: application/json; charset=utf-8 - date: Wed, 29 Jul 2020 16:12:44 GMT + date: Wed, 08 Jul 2020 23:56:25 GMT expires: '-1' pragma: no-cache strict-transport-security: max-age=31536000;includeSubDomains x-aspnet-version: 4.0.30319 x-content-type-options: nosniff - x-ms-keyvault-network-info: conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=InterNetwork; x-ms-keyvault-region: westus - x-ms-keyvault-service-version: 1.1.10.0 + x-ms-keyvault-service-version: 1.1.8.0 x-powered-by: ASP.NET status: code: 200 message: OK - url: https://pemiijtsjrmvajb4aefm7sap.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1 -- request: - body: '{"kty": "EC", "attributes": {"nbf": 32503680000}}' - headers: - Accept: - - application/json - Content-Length: - - '49' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) - method: POST - uri: https://vaultname.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1 - response: - body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-not-yet-valid/91659fe30ad6420b8be81f61797b5b21","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"_EsN6wNkHLRqxqfIzDrooYzU32NgILylQ3bC525lD1M","y":"uJlcAWWusZiCWbKmDHgrj3z2sUQdnz9yHdmJr_jnMXY"},"attributes":{"enabled":true,"nbf":32503680000,"created":1596039164,"updated":1596039164,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' - headers: - cache-control: no-cache - content-length: '425' - content-type: application/json; charset=utf-8 - date: Wed, 29 Jul 2020 16:12:44 GMT - expires: '-1' - pragma: no-cache - strict-transport-security: max-age=31536000;includeSubDomains - x-aspnet-version: 4.0.30319 - x-content-type-options: nosniff - x-ms-keyvault-network-info: conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; - x-ms-keyvault-region: westus - x-ms-keyvault-service-version: 1.1.10.0 - x-powered-by: ASP.NET - status: - code: 200 - message: OK - url: https://pemiijtsjrmvajb4aefm7sap.vault.azure.net/keys/ec-not-yet-valid/create?api-version=7.1 + url: https://pb3ak4szgezegnx3inn6gur4.vault.azure.net/keys/rsa-not-yet-valid/create?api-version=7.1-preview - request: body: '{"kty": "RSA", "attributes": {"exp": 946684800}}' headers: @@ -114,66 +80,32 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.19041-SP0) method: POST uri: https://vaultname.vault.azure.net/keys/rsa-expired/create?api-version=7.1 response: body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-expired/4c30efab47ac44108fec8e4b530d3d16","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"wDQtd3BFo88BOqd1-miCgJKIi4UnM7mQyaDhfHJaoCe-bWCuHrjgGN4xa0-zYb9eHaXh36n_j_4gXGQJNBRNmfl0wWf3j2EHtsPrfejdQguNU8LHstEwk0tQbyFflcqppyxX_r_6eQA7YsQJ9RPgZ9PXHvkCY_AE8thMuhTs1MRy9ZDHqW0C9-EnMSnALIpqabT4UHuJOheK7uDhF9N5b_AFY3xDSS2tPiYyTWQUiX8uJzY8kNkM2wcR960GwYEvS0RJ0vVEovFtodz5vxCJehx-K3-1QZZxZta4_S_v2XkRuerBziWPyEwk94K5Un95CxoLBMDsubbJJbhwBM_y0Q","e":"AQAB"},"attributes":{"enabled":true,"exp":946684800,"created":1596039165,"updated":1596039165,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-expired/55f375b758a240c695501fd45a69e5e3","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"nvExHxIdr7he4WswsXvsi0DzsILD1LPNlC5MIwoJTNmzseCKXIB8qx_gqQDO2UpWxfMR41EI0-FseNm5hy5AF9ctESXKr5hdmhoOYUjFo0UJYFoS_QR0o4Lo-6oyoiWsKH-AkjfFY9upxS671IgCoJPV3shGvvvL1O_b4TO6gU_nSJHeVOYc8feSL-zoAs7C6jkdZju3W3AMATo22VJFrLlnaohdUQRP71cNS05hoRn6pSpBpHMUxGA9YzmiY9yWpd9rXLUv_RsFJ-lVbW8qPZLr0UFYNW0lyOCJTpsgCGxwCRvpd4F_hCphXWHIS-n1E13RW1roc8pHn4y17_qy0w","e":"AQAB"},"attributes":{"enabled":true,"exp":946684800,"created":1594252585,"updated":1594252585,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' headers: cache-control: no-cache content-length: '707' content-type: application/json; charset=utf-8 - date: Wed, 29 Jul 2020 16:12:44 GMT - expires: '-1' - pragma: no-cache - strict-transport-security: max-age=31536000;includeSubDomains - x-aspnet-version: 4.0.30319 - x-content-type-options: nosniff - x-ms-keyvault-network-info: conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; - x-ms-keyvault-region: westus - x-ms-keyvault-service-version: 1.1.10.0 - x-powered-by: ASP.NET - status: - code: 200 - message: OK - url: https://pemiijtsjrmvajb4aefm7sap.vault.azure.net/keys/rsa-expired/create?api-version=7.1 -- request: - body: '{"kty": "EC", "attributes": {"exp": 946684800}}' - headers: - Accept: - - application/json - Content-Length: - - '47' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) - method: POST - uri: https://vaultname.vault.azure.net/keys/ec-expired/create?api-version=7.1 - response: - body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-expired/87a8d53eff1948f1bcbcf108c4598a32","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"DkYr0uvQcqJ51n6W1opTvNlQGit92-ilmw2K9Hu1tPk","y":"Yo4_cJt67ps5yxy95zDBE3p3TnJ2RaMbmWUPEwWgSQg"},"attributes":{"enabled":true,"exp":946684800,"created":1596039165,"updated":1596039165,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' - headers: - cache-control: no-cache - content-length: '417' - content-type: application/json; charset=utf-8 - date: Wed, 29 Jul 2020 16:12:44 GMT + date: Wed, 08 Jul 2020 23:56:25 GMT expires: '-1' pragma: no-cache strict-transport-security: max-age=31536000;includeSubDomains x-aspnet-version: 4.0.30319 x-content-type-options: nosniff - x-ms-keyvault-network-info: conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=InterNetwork; x-ms-keyvault-region: westus - x-ms-keyvault-service-version: 1.1.10.0 + x-ms-keyvault-service-version: 1.1.8.0 x-powered-by: ASP.NET status: code: 200 message: OK - url: https://pemiijtsjrmvajb4aefm7sap.vault.azure.net/keys/ec-expired/create?api-version=7.1 + url: https://pb3ak4szgezegnx3inn6gur4.vault.azure.net/keys/rsa-expired/create?api-version=7.1-preview - request: - body: '{"kty": "RSA", "attributes": {"nbf": 32503680000, "exp": 32535216000}}' + body: '{"attributes": {"nbf": 32503680000, "exp": 32535216000}, "kty": "RSA"}' headers: Accept: - application/json @@ -182,62 +114,28 @@ interactions: Content-Type: - application/json User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) + - azsdk-python-keyvault-keys/4.2.0b2 Python/3.5.4 (Windows-10-10.0.19041-SP0) method: POST uri: https://vaultname.vault.azure.net/keys/rsa-valid/create?api-version=7.1 response: body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-valid/0145342e9c98451c9a61c19bf88c6124","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"2FF3_H3_pl5AyZi8veVNoTfyHUrNMTVAOTEzOPorxoslTq5XA3S0uQtkQKdKCCHJLovHYqzatrpBAPmWAge-9vDeH7hV4tQ03JStfZQis7NfyubPBoOGMJaafyj6Ed8Jwh-Ybqvfyq3qFbvqcyCmdAZq0onP0Xzw6noNLQA_lib3L1hzJ7NYjt9DdU9FORxFWQMj2orkCQPkYB0FtXPu8QGOk8yZ12Jh9hlBxDzC-rAKprLn256-qKFn079M9y0qK4_eSb7LDKIiwDkW5Bs51_5M11jdhTmT54k8fADYRErEBGAH9PnaqgfZoOo2ldpvjVsx_4ZhlvooiVvVikgcaQ","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1596039165,"updated":1596039165,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/rsa-valid/c811f6e9c6ce4790a30d8f49efa12521","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"oiDcjX5DwrZqApkOXkz5JA8IPxWolhwaKa6JdjZm-GSE5JdVV3nFk98sR1fPJS757mfuNE7P6JF5BDUvDWWsolRZpfuQtqLoUCtVZvklatx3BkOMhUrCKe6zPRnSaamqt015SCNWMtCT7pBzVCzWkl8B2g2CQkx2zdX4vWrRHpiHOHw4Gc8quvx9w6ZKV7Dae9cGC39N_2_KAwnvuo6wKF9mPKH1Y-_KgmPC7BZFt5Zmh_iwq8wtEgthMS5WSmuF32DxZCey28vyeKDvGVKvsW2UsDGnvekvOQK2FxHsNfjkO8JaodALZXFpxlw3QIs8vvAnwyJMxqS2KglABPkl4w","e":"AQAB"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1594252585,"updated":1594252585,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' headers: cache-control: no-cache content-length: '725' content-type: application/json; charset=utf-8 - date: Wed, 29 Jul 2020 16:12:45 GMT - expires: '-1' - pragma: no-cache - strict-transport-security: max-age=31536000;includeSubDomains - x-aspnet-version: 4.0.30319 - x-content-type-options: nosniff - x-ms-keyvault-network-info: conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; - x-ms-keyvault-region: westus - x-ms-keyvault-service-version: 1.1.10.0 - x-powered-by: ASP.NET - status: - code: 200 - message: OK - url: https://pemiijtsjrmvajb4aefm7sap.vault.azure.net/keys/rsa-valid/create?api-version=7.1 -- request: - body: '{"kty": "EC", "attributes": {"nbf": 32503680000, "exp": 32535216000}}' - headers: - Accept: - - application/json - Content-Length: - - '69' - Content-Type: - - application/json - User-Agent: - - azsdk-python-keyvault-keys/4.2.0b2 Python/3.7.7 (Darwin-17.7.0-x86_64-i386-64bit) - method: POST - uri: https://vaultname.vault.azure.net/keys/ec-valid/create?api-version=7.1 - response: - body: - string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/ec-valid/dff4e7caf0534caaa4403b09d803b745","kty":"EC","key_ops":["sign","verify"],"crv":"P-256","x":"mfupesvKGlPUhPQVocNTkJ31JABm5sK4hk7khF93aEQ","y":"MzDC4LP4G2sOkLRxa1BgPJiLm9qKiCSJPaoSDWuutbc"},"attributes":{"enabled":true,"nbf":32503680000,"exp":32535216000,"created":1596039165,"updated":1596039165,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' - headers: - cache-control: no-cache - content-length: '435' - content-type: application/json; charset=utf-8 - date: Wed, 29 Jul 2020 16:12:45 GMT + date: Wed, 08 Jul 2020 23:56:25 GMT expires: '-1' pragma: no-cache strict-transport-security: max-age=31536000;includeSubDomains x-aspnet-version: 4.0.30319 x-content-type-options: nosniff - x-ms-keyvault-network-info: conn_type=Ipv4;addr=73.135.72.237;act_addr_fam=InterNetwork; + x-ms-keyvault-network-info: conn_type=Ipv4;addr=24.17.201.78;act_addr_fam=InterNetwork; x-ms-keyvault-region: westus - x-ms-keyvault-service-version: 1.1.10.0 + x-ms-keyvault-service-version: 1.1.8.0 x-powered-by: ASP.NET status: code: 200 message: OK - url: https://pemiijtsjrmvajb4aefm7sap.vault.azure.net/keys/ec-valid/create?api-version=7.1 + url: https://pb3ak4szgezegnx3inn6gur4.vault.azure.net/keys/rsa-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 74862772d9cd..94addf01a0af 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_crypto_client.py @@ -254,38 +254,20 @@ def test_operations(key, expected_error_substrings, encrypt_algorithms, wrap_alg 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] - ) + not_yet_valid_key = key_client.create_rsa_key("rsa-not-yet-valid", not_before=the_year_3000) + test_operations(not_yet_valid_key, [str(the_year_3000)], EncryptionAlgorithm, rsa_wrap_algorithms) # 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] - ) + expired_key = key_client.create_rsa_key("rsa-expired", expires_on=the_year_2000) + test_operations(expired_key, [str(the_year_2000)], EncryptionAlgorithm, rsa_wrap_algorithms) # 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], - ) + valid_key = key_client.create_rsa_key("rsa-valid", not_before=the_year_3000, expires_on=the_year_3001) + test_operations(valid_key, (str(the_year_3000), str(the_year_3001)), EncryptionAlgorithm, rsa_wrap_algorithms) class _CustomHookPolicy(object): pass 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 15f9a0847da0..644e3ad90515 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 @@ -238,39 +238,21 @@ async def test_operations(key, expected_error_substrings, encrypt_algorithms, wr 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] - ) + not_yet_valid_key = await key_client.create_rsa_key("rsa-not-yet-valid", not_before=the_year_3000) + await test_operations(not_yet_valid_key, [str(the_year_3000)], EncryptionAlgorithm, rsa_wrap_algorithms) # 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] - ) + expired_key = await key_client.create_rsa_key("rsa-expired", expires_on=the_year_2000) + await test_operations(expired_key, [str(the_year_2000)], EncryptionAlgorithm, rsa_wrap_algorithms) # 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) + valid_key = await key_client.create_rsa_key("rsa-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], + valid_key, (str(the_year_3000), str(the_year_3001)), EncryptionAlgorithm, rsa_wrap_algorithms ) class _CustomHookPolicy(object): diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_local_crypto.py b/sdk/keyvault/azure-keyvault-keys/tests/test_local_crypto.py new file mode 100644 index 000000000000..22756d7f8405 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_local_crypto.py @@ -0,0 +1,172 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import hashlib +import os + +from azure.keyvault.keys import KeyCurveName, KeyVaultKey +from azure.keyvault.keys.crypto import EncryptionAlgorithm, KeyWrapAlgorithm, SignatureAlgorithm +from azure.keyvault.keys.crypto._providers import get_local_cryptography_provider +import pytest + +from keys import EC_KEYS, RSA_KEYS + + +@pytest.mark.parametrize( + "key,algorithm,hash_function", + ( + (EC_KEYS[KeyCurveName.p_256], SignatureAlgorithm.es256, hashlib.sha256), + (EC_KEYS[KeyCurveName.p_256_k], SignatureAlgorithm.es256_k, hashlib.sha256), + (EC_KEYS[KeyCurveName.p_384], SignatureAlgorithm.es384, hashlib.sha384), + (EC_KEYS[KeyCurveName.p_521], SignatureAlgorithm.es512, hashlib.sha512), + ), +) +def test_ec_sign_verify(key, algorithm, hash_function): + provider = get_local_cryptography_provider(key) + digest = hash_function(b"message").digest() + sign_result = provider.sign(algorithm, digest) + verify_result = provider.verify(sign_result.algorithm, digest, sign_result.signature) + assert verify_result.is_valid + + +@pytest.mark.parametrize("key", RSA_KEYS.values()) +@pytest.mark.parametrize("algorithm", (a for a in EncryptionAlgorithm if a.startswith("RSA"))) +def test_rsa_encrypt_decrypt(key, algorithm): + provider = get_local_cryptography_provider(key) + plaintext = b"plaintext" + encrypt_result = provider.encrypt(algorithm, plaintext) + decrypt_result = provider.decrypt(encrypt_result.algorithm, encrypt_result.ciphertext) + assert decrypt_result.plaintext == plaintext + + +@pytest.mark.parametrize("key", RSA_KEYS.values()) +@pytest.mark.parametrize( + "algorithm,hash_function", + ( + ( + (SignatureAlgorithm.ps256, hashlib.sha256), + (SignatureAlgorithm.ps384, hashlib.sha384), + (SignatureAlgorithm.ps512, hashlib.sha512), + (SignatureAlgorithm.rs256, hashlib.sha256), + (SignatureAlgorithm.rs384, hashlib.sha384), + (SignatureAlgorithm.rs512, hashlib.sha512), + ) + ), +) +def test_rsa_sign_verify(key, algorithm, hash_function): + message = b"message" + provider = get_local_cryptography_provider(key) + digest = hash_function(message).digest() + sign_result = provider.sign(algorithm, digest) + verify_result = provider.verify(sign_result.algorithm, digest, sign_result.signature) + assert verify_result.is_valid + + +@pytest.mark.parametrize("key", RSA_KEYS.values()) +@pytest.mark.parametrize("algorithm", (a for a in KeyWrapAlgorithm if a.startswith("RSA"))) +def test_rsa_wrap_unwrap(key, algorithm): + plaintext = b"arbitrary bytes" + provider = get_local_cryptography_provider(key) + + wrap_result = provider.wrap_key(algorithm, plaintext) + assert wrap_result.key_id == key.id + + unwrap_result = provider.unwrap_key(wrap_result.algorithm, wrap_result.encrypted_key) + assert unwrap_result.key == plaintext + + +def test_symmetric_wrap_unwrap(): + jwk = {"k": os.urandom(32), "kty": "oct", "key_ops": ("unwrapKey", "wrapKey")} + key = KeyVaultKey(key_id="http://localhost/keys/key/version", jwk=jwk) + provider = get_local_cryptography_provider(key) + key_bytes = os.urandom(32) + + wrap_result = provider.wrap_key(KeyWrapAlgorithm.aes_256, key_bytes) + assert wrap_result.key_id == key.id + + unwrap_result = provider.unwrap_key(wrap_result.algorithm, wrap_result.encrypted_key) + assert unwrap_result.key == key_bytes + + +@pytest.mark.parametrize("key", RSA_KEYS.values()) +@pytest.mark.parametrize( + "algorithm", + [a for a in KeyWrapAlgorithm if not a.startswith("RSA")] + [a for a in SignatureAlgorithm if a.startswith("ES")], +) +def test_unsupported_rsa_operations(key, algorithm): + """The crypto provider should raise NotImplementedError when a key doesn't support an operation or algorithm""" + + provider = get_local_cryptography_provider(key) + if isinstance(algorithm, EncryptionAlgorithm): + with pytest.raises(NotImplementedError): + provider.encrypt(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.decrypt(algorithm, b"...") + if isinstance(algorithm, KeyWrapAlgorithm): + with pytest.raises(NotImplementedError): + provider.wrap_key(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.unwrap_key(algorithm, b"...") + elif isinstance(algorithm, SignatureAlgorithm): + with pytest.raises(NotImplementedError): + provider.sign(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.verify(algorithm, b"...", b"...") + + +@pytest.mark.parametrize("key", EC_KEYS.values()) +@pytest.mark.parametrize( + "algorithm", + [a for a in KeyWrapAlgorithm if a.startswith("RSA")] + + [a for a in SignatureAlgorithm if not a.startswith("ES")] + + list(EncryptionAlgorithm), +) +def test_unsupported_ec_operations(key, algorithm): + """The crypto provider should raise NotImplementedError when a key doesn't support an operation or algorithm""" + + provider = get_local_cryptography_provider(key) + if isinstance(algorithm, EncryptionAlgorithm): + with pytest.raises(NotImplementedError): + provider.encrypt(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.decrypt(algorithm, b"...") + if isinstance(algorithm, KeyWrapAlgorithm): + with pytest.raises(NotImplementedError): + provider.wrap_key(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.unwrap_key(algorithm, b"...") + elif isinstance(algorithm, SignatureAlgorithm): + with pytest.raises(NotImplementedError): + provider.sign(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.verify(algorithm, b"...", b"...") + + +@pytest.mark.parametrize( + "algorithm", + [a for a in KeyWrapAlgorithm if a != KeyWrapAlgorithm.aes_256] + + list(SignatureAlgorithm) + + list(EncryptionAlgorithm), +) +def test_unsupported_symmetric_operations(algorithm): + """The crypto provider should raise NotImplementedError when a key doesn't support an operation or algorithm""" + + jwk = {"k": os.urandom(32), "kty": "oct", "key_ops": ("unwrapKey", "wrapKey")} + key = KeyVaultKey(key_id="http://localhost/keys/key/version", jwk=jwk) + provider = get_local_cryptography_provider(key) + if isinstance(algorithm, EncryptionAlgorithm): + with pytest.raises(NotImplementedError): + provider.encrypt(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.decrypt(algorithm, b"...") + if isinstance(algorithm, KeyWrapAlgorithm): + with pytest.raises(NotImplementedError): + provider.wrap_key(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.unwrap_key(algorithm, b"...") + elif isinstance(algorithm, SignatureAlgorithm): + with pytest.raises(NotImplementedError): + provider.sign(algorithm, b"...") + with pytest.raises(NotImplementedError): + provider.verify(algorithm, b"...", b"...")