def test_prefers_local_provider():
    """The client should complete operations locally whenever possible"""

    mock_client = mock.Mock()
    key = mock.Mock(
        spec=KeyVaultKey,
        id="https://localhost/fake/key/version",
        properties=mock.Mock(not_before=datetime(2000, 1, 1, tzinfo=_UTC),
                             expires_on=datetime(3000, 1, 1, tzinfo=_UTC)),
    )
    client = CryptographyClient(key, mock.Mock())
    client._client = mock_client

    supports_everything = mock.Mock(supports=mock.Mock(return_value=True))
    with mock.patch(
            CryptographyClient.__module__ + ".get_local_cryptography_provider",
            lambda *_: supports_everything):
        client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...")
    assert mock_client.decrypt.call_count == 0
    assert supports_everything.decrypt.call_count == 1

    client.encrypt(EncryptionAlgorithm.rsa_oaep, b"...")
    assert mock_client.encrypt.call_count == 0
    assert supports_everything.encrypt.call_count == 1

    client.sign(SignatureAlgorithm.rs256, b"...")
    assert mock_client.sign.call_count == 0
    assert supports_everything.sign.call_count == 1

    client.verify(SignatureAlgorithm.rs256, b"...", b"...")
    assert mock_client.verify.call_count == 0
    assert supports_everything.verify.call_count == 1

    client.unwrap_key(KeyWrapAlgorithm.rsa_oaep, b"...")
    assert mock_client.unwrap_key.call_count == 0
    assert supports_everything.unwrap_key.call_count == 1

    client.wrap_key(KeyWrapAlgorithm.rsa_oaep, b"...")
    assert mock_client.wrap_key.call_count == 0
    assert supports_everything.wrap_key.call_count == 1
Beispiel #2
0
    def test_encrypt_and_decrypt(self, key_client, credential, **kwargs):
        key_name = self.get_resource_name("keycrypt")

        imported_key = self._import_test_key(key_client, key_name)
        crypto_client = CryptographyClient(imported_key.id, credential)

        result = crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep,
                                       self.plaintext)
        self.assertEqual(result.key_id, imported_key.id)

        result = crypto_client.decrypt(result.algorithm, result.ciphertext)
        self.assertEqual(result.key_id, imported_key.id)
        self.assertEqual(EncryptionAlgorithm.rsa_oaep, result.algorithm)
        self.assertEqual(self.plaintext, result.plaintext)
Beispiel #3
0
def encrypt(request, json_key_attributes_dict, pin, version):
    """
    This method will be called by the application entry point
    for encrypting the payload.
    request.value has the plaintext payload
    request.alg contains the padding algorithm for encryption.
    """
    set_env(json_key_attributes_dict, pin)
    credential = DefaultAzureCredential()
    key_vault_key = get_akv_key(json_key_attributes_dict, credential)
    crypto_client = CryptographyClient(key_vault_key, credential=credential)
    encrypted_payload = crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep,
                                              request.value)
    response = EncryptDecryptResponse(encrypted_payload.ciphertext)
    return response
 def encrypt_secret(self, secret, vault_name, keyid, subscription_id,
                    region):
     logger.info("secret to encrypt: {0}".format(secret))
     logger.info("keyid: {0}".format(keyid))
     logger.info("Azure subscription id: {0}".format(subscription_id))
     logger.info("Azure region: {0}".format(region))
     # https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/identity/azure-identity#defaultazurecredential
     vault_url = "https://{0}.vault.azure.net/".format(vault_name)
     logger.info("vault url: ".format(vault_url))
     byte_literal_value = secret.encode()  # convert string to byte literal
     credential = DefaultAzureCredential()
     key_client = KeyClient(vault_url=vault_url, credential=credential)
     key = key_client.get_key(keyid)
     crypto_client = CryptographyClient(key, credential=credential)
     # the result holds the ciphertext and identifies the encryption key and algorithm used
     result = crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep,
                                    byte_literal_value)
     ciphertext = result.ciphertext
     print("-" * 50)
     print("ciphertext: {0}".format(ciphertext))
     print("result: {0}".format(result.key_id))
     print(result.algorithm)
     print("-" * 50)
def test_encrypt_argument_validation():
    """The client should raise an error when arguments don't work with the specified algorithm"""

    mock_client = mock.Mock()
    key = mock.Mock(
        spec=KeyVaultKey,
        id="https://localhost/fake/key/version",
        properties=mock.Mock(not_before=datetime(2000, 1, 1, tzinfo=_UTC),
                             expires_on=datetime(3000, 1, 1, tzinfo=_UTC)),
    )
    client = CryptographyClient(key, mock.Mock())
    client._client = mock_client

    with pytest.raises(ValueError) as ex:
        client.encrypt(EncryptionAlgorithm.rsa_oaep, b"...", iv=b"...")
    assert "iv" in str(ex.value)
    with pytest.raises(ValueError) as ex:
        client.encrypt(EncryptionAlgorithm.rsa_oaep,
                       b"...",
                       additional_authenticated_data=b"...")
    assert "additional_authenticated_data" in str(ex.value)
    with pytest.raises(ValueError) as ex:
        client.encrypt(EncryptionAlgorithm.a256_cbc, b"...")
    assert "iv" in str(ex.value) and "required" in str(ex.value)
