Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changes/nextrelease/s3-encryption-client.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"type": "feature",
"category": "S3",
"description": "A new `S3EncryptionClient` implementation and a new `KmsMaterialProvider` implementation. `S3EncryptionClientV3` now supports writing and reading objects with Key Commitment. `KmsMaterialProviderV3` now supports verifying supplied encryption context on `decryptCek` calls."
}
]
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Jump To:
* [Bug Reports](#Bug-Reports)
* [Feature Requests](#Feature-Requests)
* [Code Contributions](#Code-Contributions)
* [Security issue notifications](#Security-Issue-Notifications)

## How to contribute

Expand Down Expand Up @@ -114,6 +115,9 @@ we ask the same of all community contributions as well:
9. If you are working on the SDK, make sure to check out the `Makefile` for some
of the common tasks that we have to do.

## Security issue notifications
If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.

### Changelog Documents

A changelog document is a small JSON blob placed in the `.changes/nextrelease`
Expand Down
3 changes: 3 additions & 0 deletions behat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ default:
s3EncryptionV2:
paths: [ "%paths.base%/features/s3EncryptionV2" ]
contexts: [ Aws\Test\Integ\S3EncryptionContextV2 ]
s3EncryptionV3:
paths: [ "%paths.base%/features/s3EncryptionV3" ]
contexts: [ Aws\Test\Integ\S3EncryptionContextV3 ]
76 changes: 76 additions & 0 deletions features/s3EncryptionV3/encryptionV3.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@s3EncryptionV3 @integ @requiresUniqueResources
Feature: S3 Client Side Encryption V3

Scenario: Upload PHP V3 GCM encrypted fixtures with key commitment
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I encrypt each fixture with "kms" "AWS_SDK_PHP_TEST_ALIAS" "us-west-2" and "aes_gcm" using V3 with commitment policy "REQUIRE_ENCRYPT_REQUIRE_DECRYPT"
And upload "PHP" data with folder "version_3"

Scenario: Get all PHP V3 plaintext fixtures for kms keyed aes gcm with key commitment and commitment policy "REQUIRE_ENCRYPT_REQUIRE_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "PHP" "version_3" using V3 client with security profile "V3" with commitment policy "REQUIRE_ENCRYPT_REQUIRE_DECRYPT"
And I compare the decrypted ciphertext to the plaintext

Scenario: Get all PHP V3 plaintext fixtures for kms keyed aes gcm with key commitment and commitment policy "REQUIRE_ENCRYPT_ALLOW_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "PHP" "version_3" using V3 client with security profile "V3" with commitment policy "REQUIRE_ENCRYPT_ALLOW_DECRYPT"
And I compare the decrypted ciphertext to the plaintext

Scenario: Get all PHP V3 plaintext fixtures for kms keyed aes gcm with key commitment and commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "PHP" "version_3" using V3 client with security profile "V3" with commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
And I compare the decrypted ciphertext to the plaintext

Scenario: Get all PHP V3 plaintext fixtures for kms keyed aes cbc
When I get all fixtures for "aes_cbc" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "PHP" "version_3" using V3 client with security profile "V3_AND_LEGACY" and commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
And I compare the decrypted ciphertext to the plaintext

Scenario: Cross-language compatibility - V3 decrypts Go V2 objects with legacy profile and commitment policy "REQUIRE_ENCRYPT_ALLOW_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "Go" "version_2" using V3 client with security profile "V3_AND_LEGACY" and commitment policy "REQUIRE_ENCRYPT_ALLOW_DECRYPT"
And I compare the decrypted ciphertext to the plaintext

Scenario: Cross-language compatibility - V3 decrypts Go V2 objects with legacy profile and commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "Go" "version_2" using V3 client with security profile "V3_AND_LEGACY" and commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
And I compare the decrypted ciphertext to the plaintext

Scenario: Cross-language compatibility - V3 decrypts Java V2 objects with legacy profile and commitment policy "REQUIRE_ENCRYPT_ALLOW_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "Java" "version_2" using V3 client with security profile "V3_AND_LEGACY" and commitment policy "REQUIRE_ENCRYPT_ALLOW_DECRYPT"
And I compare the decrypted ciphertext to the plaintext

Scenario: Cross-language compatibility - V3 decrypts Java V2 objects with legacy profile and commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "Java" "version_2" using V3 client with security profile "V3_AND_LEGACY" and commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
And I compare the decrypted ciphertext to the plaintext

Scenario: Key commitment validation - V3 encrypts with commitment policy REQUIRE_ENCRYPT_REQUIRE_DECRYPT
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I encrypt each fixture with "kms" "AWS_SDK_PHP_TEST_ALIAS" "us-west-2" and "aes_gcm" using V3 with commitment policy "REQUIRE_ENCRYPT_REQUIRE_DECRYPT"
And I verify key commitment is present in metadata
And upload "PHP" data with folder "version_3_commitment"

Scenario: Key commitment validation - V3 decrypts objects with valid commitment policy "REQUIRE_ENCRYPT_REQUIRE_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "PHP" "version_3_commitment" using V3 client with security profile "V3" with commitment policy "REQUIRE_ENCRYPT_REQUIRE_DECRYPT"
And I verify key commitment validation passes
And I compare the decrypted ciphertext to the plaintext

Scenario: Key commitment validation - V3 decrypts objects with valid commitment policy "REQUIRE_ENCRYPT_ALLOW_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "PHP" "version_3_commitment" using V3 client with security profile "V3" with commitment policy "REQUIRE_ENCRYPT_ALLOW_DECRYPT"
And I verify key commitment validation passes
And I compare the decrypted ciphertext to the plaintext

Scenario: Key commitment validation - V3 decrypts objects with valid commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "PHP" "version_3_commitment" using V3 client with security profile "V3" with commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT"
And I verify key commitment validation passes
And I compare the decrypted ciphertext to the plaintext

Scenario: Commitment policy FORBID_ENCRYPT_ALLOW_DECRYPT - V3 decrypts V2 objects
When I get all fixtures for "aes_gcm" from "aws-sdk-php-crypto-tests"
Then I decrypt each fixture against "PHP" "version_2" using V3 client with commitment policy "FORBID_ENCRYPT_ALLOW_DECRYPT" and security profile "V3_AND_LEGACY"
And I compare the decrypted ciphertext to the plaintext
16 changes: 16 additions & 0 deletions src/Crypto/AbstractCryptoClientV2.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
*/
abstract class AbstractCryptoClientV2
{
const KEY_COMMITMENT_POLICIES = [
'FORBID_ENCRYPT_ALLOW_DECRYPT'
];

public static $supportedCiphers = ['gcm'];

public static $supportedKeyWraps = [
Expand All @@ -19,6 +23,18 @@ abstract class AbstractCryptoClientV2

public static $legacySecurityProfiles = ['V2_AND_LEGACY'];

/**
* Returns if the passed policy name is supported for encryption by the SDK.
*
* @param string $policy The name of a key commitment policy to verify is registered.
*
* @return bool If the key commitment policy passed is in our supported list.
*/
public static function isSupportedKeyCommitmentPolicy(string $policy): bool
{
return in_array($policy, self::KEY_COMMITMENT_POLICIES, strict: true);
}

/**
* Returns if the passed cipher name is supported for encryption by the SDK.
*
Expand Down
151 changes: 151 additions & 0 deletions src/Crypto/AbstractCryptoClientV3.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace Aws\Crypto;

use Aws\Crypto\Cipher\CipherMethod;
use GuzzleHttp\Psr7\AppendStream;
use GuzzleHttp\Psr7\Stream;

/**
* @internal
*/
abstract class AbstractCryptoClientV3
{
const SUPPORTED_SECURITY_PROFILES = ['V3', 'V3_AND_LEGACY'];

const LEGACY_SECURITY_PROFILES = ['V3_AND_LEGACY'];

const KEY_COMMITMENT_POLICIES = [
'FORBID_ENCRYPT_ALLOW_DECRYPT',
'REQUIRE_ENCRYPT_ALLOW_DECRYPT',
'REQUIRE_ENCRYPT_REQUIRE_DECRYPT'
];

public static array $supportedCiphers = ['gcm'];

public static array $supportedKeyWraps = [
KmsMaterialsProviderV3::WRAP_ALGORITHM_NAME
];

/**
* Returns if the passed policy name is supported for encryption by the SDK.
*
* @param string $policy The name of a key commitment policy to verify is registered.
*
* @return bool If the key commitment policy passed is in our supported list.
*/
public static function isSupportedKeyCommitmentPolicy(string $policy): bool
{
return in_array($policy, AbstractCryptoClientV3::KEY_COMMITMENT_POLICIES, strict: true);
}

/**
* Returns if the passed cipher name is supported for encryption by the SDK.
*
* @param string $cipherName The name of a cipher to verify is registered.
*
* @return bool If the cipher passed is in our supported list.
*/
public static function isSupportedCipher(string $cipherName): bool
{
return in_array($cipherName, self::$supportedCiphers, true);
}

/**
* Returns an identifier recognizable by `openssl_*` functions, such as
* `aes-256-gcm`
*
* @param string $cipherName Name of the cipher being used for encrypting
* or decrypting.
* @param int $keySize Size of the encryption key, in bits, that will be
* used.
*
* @return string
*/
abstract protected function getCipherOpenSslName(
$cipherName,
$keySize
);

/**
* Constructs a CipherMethod for the given name, initialized with the other
* data passed for use in encrypting or decrypting.
*
* @param string $cipherName Name of the cipher to generate for encrypting.
* @param string $iv Base Initialization Vector for the cipher.
* @param int $keySize Size of the encryption key, in bits, that will be
* used.
*
* @return CipherMethod
*
* @internal
*/
abstract protected function buildCipherMethod(
$cipherName,
$iv,
$keySize
);

/**
* Performs a reverse lookup to get the openssl_* cipher name from the
* AESName passed in from the MetadataEnvelope.
*
* @param string $aesName
*
* @return string
*
* @internal
*/
abstract protected function getCipherFromAesName($aesName);

/**
* Dependency to provide an interface for building an encryption stream for
* data given cipher details, metadata, and materials to do so.
*
* @param Stream $plaintext Plain-text data to be encrypted using the
* materials, algorithm, and data provided.
* @param AlgorithmSuite $algorithmSuite AlgorithmSuite for use in encryption.
* @param array $options Options for use in encryption, including cipher
* options, and encryption context.
* @param MaterialsProviderV3 $provider A provider to supply and encrypt
* materials used in encryption.
* @param MetadataEnvelope $envelope A storage envelope for encryption
* metadata to be added to.
*
* @return AppendStream
*
* @internal
*/
abstract public function encrypt(
Stream $plaintext,
AlgorithmSuite $algorithmSuite,
array $options,
MaterialsProviderV3 $provider,
MetadataEnvelope $envelope
): AppendStream;

/**
* Dependency to provide an interface for building a decryption stream for
* cipher text given metadata and materials to do so.
*
* @param string $cipherText Plain-text data to be decrypted using the
* materials, algorithm, and data provided.
* @param MaterialsProviderInterface $provider A provider to supply and encrypt
* materials used in encryption.
* @param MetadataEnvelope $envelope A storage envelope for encryption
* metadata to be read from.
* @param string $commitmentPolicy Commitment Policy to use for decrypting objects.
* @param array $options Options used for decryption.
*
* @return AesStreamInterface
*
* @internal
*/
abstract public function decrypt(
string $cipherText,
MaterialsProviderInterfaceV3 $provider,
MetadataEnvelope $envelope,
string $commitmentPolicy,
array $options = []
): AesStreamInterface;
}
26 changes: 26 additions & 0 deletions src/Crypto/AlgorithmConstants.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
namespace Aws\Crypto;

class AlgorithmConstants
{
/**
* The maximum number of 16-byte blocks that can be encrypted with a
* GCM cipher. Note the maximum bit-length of the plaintext is (2^39 - 256),
* which translates to a maximum byte-length of (2^36 - 32), which in turn
* translates to a maximum block-length of (2^32 - 2).
*
* Reference: NIST Special Publication 800-38D.
* @link http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
*/
public const GCM_MAX_CONTENT_LENGTH_BITS = (1 << 39) - 256;

/**
* The Maximum length of the content that can be encrypted in CBC mode.
*/
public const CBC_MAX_CONTENT_LENGTH_BYTES = 1 << 55;

/**
* The maximum number of bytes that can be securely encrypted per a single key using AES/CTR.
*/
public const CTR_MAX_CONTENT_LENGTH_BYTES = -1;
}
Loading