mirror of
https://github.com/ceph/s3-tests.git
synced 2024-11-25 13:47:27 +00:00
Merge pull request #154 from ceph/wip-pr-118
functional tests for server side encryption Reviewed-by: Orit Wasserman <owasserm@redhat.com>
This commit is contained in:
commit
289eff57ec
4 changed files with 744 additions and 4 deletions
|
@ -56,6 +56,9 @@ service and two different credentials, something like this::
|
||||||
access_key = ABCDEFGHIJKLMNOPQRST
|
access_key = ABCDEFGHIJKLMNOPQRST
|
||||||
secret_key = abcdefghijklmnopqrstuvwxyzabcdefghijklmn
|
secret_key = abcdefghijklmnopqrstuvwxyzabcdefghijklmn
|
||||||
|
|
||||||
|
## replace with key id obtained when secret is created, or delete if KMS not tested
|
||||||
|
kms_keyid = 01234567-89ab-cdef-0123-456789abcdef
|
||||||
|
|
||||||
[s3 alt]
|
[s3 alt]
|
||||||
## another user account, used for ACL-related tests
|
## another user account, used for ACL-related tests
|
||||||
user_id = 56789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234
|
user_id = 56789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234
|
||||||
|
|
|
@ -71,6 +71,9 @@ s3:
|
||||||
access_key: AWS_ACCESS_KEY
|
access_key: AWS_ACCESS_KEY
|
||||||
secret_key: AWS_SECRET_KEY
|
secret_key: AWS_SECRET_KEY
|
||||||
|
|
||||||
|
## If KMS is tested, this if barbican key id. Optional.
|
||||||
|
kms_keyid: barbican_key_id
|
||||||
|
|
||||||
alt:
|
alt:
|
||||||
## Another user accout, used for ACL-related tests.
|
## Another user accout, used for ACL-related tests.
|
||||||
|
|
||||||
|
|
|
@ -318,6 +318,7 @@ def setup():
|
||||||
'host',
|
'host',
|
||||||
'port',
|
'port',
|
||||||
'is_secure',
|
'is_secure',
|
||||||
|
'kms_keyid',
|
||||||
]:
|
]:
|
||||||
try:
|
try:
|
||||||
config[name][var] = cfg.get(section, var)
|
config[name][var] = cfg.get(section, var)
|
||||||
|
|
|
@ -5119,14 +5119,14 @@ def test_object_copy_versioning_multipart_upload():
|
||||||
got = key6.get_contents_as_string()
|
got = key6.get_contents_as_string()
|
||||||
eq(got, data)
|
eq(got, data)
|
||||||
|
|
||||||
def transfer_part(bucket, mp_id, mp_keyname, i, part):
|
def transfer_part(bucket, mp_id, mp_keyname, i, part, headers=None):
|
||||||
"""Transfer a part of a multipart upload. Designed to be run in parallel.
|
"""Transfer a part of a multipart upload. Designed to be run in parallel.
|
||||||
"""
|
"""
|
||||||
mp = boto.s3.multipart.MultiPartUpload(bucket)
|
mp = boto.s3.multipart.MultiPartUpload(bucket)
|
||||||
mp.key_name = mp_keyname
|
mp.key_name = mp_keyname
|
||||||
mp.id = mp_id
|
mp.id = mp_id
|
||||||
part_out = StringIO(part)
|
part_out = StringIO(part)
|
||||||
mp.upload_part_from_file(part_out, i+1)
|
mp.upload_part_from_file(part_out, i+1, headers=headers)
|
||||||
|
|
||||||
def copy_part(src_bucket, src_keyname, dst_bucket, dst_keyname, mp_id, i, start=None, end=None, src_version_id=None):
|
def copy_part(src_bucket, src_keyname, dst_bucket, dst_keyname, mp_id, i, start=None, end=None, src_version_id=None):
|
||||||
"""Copy a part of a multipart upload from other bucket.
|
"""Copy a part of a multipart upload from other bucket.
|
||||||
|
@ -5167,9 +5167,9 @@ def _multipart_upload(bucket, s3_key_name, size, part_size=5*1024*1024, do_list=
|
||||||
s = ''
|
s = ''
|
||||||
for i, part in enumerate(generate_random(size, part_size)):
|
for i, part in enumerate(generate_random(size, part_size)):
|
||||||
s += part
|
s += part
|
||||||
transfer_part(bucket, upload.id, upload.key_name, i, part)
|
transfer_part(bucket, upload.id, upload.key_name, i, part, headers)
|
||||||
if i in resend_parts:
|
if i in resend_parts:
|
||||||
transfer_part(bucket, upload.id, upload.key_name, i, part)
|
transfer_part(bucket, upload.id, upload.key_name, i, part, headers)
|
||||||
|
|
||||||
if do_list is not None:
|
if do_list is not None:
|
||||||
l = bucket.list_multipart_uploads()
|
l = bucket.list_multipart_uploads()
|
||||||
|
@ -7533,3 +7533,736 @@ def test_lifecycle_rules_conflicted():
|
||||||
eq(e.status, 400)
|
eq(e.status, 400)
|
||||||
eq(e.error_code, 'InvalidRequest')
|
eq(e.error_code, 'InvalidRequest')
|
||||||
|
|
||||||
|
def _test_encryption_sse_customer_write(file_size):
|
||||||
|
"""
|
||||||
|
Tests Create a file of A's, use it to set_contents_from_file.
|
||||||
|
Create a file of B's, use it to re-set_contents_from_file.
|
||||||
|
Re-read the contents, and confirm we get B's
|
||||||
|
"""
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_client_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw=='
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*file_size
|
||||||
|
key.set_contents_from_string(data, headers=sse_client_headers)
|
||||||
|
rdata = key.get_contents_as_string(headers=sse_client_headers)
|
||||||
|
eq(data, rdata)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-C encrypted transfer 1 byte')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encrypted_transfer_1b():
|
||||||
|
_test_encryption_sse_customer_write(1)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-C encrypted transfer 1KB')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encrypted_transfer_1kb():
|
||||||
|
_test_encryption_sse_customer_write(1024)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-C encrypted transfer 1MB')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encrypted_transfer_1MB():
|
||||||
|
_test_encryption_sse_customer_write(1024*1024)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-C encrypted transfer 13 bytes')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encrypted_transfer_13b():
|
||||||
|
_test_encryption_sse_customer_write(13)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='head')
|
||||||
|
@attr(operation='Test SSE-C encrypted does perform head properly')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_method_head():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_client_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw=='
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*1000
|
||||||
|
key.set_contents_from_string(data, headers=sse_client_headers)
|
||||||
|
|
||||||
|
res = _make_request('HEAD', bucket, key, authenticated=True)
|
||||||
|
eq(res.status, 400)
|
||||||
|
|
||||||
|
res = _make_request('HEAD', bucket, key, authenticated=True, request_headers=sse_client_headers)
|
||||||
|
eq(res.status, 200)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='write encrypted with SSE-C and read without SSE-C')
|
||||||
|
@attr(assertion='operation fails')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_present():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_client_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw=='
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
key.set_contents_from_string(data, headers=sse_client_headers)
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError, key.get_contents_as_string)
|
||||||
|
eq(e.status, 400)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='write encrypted with SSE-C but read with other key')
|
||||||
|
@attr(assertion='operation fails')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_other_key():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_client_headers_A = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw=='
|
||||||
|
}
|
||||||
|
sse_client_headers_B = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': '6b+WOZ1T3cqZMxgThRcXAQBrS5mXKdDUphvpxptl9/4=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'arxBvwY2V4SiOne6yppVPQ=='
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
key.set_contents_from_string(data, headers=sse_client_headers_A)
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError,
|
||||||
|
key.get_contents_as_string, headers=sse_client_headers_B)
|
||||||
|
eq(e.status, 400)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='write encrypted with SSE-C, but md5 is bad')
|
||||||
|
@attr(assertion='operation fails')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_invalid_md5():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_client_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'AAAAAAAAAAAAAAAAAAAAAA=='
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError,
|
||||||
|
key.set_contents_from_string, data, headers=sse_client_headers)
|
||||||
|
eq(e.status, 400)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='write encrypted with SSE-C, but dont provide MD5')
|
||||||
|
@attr(assertion='operation fails')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_no_md5():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_client_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs='
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError,
|
||||||
|
key.set_contents_from_string, data, headers=sse_client_headers)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='declare SSE-C but do not provide key')
|
||||||
|
@attr(assertion='operation fails')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_no_key():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_client_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256'
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError,
|
||||||
|
key.set_contents_from_string, data, headers=sse_client_headers)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Do not declare SSE-C but provide key and MD5')
|
||||||
|
@attr(assertion='operation successfull, no encryption')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_key_no_sse_c():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_client_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw=='
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
key.set_contents_from_string(data, headers=sse_client_headers)
|
||||||
|
rdata = key.get_contents_as_string()
|
||||||
|
eq(data, rdata)
|
||||||
|
|
||||||
|
|
||||||
|
def _multipart_upload_enc(bucket, s3_key_name, size, part_size=5*1024*1024,
|
||||||
|
do_list=None, init_headers=None, part_headers=None,
|
||||||
|
metadata=None, resend_parts=[]):
|
||||||
|
"""
|
||||||
|
generate a multi-part upload for a random file of specifed size,
|
||||||
|
if requested, generate a list of the parts
|
||||||
|
return the upload descriptor
|
||||||
|
"""
|
||||||
|
upload = bucket.initiate_multipart_upload(s3_key_name, headers=init_headers, metadata=metadata)
|
||||||
|
s = ''
|
||||||
|
for i, part in enumerate(generate_random(size, part_size)):
|
||||||
|
s += part
|
||||||
|
transfer_part(bucket, upload.id, upload.key_name, i, part, part_headers)
|
||||||
|
if i in resend_parts:
|
||||||
|
transfer_part(bucket, upload.id, upload.key_name, i, part, part_headers)
|
||||||
|
|
||||||
|
if do_list is not None:
|
||||||
|
l = bucket.list_multipart_uploads()
|
||||||
|
l = list(l)
|
||||||
|
|
||||||
|
return (upload, s)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_content_using_range_enc(k, data, step, enc_headers=None):
|
||||||
|
objlen = k.size
|
||||||
|
for ofs in xrange(0, k.size, step):
|
||||||
|
toread = k.size - ofs
|
||||||
|
if toread > step:
|
||||||
|
toread = step
|
||||||
|
end = ofs + toread - 1
|
||||||
|
read_range = k.get_contents_as_string(
|
||||||
|
headers=dict({'Range': 'bytes={s}-{e}'.format(s=ofs, e=end)}, **enc_headers))
|
||||||
|
eq(len(read_range), toread)
|
||||||
|
eq(read_range, data[ofs:end+1])
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='complete multi-part upload')
|
||||||
|
@attr(assertion='successful')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_multipart_upload():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
key = "multipart_enc"
|
||||||
|
content_type = 'text/plain'
|
||||||
|
objlen = 30 * 1024 * 1024
|
||||||
|
enc_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw==',
|
||||||
|
'Content-Type': content_type
|
||||||
|
}
|
||||||
|
(upload, data) = _multipart_upload_enc(bucket, key, objlen,
|
||||||
|
init_headers=enc_headers, part_headers=enc_headers,
|
||||||
|
metadata={'foo': 'bar'})
|
||||||
|
upload.complete_upload()
|
||||||
|
result = _head_bucket(bucket)
|
||||||
|
|
||||||
|
eq(result.get('x-rgw-object-count', 1), 1)
|
||||||
|
eq(result.get('x-rgw-bytes-used', 30 * 1024 * 1024), 30 * 1024 * 1024)
|
||||||
|
|
||||||
|
k = bucket.get_key(key, headers=enc_headers)
|
||||||
|
eq(k.metadata['foo'], 'bar')
|
||||||
|
eq(k.content_type, content_type)
|
||||||
|
test_string = k.get_contents_as_string(headers=enc_headers)
|
||||||
|
eq(len(test_string), k.size)
|
||||||
|
eq(data, test_string)
|
||||||
|
eq(test_string, data)
|
||||||
|
|
||||||
|
_check_content_using_range_enc(k, data, 1000000, enc_headers=enc_headers)
|
||||||
|
_check_content_using_range_enc(k, data, 10000000, enc_headers=enc_headers)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='multipart upload with bad key for uploading chunks')
|
||||||
|
@attr(assertion='successful')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_multipart_invalid_chunks_1():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
key = "multipart_enc"
|
||||||
|
content_type = 'text/bla'
|
||||||
|
objlen = 30 * 1024 * 1024
|
||||||
|
init_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw==',
|
||||||
|
'Content-Type': content_type
|
||||||
|
}
|
||||||
|
part_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': '6b+WOZ1T3cqZMxgThRcXAQBrS5mXKdDUphvpxptl9/4=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'arxBvwY2V4SiOne6yppVPQ=='
|
||||||
|
}
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError,
|
||||||
|
_multipart_upload_enc, bucket, key, objlen,
|
||||||
|
init_headers=init_headers, part_headers=part_headers,
|
||||||
|
metadata={'foo': 'bar'})
|
||||||
|
eq(e.status, 400)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='multipart upload with bad md5 for chunks')
|
||||||
|
@attr(assertion='successful')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_multipart_invalid_chunks_2():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
key = "multipart_enc"
|
||||||
|
content_type = 'text/plain'
|
||||||
|
objlen = 30 * 1024 * 1024
|
||||||
|
init_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw==',
|
||||||
|
'Content-Type': content_type
|
||||||
|
}
|
||||||
|
part_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'AAAAAAAAAAAAAAAAAAAAAA=='
|
||||||
|
}
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError,
|
||||||
|
_multipart_upload_enc, bucket, key, objlen,
|
||||||
|
init_headers=init_headers, part_headers=part_headers,
|
||||||
|
metadata={'foo': 'bar'})
|
||||||
|
eq(e.status, 400)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='complete multi-part upload and download with bad key')
|
||||||
|
@attr(assertion='successful')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_multipart_bad_download():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
key = "multipart_enc"
|
||||||
|
content_type = 'text/plain'
|
||||||
|
objlen = 30 * 1024 * 1024
|
||||||
|
put_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw==',
|
||||||
|
'Content-Type': content_type
|
||||||
|
}
|
||||||
|
get_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': '6b+WOZ1T3cqZMxgThRcXAQBrS5mXKdDUphvpxptl9/4=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'arxBvwY2V4SiOne6yppVPQ=='
|
||||||
|
}
|
||||||
|
|
||||||
|
(upload, data) = _multipart_upload_enc(bucket, key, objlen,
|
||||||
|
init_headers=put_headers, part_headers=put_headers,
|
||||||
|
metadata={'foo': 'bar'})
|
||||||
|
upload.complete_upload()
|
||||||
|
result = _head_bucket(bucket)
|
||||||
|
|
||||||
|
eq(result.get('x-rgw-object-count', 1), 1)
|
||||||
|
eq(result.get('x-rgw-bytes-used', 30 * 1024 * 1024), 30 * 1024 * 1024)
|
||||||
|
|
||||||
|
k = bucket.get_key(key, headers=put_headers)
|
||||||
|
eq(k.metadata['foo'], 'bar')
|
||||||
|
eq(k.content_type, content_type)
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError,
|
||||||
|
k.get_contents_as_string, headers=get_headers)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='post')
|
||||||
|
@attr(operation='authenticated browser based upload via POST request')
|
||||||
|
@attr(assertion='succeeds and returns written data')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_encryption_sse_c_post_object_authenticated_request():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
|
||||||
|
url = _get_post_url(s3.main, bucket)
|
||||||
|
|
||||||
|
utc = pytz.utc
|
||||||
|
expires = datetime.datetime.now(utc) + datetime.timedelta(seconds=+6000)
|
||||||
|
|
||||||
|
policy_document = {"expiration": expires.strftime("%Y-%m-%dT%H:%M:%SZ"), \
|
||||||
|
"conditions": [ \
|
||||||
|
{"bucket": bucket.name}, \
|
||||||
|
["starts-with", "$key", "foo"], \
|
||||||
|
{"acl": "private"}, \
|
||||||
|
["starts-with", "$Content-Type", "text/plain"], \
|
||||||
|
["starts-with", "$x-amz-server-side-encryption-customer-algorithm", ""], \
|
||||||
|
["starts-with", "$x-amz-server-side-encryption-customer-key", ""], \
|
||||||
|
["starts-with", "$x-amz-server-side-encryption-customer-key-md5", ""], \
|
||||||
|
["content-length-range", 0, 1024] \
|
||||||
|
] \
|
||||||
|
}
|
||||||
|
|
||||||
|
json_policy_document = json.JSONEncoder().encode(policy_document)
|
||||||
|
policy = base64.b64encode(json_policy_document)
|
||||||
|
conn = s3.main
|
||||||
|
signature = base64.b64encode(hmac.new(conn.aws_secret_access_key, policy, sha).digest())
|
||||||
|
|
||||||
|
payload = OrderedDict([ ("key" , "foo.txt"),("AWSAccessKeyId" , conn.aws_access_key_id), \
|
||||||
|
("acl" , "private"),("signature" , signature),("policy" , policy), \
|
||||||
|
("Content-Type" , "text/plain"), \
|
||||||
|
('x-amz-server-side-encryption-customer-algorithm', 'AES256'), \
|
||||||
|
('x-amz-server-side-encryption-customer-key', 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs='), \
|
||||||
|
('x-amz-server-side-encryption-customer-key-md5', 'DWygnHRtgiJ77HCm+1rvHw=='), \
|
||||||
|
('file', ('bar'),), ])
|
||||||
|
|
||||||
|
r = requests.post(url, files = payload)
|
||||||
|
eq(r.status_code, 204)
|
||||||
|
get_headers = {
|
||||||
|
'x-amz-server-side-encryption-customer-algorithm': 'AES256',
|
||||||
|
'x-amz-server-side-encryption-customer-key': 'pO3upElrwuEXSoFwCfnZPdSsmt/xWeFa0N9KgDijwVs=',
|
||||||
|
'x-amz-server-side-encryption-customer-key-md5': 'DWygnHRtgiJ77HCm+1rvHw=='
|
||||||
|
}
|
||||||
|
|
||||||
|
key = bucket.get_key("foo.txt", headers=get_headers)
|
||||||
|
got = key.get_contents_as_string(headers=get_headers)
|
||||||
|
eq(got, 'bar')
|
||||||
|
|
||||||
|
|
||||||
|
def _test_sse_kms_customer_write(file_size, key_id = 'testkey-1'):
|
||||||
|
"""
|
||||||
|
Tests Create a file of A's, use it to set_contents_from_file.
|
||||||
|
Create a file of B's, use it to re-set_contents_from_file.
|
||||||
|
Re-read the contents, and confirm we get B's
|
||||||
|
"""
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_kms_client_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': key_id
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*file_size
|
||||||
|
key.set_contents_from_string(data, headers=sse_kms_client_headers)
|
||||||
|
rdata = key.get_contents_as_string(headers=sse_kms_client_headers)
|
||||||
|
eq(data, rdata)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted transfer 1 byte')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_transfer_1b():
|
||||||
|
_test_sse_kms_customer_write(1)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted transfer 1KB')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_transfer_1kb():
|
||||||
|
_test_sse_kms_customer_write(1024)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted transfer 1MB')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_transfer_1MB():
|
||||||
|
_test_sse_kms_customer_write(1024*1024)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted transfer 13 bytes')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_transfer_13b():
|
||||||
|
_test_sse_kms_customer_write(13)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='head')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted does perform head properly')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_method_head():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_kms_client_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-1'
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*1000
|
||||||
|
key.set_contents_from_string(data, headers=sse_kms_client_headers)
|
||||||
|
|
||||||
|
res = _make_request('HEAD', bucket, key, authenticated=True)
|
||||||
|
eq(res.status, 200)
|
||||||
|
eq(res.getheader('x-amz-server-side-encryption'), 'aws:kms')
|
||||||
|
eq(res.getheader('x-amz-server-side-encryption-aws-kms-key-id'), 'testkey-1')
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='write encrypted with SSE-KMS and read without SSE-KMS')
|
||||||
|
@attr(assertion='operation success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_present():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_kms_client_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-1'
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
key.set_contents_from_string(data, headers=sse_kms_client_headers)
|
||||||
|
result = key.get_contents_as_string()
|
||||||
|
eq(data, result)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='write encrypted with SSE-KMS but read with other key')
|
||||||
|
@attr(assertion='operation fails')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_other_key():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_kms_client_headers_A = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-1'
|
||||||
|
}
|
||||||
|
sse_kms_client_headers_B = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-2'
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
key.set_contents_from_string(data, headers=sse_kms_client_headers_A)
|
||||||
|
result = key.get_contents_as_string(headers=sse_kms_client_headers_B)
|
||||||
|
eq(data, result)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='declare SSE-KMS but do not provide key_id')
|
||||||
|
@attr(assertion='operation fails')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_no_key():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_kms_client_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms'
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
e = assert_raises(boto.exception.S3ResponseError,
|
||||||
|
key.set_contents_from_string, data, headers=sse_kms_client_headers)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Do not declare SSE-KMS but provide key_id')
|
||||||
|
@attr(assertion='operation successfull, no encryption')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_not_declared():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
sse_kms_client_headers = {
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-2'
|
||||||
|
}
|
||||||
|
key = bucket.new_key('testobj')
|
||||||
|
data = 'A'*100
|
||||||
|
key.set_contents_from_string(data, headers=sse_kms_client_headers)
|
||||||
|
rdata = key.get_contents_as_string()
|
||||||
|
eq(data, rdata)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='complete KMS multi-part upload')
|
||||||
|
@attr(assertion='successful')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_multipart_upload():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
key = "multipart_enc"
|
||||||
|
content_type = 'text/plain'
|
||||||
|
objlen = 30 * 1024 * 1024
|
||||||
|
enc_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-2',
|
||||||
|
'Content-Type': content_type
|
||||||
|
}
|
||||||
|
(upload, data) = _multipart_upload_enc(bucket, key, objlen,
|
||||||
|
init_headers=enc_headers, part_headers=enc_headers,
|
||||||
|
metadata={'foo': 'bar'})
|
||||||
|
upload.complete_upload()
|
||||||
|
result = _head_bucket(bucket)
|
||||||
|
|
||||||
|
eq(result.get('x-rgw-object-count', 1), 1)
|
||||||
|
eq(result.get('x-rgw-bytes-used', 30 * 1024 * 1024), 30 * 1024 * 1024)
|
||||||
|
|
||||||
|
k = bucket.get_key(key)
|
||||||
|
eq(k.metadata['foo'], 'bar')
|
||||||
|
eq(k.content_type, content_type)
|
||||||
|
test_string = k.get_contents_as_string(headers=enc_headers)
|
||||||
|
eq(len(test_string), k.size)
|
||||||
|
eq(data, test_string)
|
||||||
|
eq(test_string, data)
|
||||||
|
|
||||||
|
_check_content_using_range_enc(k, data, 1000000, enc_headers=enc_headers)
|
||||||
|
_check_content_using_range_enc(k, data, 10000000, enc_headers=enc_headers)
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='multipart KMS upload with bad key_id for uploading chunks')
|
||||||
|
@attr(assertion='successful')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_multipart_invalid_chunks_1():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
key = "multipart_enc"
|
||||||
|
content_type = 'text/bla'
|
||||||
|
objlen = 30 * 1024 * 1024
|
||||||
|
init_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-1',
|
||||||
|
'Content-Type': content_type
|
||||||
|
}
|
||||||
|
part_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-2'
|
||||||
|
}
|
||||||
|
_multipart_upload_enc(bucket, key, objlen,
|
||||||
|
init_headers=init_headers, part_headers=part_headers,
|
||||||
|
metadata={'foo': 'bar'})
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='multipart KMS upload with unexistent key_id for chunks')
|
||||||
|
@attr(assertion='successful')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_multipart_invalid_chunks_2():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
key = "multipart_enc"
|
||||||
|
content_type = 'text/plain'
|
||||||
|
objlen = 30 * 1024 * 1024
|
||||||
|
init_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-1',
|
||||||
|
'Content-Type': content_type
|
||||||
|
}
|
||||||
|
part_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-not-present'
|
||||||
|
}
|
||||||
|
_multipart_upload_enc(bucket, key, objlen,
|
||||||
|
init_headers=init_headers, part_headers=part_headers,
|
||||||
|
metadata={'foo': 'bar'})
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='post')
|
||||||
|
@attr(operation='authenticated KMS browser based upload via POST request')
|
||||||
|
@attr(assertion='succeeds and returns written data')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_post_object_authenticated_request():
|
||||||
|
bucket = get_new_bucket()
|
||||||
|
|
||||||
|
url = _get_post_url(s3.main, bucket)
|
||||||
|
|
||||||
|
utc = pytz.utc
|
||||||
|
expires = datetime.datetime.now(utc) + datetime.timedelta(seconds=+6000)
|
||||||
|
|
||||||
|
policy_document = {"expiration": expires.strftime("%Y-%m-%dT%H:%M:%SZ"), \
|
||||||
|
"conditions": [ \
|
||||||
|
{"bucket": bucket.name}, \
|
||||||
|
["starts-with", "$key", "foo"], \
|
||||||
|
{"acl": "private"}, \
|
||||||
|
["starts-with", "$Content-Type", "text/plain"], \
|
||||||
|
["starts-with", "$x-amz-server-side-encryption", ""], \
|
||||||
|
["starts-with", "$x-amz-server-side-encryption-aws-kms-key-id", ""], \
|
||||||
|
["content-length-range", 0, 1024] \
|
||||||
|
] \
|
||||||
|
}
|
||||||
|
|
||||||
|
json_policy_document = json.JSONEncoder().encode(policy_document)
|
||||||
|
policy = base64.b64encode(json_policy_document)
|
||||||
|
conn = s3.main
|
||||||
|
signature = base64.b64encode(hmac.new(conn.aws_secret_access_key, policy, sha).digest())
|
||||||
|
|
||||||
|
payload = OrderedDict([ ("key" , "foo.txt"),("AWSAccessKeyId" , conn.aws_access_key_id), \
|
||||||
|
("acl" , "private"),("signature" , signature),("policy" , policy), \
|
||||||
|
("Content-Type" , "text/plain"), \
|
||||||
|
('x-amz-server-side-encryption', 'aws:kms'), \
|
||||||
|
('x-amz-server-side-encryption-aws-kms-key-id', 'testkey-1'), \
|
||||||
|
('file', ('bar'),), ])
|
||||||
|
|
||||||
|
r = requests.post(url, files = payload)
|
||||||
|
eq(r.status_code, 204)
|
||||||
|
get_headers = {
|
||||||
|
'x-amz-server-side-encryption': 'aws:kms',
|
||||||
|
'x-amz-server-side-encryption-aws-kms-key-id': 'testkey-1',
|
||||||
|
}
|
||||||
|
|
||||||
|
key = bucket.get_key("foo.txt")
|
||||||
|
got = key.get_contents_as_string(headers=get_headers)
|
||||||
|
eq(got, 'bar')
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted transfer 1 byte')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_barb_transfer_1b():
|
||||||
|
if 'kms_keyid' not in config['main']:
|
||||||
|
raise SkipTest
|
||||||
|
_test_sse_kms_customer_write(1, key_id = config['main']['kms_keyid'])
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted transfer 1KB')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_barb_transfer_1kb():
|
||||||
|
if 'kms_keyid' not in config['main']:
|
||||||
|
raise SkipTest
|
||||||
|
_test_sse_kms_customer_write(1024, key_id = config['main']['kms_keyid'])
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted transfer 1MB')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_barb_transfer_1MB():
|
||||||
|
if 'kms_keyid' not in config['main']:
|
||||||
|
raise SkipTest
|
||||||
|
_test_sse_kms_customer_write(1024*1024, key_id = config['main']['kms_keyid'])
|
||||||
|
|
||||||
|
|
||||||
|
@attr(resource='object')
|
||||||
|
@attr(method='put')
|
||||||
|
@attr(operation='Test SSE-KMS encrypted transfer 13 bytes')
|
||||||
|
@attr(assertion='success')
|
||||||
|
@attr('encryption')
|
||||||
|
def test_sse_kms_barb_transfer_13b():
|
||||||
|
if 'kms_keyid' not in config['main']:
|
||||||
|
raise SkipTest
|
||||||
|
_test_sse_kms_customer_write(13, key_id = config['main']['kms_keyid'])
|
||||||
|
|
Loading…
Reference in a new issue