Beispiel #6
0
class AzureEncryptionProvider(EncryptionProvider):
    """An EncryptionProvider implementation for Azure Key Vault.

    To authenticate, please provide Azure AD service principal info as 
    'tenant_id', 'client_id', and 'client_secret' kwargs, or as
    AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET environment
    variables.

    Attributes:
        tenant_id (str): The tenant ID of the Azure SP to connect with.
        client_id (str): The client ID of the Azure SP to connect with.
        client_secret (str): The client secret of the Azure SP to connect with.
        key_client (KeyClient): Azure Key Vault key client.
        crypto_client (CryptographyClient): Azure Key Vault crypto client.

    Args:
        vault_url (str): The URL of the key vault to connect to.
        key (str): The name of the key encryption key to use for envelope encryption.
        auth_via_cli (bool, kwarg): If we should auth via Azure CLI.
        **kwargs: Authentication information.

    Raises:
        TypeError: If authentication information is not provided correctly.
    """
    def __init__(self, vault_url: str, key: str, **kwargs) -> None:
        auth_via_cli = bool(kwargs.pop("auth_via_cli", False))
        if auth_via_cli:
            try:
                self.key_client = get_client_from_cli_profile(
                    KeyClient, vault_url=vault_url)
                self.key_encryption_key = self.key_client.get_key(key)
                self.crypto_client = get_client_from_cli_profile(
                    CryptographyClient, key=self.key_encryption_key)
            except CLIError:
                logging.error(
                    "ERROR: Unable to authenticate via Azure CLI, have you "
                    "logged in with 'az login'?")
                raise SystemExit(1)
        else:
            tenant_id = kwargs.pop("tenant_id", os.getenv(TENANT_ID_ENVVAR))
            client_id = kwargs.pop("client_id", os.getenv(CLIENT_ID_ENVVAR))
            client_secret = kwargs.pop("client_secret",
                                       os.getenv(CLIENT_SECRET_ENVVAR))
            if tenant_id is None or client_id is None or client_secret is None:
                raise TypeError(
                    "Please specify tenant_id, client_id, and client_secret "
                    "in config or in environment variables as in "
                    "https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/identity/azure-identity#service-principal-with-secret"
                )
            self.cred = ClientSecretCredential(tenant_id, client_id,
                                               client_secret)
            self.key_client = KeyClient(vault_url,
                                        credential=self.cred,
                                        logger=None)
            self.key_encryption_key = self.key_client.get_key(key)
            self.crypto_client = CryptographyClient(self.key_encryption_key,
                                                    self.cred)

    # overrides EncryptionProvider.encrypt()
    def encrypt(self, data: bytes) -> EncryptionEnvelope:
        # encrypt the data locally, generating a data key and a nonce
        ciphertext, data_key, nonce = self._data_encrypt(data)

        # encrypt the data key using the key from the vault
        result = self.crypto_client.encrypt(ENCRYPTION_ALGORITHM, data_key)
        del data_key  # we don't wanna keep this around after we've encrypted it
        encrypted_data_key = result.ciphertext

        # encode to base64 for storage/transmission
        b64_ciphertext = base64.b64encode(ciphertext).decode("utf-8")
        b64_encrypted_data_key = base64.b64encode(encrypted_data_key).decode(
            "utf-8")
        b64_nonce = base64.b64encode(nonce).decode("utf-8")

        return EncryptionEnvelope(b64_ciphertext, b64_encrypted_data_key,
                                  b64_nonce,
                                  self.key_encryption_key.properties.version)

    # overrides EncryptionProvider.decrypt()
    def decrypt(self, envelope: EncryptionEnvelope) -> Union[bytes, None]:
        if envelope.version != self.key_encryption_key.properties.version:
            logging.error(
                "Encryption key version %s is out of "
                "date, please re-encrypt with 'victoria encrypt rotate'",
                envelope.version)
            return None

        # decode from base64
        ciphertext = base64.b64decode(envelope.data)
        encrypted_data_key = base64.b64decode(envelope.key)
        nonce = base64.b64decode(envelope.iv)

        # decrypt the data key
        result = self.crypto_client.decrypt(ENCRYPTION_ALGORITHM,
                                            encrypted_data_key)
        data_key = result.plaintext

        # decrypt the data locally using the nonce and decrypted data key
        return self._data_decrypt(ciphertext, data_key, nonce)

    # overrides EncryptionProvider.rotate_key()
    def rotate_key(self,
                   envelope: EncryptionEnvelope,
                   version: Optional[str] = None) -> EncryptionEnvelope:
        old_key = self.key_client.get_key(self.key_encryption_key.name,
                                          version=envelope.version)
        old_crypto_client = CryptographyClient(old_key, self.cred)

        # decode from base64
        ciphertext = base64.b64decode(envelope.data)
        encrypted_data_key = base64.b64decode(envelope.key)
        nonce = base64.b64decode(envelope.iv)

        # decrypt the data key
        result = old_crypto_client.decrypt(ENCRYPTION_ALGORITHM,
                                           encrypted_data_key)
        data_key = result.plaintext

        # decrypt the data locally using the nonce and decrypted data key
        decrypted_data = self._data_decrypt(ciphertext, data_key, nonce)

        # now re-encrypt with the latest key encryption key
        return self.encrypt(decrypted_data)
Beispiel #7
0
 def encrypt(self, key_name, plaintext):
     key = self.key_client.get_key(key_name)
     crypto_client = CryptographyClient(key, credential=self.credential)
     text = crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep,
                                  bytes(plaintext.encode()))
     return text.ciphertext