diff --git a/src/s3_encryption/__init__.py b/src/s3_encryption/__init__.py index 6ee409ad..caad4f66 100644 --- a/src/s3_encryption/__init__.py +++ b/src/s3_encryption/__init__.py @@ -511,6 +511,12 @@ def get_object(self, **kwargs): Raises: S3EncryptionClientError: If decryption fails or the object is not properly encrypted. """ + # Ranged gets are not supported — decryption requires the full ciphertext. + if "Range" in kwargs: + raise S3EncryptionClientError( + "Ranged gets are currently not supported by the S3 Encryption Client for Python." + ) + # Extract EncryptionContext if provided (not a standard S3 parameter) encryption_context = kwargs.pop("EncryptionContext", None) _validate_encryption_context(encryption_context) diff --git a/test/integration/test_i_ranged_get.py b/test/integration/test_i_ranged_get.py new file mode 100644 index 00000000..d9bb65af --- /dev/null +++ b/test/integration/test_i_ranged_get.py @@ -0,0 +1,72 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Integration test for ranged get (Range parameter on get_object). + +The S3 Encryption Client does not support ranged gets because decryption +requires the full ciphertext (IV, encrypted data, and auth tag). Passing +a Range parameter retrieves only a slice of the ciphertext, which causes +decryption to fail. +""" + +import os +from datetime import datetime + +import boto3 +import pytest + +from s3_encryption import S3EncryptionClient, S3EncryptionClientConfig +from s3_encryption.exceptions import S3EncryptionClientError +from s3_encryption.materials.kms_keyring import KmsKeyring +from s3_encryption.materials.materials import AlgorithmSuite, CommitmentPolicy + +bucket = os.environ.get("CI_S3_BUCKET", "s3ec-python-github-test-bucket") +region = os.environ.get("CI_AWS_REGION", "us-west-2") +kms_key_id = os.environ.get( + "CI_KMS_KEY_ALIAS", "arn:aws:kms:us-west-2:370957321024:alias/S3EC-Python-Github-KMS-Key" +) + + +def _make_client(algorithm_suite, commitment_policy): + """Create an S3EncryptionClient with the given algorithm config.""" + kms_client = boto3.client("kms", region_name=region) + keyring = KmsKeyring(kms_client, kms_key_id) + wrapped_client = boto3.client("s3") + config = S3EncryptionClientConfig( + keyring, + encryption_algorithm=algorithm_suite, + commitment_policy=commitment_policy, + ) + return S3EncryptionClient(wrapped_client, config) + + +def _unique_key(prefix): + """Generate a unique S3 key with a timestamp suffix.""" + return prefix + datetime.now().strftime("%Y-%m-%d-%H:%M:%S-%f") + + +ALGORITHM_CONFIGS = [ + pytest.param( + AlgorithmSuite.ALG_AES_256_GCM_IV12_TAG16_NO_KDF, + CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT, + id="AES_GCM", + ), + pytest.param( + AlgorithmSuite.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY, + CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT, + id="KC_GCM", + ), +] + + +@pytest.mark.parametrize(("algorithm_suite", "commitment_policy"), ALGORITHM_CONFIGS) +def test_ranged_get_fails(algorithm_suite, commitment_policy): + """Ranged gets are rejected with a clear error.""" + key = _unique_key("ranged-get-") + data = b"A" * 1024 + + s3ec = _make_client(algorithm_suite, commitment_policy) + s3ec.put_object(Bucket=bucket, Key=key, Body=data) + + # Attempt a ranged get — should raise immediately with a clear message + with pytest.raises(S3EncryptionClientError, match="Ranged gets are currently not supported"): + s3ec.get_object(Bucket=bucket, Key=key, Range="bytes=0-255")