|
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