|
8 | 8 |
|
9 | 9 | from __future__ import absolute_import, division
|
10 | 10 |
|
11 |
| -import sys |
12 |
| -import os |
13 |
| -import time |
| 11 | +import base64 |
14 | 12 | import errno
|
15 |
| -import mimetypes |
| 13 | +import hashlib |
16 | 14 | import io
|
| 15 | +import mimetypes |
| 16 | +import os |
17 | 17 | import pprint
|
18 |
| -from xml.sax import saxutils |
| 18 | +import sys |
| 19 | +import time |
| 20 | +from logging import debug, error, info, warning |
19 | 21 | from socket import timeout as SocketTimeoutException
|
20 |
| -from logging import debug, info, warning, error |
21 | 22 | from stat import ST_SIZE
|
| 23 | +from xml.sax import saxutils |
| 24 | + |
22 | 25 | try:
|
23 | 26 | # python 3 support
|
24 | 27 | from urlparse import urlparse
|
|
38 | 41 | except ImportError:
|
39 | 42 | from md5 import md5
|
40 | 43 |
|
41 |
| -from .BaseUtils import (getListFromXml, getTextFromXml, getRootTagName, |
42 |
| - decode_from_s3, encode_to_s3, s3_quote) |
43 |
| -from .Utils import (convertHeaderTupleListToDict, hash_file_md5, unicodise, |
44 |
| - deunicodise, check_bucket_name, |
45 |
| - check_bucket_name_dns_support, getHostnameFromBucket, |
46 |
| - calculateChecksum) |
47 |
| -from .SortedDict import SortedDict |
48 | 44 | from .AccessLog import AccessLog
|
49 | 45 | from .ACL import ACL, GranteeLogDelivery
|
| 46 | +from .BaseUtils import (decode_from_s3, encode_to_s3, getListFromXml, |
| 47 | + getRootTagName, getTextFromXml, s3_quote) |
50 | 48 | from .BidirMap import BidirMap
|
51 | 49 | from .Config import Config
|
| 50 | +from .ConnMan import ConnMan |
| 51 | +from .Crypto import (checksum_sha256_buffer, checksum_sha256_file, |
| 52 | + format_param_str, sign_request_v2, sign_request_v4) |
52 | 53 | from .Exceptions import *
|
53 | 54 | from .MultiPart import MultiPartUpload
|
54 | 55 | from .S3Uri import S3Uri
|
55 |
| -from .ConnMan import ConnMan |
56 |
| -from .Crypto import (sign_request_v2, sign_request_v4, checksum_sha256_file, |
57 |
| - checksum_sha256_buffer, format_param_str) |
| 56 | +from .SortedDict import SortedDict |
| 57 | +from .Utils import (calculateChecksum, check_bucket_name, |
| 58 | + check_bucket_name_dns_support, |
| 59 | + convertHeaderTupleListToDict, deunicodise, |
| 60 | + getHostnameFromBucket, hash_file_md5, unicodise) |
58 | 61 |
|
59 | 62 | try:
|
60 | 63 | from ctypes import ArgumentError
|
| 64 | + |
61 | 65 | import magic
|
62 | 66 | try:
|
63 | 67 | ## https://github.com/ahupp/python-magic
|
@@ -701,6 +705,16 @@ def object_put(self, filename, uri, extra_headers = None, extra_label = ""):
|
701 | 705 | headers['x-amz-server-side-encryption'] = 'aws:kms'
|
702 | 706 | headers['x-amz-server-side-encryption-aws-kms-key-id'] = self.config.kms_key
|
703 | 707 |
|
| 708 | + if self.config.sse_customer_key: |
| 709 | + md5 = hashlib.md5() |
| 710 | + sse_customer_key = self.config.sse_customer_key.encode() |
| 711 | + md5.update(sse_customer_key) |
| 712 | + md5_encoded = base64.b64encode(md5.digest()) |
| 713 | + encoded = base64.b64encode(sse_customer_key) |
| 714 | + headers["x-amz-server-side-encryption-customer-algorithm"] = "AES256" |
| 715 | + headers["x-amz-server-side-encryption-customer-key"] = encoded.decode() |
| 716 | + headers["x-amz-server-side-encryption-customer-key-md5"] = md5_encoded.decode() |
| 717 | + |
704 | 718 | ## MIME-type handling
|
705 | 719 | headers["content-type"] = self.content_type(filename=filename)
|
706 | 720 |
|
@@ -755,10 +769,32 @@ def object_put(self, filename, uri, extra_headers = None, extra_label = ""):
|
755 | 769 | response = self.send_file(request, src_stream, labels)
|
756 | 770 | return response
|
757 | 771 |
|
758 |
| - def object_get(self, uri, stream, dest_name, start_position = 0, extra_label = ""): |
| 772 | + def object_get(self, uri, stream, dest_name, extra_headers, start_position = 0, extra_label = ""): |
759 | 773 | if uri.type != "s3":
|
760 | 774 | raise ValueError("Expected URI type 's3', got '%s'" % uri.type)
|
761 |
| - request = self.create_request("OBJECT_GET", uri = uri) |
| 775 | + headers = SortedDict(ignore_case=True) |
| 776 | + if extra_headers: |
| 777 | + headers.update(extra_headers) |
| 778 | + ## Set server side encryption |
| 779 | + if self.config.server_side_encryption: |
| 780 | + headers["x-amz-server-side-encryption"] = "AES256" |
| 781 | + |
| 782 | + ## Set kms headers |
| 783 | + if self.config.kms_key: |
| 784 | + headers['x-amz-server-side-encryption'] = 'aws:kms' |
| 785 | + headers['x-amz-server-side-encryption-aws-kms-key-id'] = self.config.kms_key |
| 786 | + |
| 787 | + if self.config.sse_customer_key: |
| 788 | + md5 = hashlib.md5() |
| 789 | + sse_customer_key = self.config.sse_customer_key.encode() |
| 790 | + md5.update(sse_customer_key) |
| 791 | + md5_encoded = base64.b64encode(md5.digest()) |
| 792 | + encoded = base64.b64encode(sse_customer_key) |
| 793 | + headers["x-amz-server-side-encryption-customer-algorithm"] = "AES256" |
| 794 | + headers["x-amz-server-side-encryption-customer-key"] = encoded.decode() |
| 795 | + headers["x-amz-server-side-encryption-customer-key-md5"] = md5_encoded.decode() |
| 796 | + |
| 797 | + request = self.create_request("OBJECT_GET", uri = uri, headers=headers) |
762 | 798 | labels = { 'source' : uri.uri(), 'destination' : dest_name, 'extra' : extra_label }
|
763 | 799 | response = self.recv_file(request, stream, labels, start_position)
|
764 | 800 | return response
|
@@ -954,6 +990,16 @@ def object_copy(self, src_uri, dst_uri, extra_headers=None,
|
954 | 990 | headers['x-amz-server-side-encryption-aws-kms-key-id'] = \
|
955 | 991 | self.config.kms_key
|
956 | 992 |
|
| 993 | + if self.config.sse_copy_source_customer_key: |
| 994 | + md5 = hashlib.md5() |
| 995 | + sse_copy_source_customer_key = self.config.sse_copy_source_customer_key.encode() |
| 996 | + md5.update(sse_copy_source_customer_key) |
| 997 | + md5_encoded = base64.b64encode(md5.digest()) |
| 998 | + encoded = base64.b64encode(sse_copy_source_customer_key) |
| 999 | + headers["x-amz-copy-source-server-side-encryption-customer-algorithm"] = "AES256" |
| 1000 | + headers["x-amz-copy-source-server-side-encryption-customer-key"] = encoded.decode() |
| 1001 | + headers["x-amz-copy-source-server-side-encryption-customer-key-md5"] = md5_encoded.decode() |
| 1002 | + |
957 | 1003 | # Following meta data are not updated in simple COPY by aws.
|
958 | 1004 | if extra_headers:
|
959 | 1005 | headers.update(extra_headers)
|
@@ -1828,19 +1874,32 @@ def send_file(self, request, stream, labels, buffer = '', throttle = 0,
|
1828 | 1874 | ## Non-recoverable error
|
1829 | 1875 | raise S3Error(response)
|
1830 | 1876 |
|
1831 |
| - debug("MD5 sums: computed=%s, received=%s" % (md5_computed, response["headers"].get('etag', '').strip('"\''))) |
1832 |
| - ## when using KMS encryption, MD5 etag value will not match |
1833 |
| - md5_from_s3 = response["headers"].get("etag", "").strip('"\'') |
1834 |
| - if ('-' not in md5_from_s3) and (md5_from_s3 != md5_hash.hexdigest()) and response["headers"].get("x-amz-server-side-encryption") != 'aws:kms': |
1835 |
| - warning("MD5 Sums don't match!") |
1836 |
| - if retries: |
1837 |
| - warning("Retrying upload of %s" % (filename)) |
1838 |
| - return self.send_file(request, stream, labels, buffer, throttle, |
1839 |
| - retries - 1, offset, chunk_size, use_expect_continue) |
| 1877 | + if self.config.sse_customer_key: |
| 1878 | + if response["headers"]["x-amz-server-side-encryption-customer-key-md5"] != \ |
| 1879 | + request.headers["x-amz-server-side-encryption-customer-key-md5"]: |
| 1880 | + warning("MD5 of customer key don't match!") |
| 1881 | + if retries: |
| 1882 | + warning("Retrying upload of %s" % (filename)) |
| 1883 | + return self.send_file(request, stream, labels, buffer, throttle, retries - 1, offset, chunk_size) |
| 1884 | + else: |
| 1885 | + warning("Too many failures. Giving up on '%s'" % (filename)) |
| 1886 | + raise S3UploadError |
1840 | 1887 | else:
|
1841 |
| - warning("Too many failures. Giving up on '%s'" % (filename)) |
1842 |
| - raise S3UploadError("Too many failures. Giving up on '%s'" |
1843 |
| - % filename) |
| 1888 | + debug("Match of x-amz-server-side-encryption-customer-key-md5") |
| 1889 | + else: |
| 1890 | + debug("MD5 sums: computed=%s, received=%s" % (md5_computed, response["headers"].get('etag', '').strip('"\''))) |
| 1891 | + ## when using KMS encryption, MD5 etag value will not match |
| 1892 | + md5_from_s3 = response["headers"].get("etag", "").strip('"\'') |
| 1893 | + if ('-' not in md5_from_s3) and (md5_from_s3 != md5_hash.hexdigest()) and response["headers"].get("x-amz-server-side-encryption") != 'aws:kms': |
| 1894 | + warning("MD5 Sums don't match!") |
| 1895 | + if retries: |
| 1896 | + warning("Retrying upload of %s" % (filename)) |
| 1897 | + return self.send_file(request, stream, labels, buffer, throttle, |
| 1898 | + retries - 1, offset, chunk_size, use_expect_continue) |
| 1899 | + else: |
| 1900 | + warning("Too many failures. Giving up on '%s'" % (filename)) |
| 1901 | + raise S3UploadError("Too many failures. Giving up on '%s'" |
| 1902 | + % filename) |
1844 | 1903 |
|
1845 | 1904 | return response
|
1846 | 1905 |
|
|
0 commit comments