| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2017, 2018 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	"crypto/hmac" | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	"crypto/rand" | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	"crypto/subtle" | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	"encoding/binary" | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	"errors" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/crypto" | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/ioutil" | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	sha256 "github.com/minio/sha256-simd" | 
					
						
							|  |  |  | 	"github.com/minio/sio" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	// AWS errors for invalid SSE-C requests.
 | 
					
						
							| 
									
										
										
										
											2018-04-07 05:15:23 +08:00
										 |  |  | 	errInsecureSSERequest   = errors.New("SSE-C requests require TLS connections") | 
					
						
							|  |  |  | 	errEncryptedObject      = errors.New("The object was stored using a form of SSE") | 
					
						
							|  |  |  | 	errInvalidSSEParameters = errors.New("The SSE-C key for key-rotation is not correct") // special access denied
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	errKMSNotConfigured     = errors.New("KMS not configured for a server side encrypted object") | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	// Additional Minio errors for SSE-C requests.
 | 
					
						
							|  |  |  | 	errObjectTampered = errors.New("The requested object was modified and may be compromised") | 
					
						
							| 
									
										
										
										
											2018-08-24 22:56:24 +08:00
										 |  |  | 	// error returned when invalid encryption parameters are specified
 | 
					
						
							|  |  |  | 	errInvalidEncryptionParameters = errors.New("The encryption parameters are not applicable to this object") | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// SSECustomerKeySize is the size of valid client provided encryption keys in bytes.
 | 
					
						
							|  |  |  | 	// Currently AWS supports only AES256. So the SSE-C key size is fixed to 32 bytes.
 | 
					
						
							|  |  |  | 	SSECustomerKeySize = 32 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	// SSEIVSize is the size of the IV data
 | 
					
						
							|  |  |  | 	SSEIVSize = 32 // 32 bytes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// SSE dare package block size.
 | 
					
						
							|  |  |  | 	sseDAREPackageBlockSize = 64 * 1024 // 64KiB bytes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// SSE dare package meta padding bytes.
 | 
					
						
							|  |  |  | 	sseDAREPackageMetaSize = 32 // 32 bytes
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	// SSESealAlgorithmDareSha256 specifies DARE as authenticated en/decryption scheme and SHA256 as cryptographic
 | 
					
						
							|  |  |  | 	// hash function. The key derivation of DARE-SHA256 is not optimal and does not include the object path.
 | 
					
						
							|  |  |  | 	// It is considered legacy and should not be used anymore.
 | 
					
						
							|  |  |  | 	SSESealAlgorithmDareSha256 = "DARE-SHA256" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// SSESealAlgorithmDareV2HmacSha256 specifies DAREv2 as authenticated en/decryption scheme and SHA256 as cryptographic
 | 
					
						
							|  |  |  | 	// hash function for the HMAC PRF.
 | 
					
						
							|  |  |  | 	SSESealAlgorithmDareV2HmacSha256 = "DAREv2-HMAC-SHA256" | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | // hasServerSideEncryptionHeader returns true if the given HTTP header
 | 
					
						
							|  |  |  | // contains server-side-encryption.
 | 
					
						
							|  |  |  | func hasServerSideEncryptionHeader(header http.Header) bool { | 
					
						
							|  |  |  | 	return crypto.S3.IsRequested(header) || crypto.SSEC.IsRequested(header) | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ParseSSECopyCustomerRequest parses the SSE-C header fields of the provided request.
 | 
					
						
							|  |  |  | // It returns the client provided key on success.
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | func ParseSSECopyCustomerRequest(r *http.Request, metadata map[string]string) (key []byte, err error) { | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	if !globalIsSSL { // minio only supports HTTP or HTTPS requests not both at the same time
 | 
					
						
							|  |  |  | 		// we cannot use r.TLS == nil here because Go's http implementation reflects on
 | 
					
						
							|  |  |  | 		// the net.Conn and sets the TLS field of http.Request only if it's an tls.Conn.
 | 
					
						
							|  |  |  | 		// Minio uses a BufConn (wrapping a tls.Conn) so the type check within the http package
 | 
					
						
							|  |  |  | 		// will always fail -> r.TLS is always nil even for TLS requests.
 | 
					
						
							|  |  |  | 		return nil, errInsecureSSERequest | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if crypto.S3.IsEncrypted(metadata) && crypto.SSECopy.IsRequested(r.Header) { | 
					
						
							|  |  |  | 		return nil, crypto.ErrIncompatibleEncryptionMethod | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	k, err := crypto.SSECopy.ParseHTTP(r.Header) | 
					
						
							|  |  |  | 	return k[:], err | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | // ParseSSECustomerRequest parses the SSE-C header fields of the provided request.
 | 
					
						
							|  |  |  | // It returns the client provided key on success.
 | 
					
						
							|  |  |  | func ParseSSECustomerRequest(r *http.Request) (key []byte, err error) { | 
					
						
							| 
									
										
										
										
											2018-03-06 00:02:56 +08:00
										 |  |  | 	return ParseSSECustomerHeader(r.Header) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ParseSSECustomerHeader parses the SSE-C header fields and returns
 | 
					
						
							|  |  |  | // the client provided key on success.
 | 
					
						
							|  |  |  | func ParseSSECustomerHeader(header http.Header) (key []byte, err error) { | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	if !globalIsSSL { // minio only supports HTTP or HTTPS requests not both at the same time
 | 
					
						
							|  |  |  | 		// we cannot use r.TLS == nil here because Go's http implementation reflects on
 | 
					
						
							|  |  |  | 		// the net.Conn and sets the TLS field of http.Request only if it's an tls.Conn.
 | 
					
						
							|  |  |  | 		// Minio uses a BufConn (wrapping a tls.Conn) so the type check within the http package
 | 
					
						
							|  |  |  | 		// will always fail -> r.TLS is always nil even for TLS requests.
 | 
					
						
							|  |  |  | 		return nil, errInsecureSSERequest | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if crypto.S3.IsRequested(header) && crypto.SSEC.IsRequested(header) { | 
					
						
							|  |  |  | 		return key, crypto.ErrIncompatibleEncryptionMethod | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	k, err := crypto.SSEC.ParseHTTP(header) | 
					
						
							|  |  |  | 	return k[:], err | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | // This function rotates old to new key.
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func rotateKey(oldKey []byte, newKey []byte, bucket, object string, metadata map[string]string) error { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	delete(metadata, crypto.SSECKey) // make sure we do not save the key by accident
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	switch { | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return errObjectTampered | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	case crypto.SSEC.IsEncrypted(metadata): | 
					
						
							|  |  |  | 		sealedKey, err := crypto.SSEC.ParseMetadata(metadata) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-04-07 05:15:23 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		var objectKey crypto.ObjectKey | 
					
						
							|  |  |  | 		var extKey [32]byte | 
					
						
							|  |  |  | 		copy(extKey[:], oldKey) | 
					
						
							|  |  |  | 		if err = objectKey.Unseal(extKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil { | 
					
						
							|  |  |  | 			if subtle.ConstantTimeCompare(oldKey, newKey) == 1 { | 
					
						
							|  |  |  | 				return errInvalidSSEParameters // AWS returns special error for equal but invalid keys.
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return crypto.ErrInvalidCustomerKey // To provide strict AWS S3 compatibility we return: access denied.
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if subtle.ConstantTimeCompare(oldKey, newKey) == 1 && sealedKey.Algorithm == crypto.SealAlgorithm { | 
					
						
							|  |  |  | 			return nil // don't rotate on equal keys if seal algorithm is latest
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		copy(extKey[:], newKey) | 
					
						
							|  |  |  | 		sealedKey = objectKey.Seal(extKey, sealedKey.IV, crypto.SSEC.String(), bucket, object) | 
					
						
							| 
									
										
										
										
											2018-08-22 06:12:00 +08:00
										 |  |  | 		crypto.SSEC.CreateMetadata(metadata, sealedKey) | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | func newEncryptMetadata(key []byte, bucket, object string, metadata map[string]string, sseS3 bool) ([]byte, error) { | 
					
						
							|  |  |  | 	delete(metadata, crypto.SSECKey) // make sure we do not save the key by accident
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var sealedKey crypto.SealedKey | 
					
						
							|  |  |  | 	if sseS3 { | 
					
						
							|  |  |  | 		if globalKMS == nil { | 
					
						
							|  |  |  | 			return nil, errKMSNotConfigured | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		key, encKey, err := globalKMS.GenerateKey(globalKMSKeyID, crypto.Context{bucket: path.Join(bucket, object)}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-11 09:21:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		objectKey := crypto.GenerateKey(key, rand.Reader) | 
					
						
							|  |  |  | 		sealedKey = objectKey.Seal(key, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object) | 
					
						
							|  |  |  | 		crypto.S3.CreateMetadata(metadata, globalKMSKeyID, encKey, sealedKey) | 
					
						
							|  |  |  | 		return objectKey[:], nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var extKey [32]byte | 
					
						
							|  |  |  | 	copy(extKey[:], key) | 
					
						
							|  |  |  | 	objectKey := crypto.GenerateKey(extKey, rand.Reader) | 
					
						
							|  |  |  | 	sealedKey = objectKey.Seal(extKey, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object) | 
					
						
							|  |  |  | 	crypto.SSEC.CreateMetadata(metadata, sealedKey) | 
					
						
							|  |  |  | 	return objectKey[:], nil | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | func newEncryptReader(content io.Reader, key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (io.Reader, error) { | 
					
						
							|  |  |  | 	objectEncryptionKey, err := newEncryptMetadata(key, bucket, object, metadata, sseS3) | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	reader, err := sio.EncryptReader(content, sio.Config{Key: objectEncryptionKey[:], MinVersion: sio.Version20}) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		return nil, crypto.ErrInvalidCustomerKey | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return reader, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | // set new encryption metadata from http request headers for SSE-C and generated key from KMS in the case of
 | 
					
						
							|  |  |  | // SSE-S3
 | 
					
						
							|  |  |  | func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[string]string) (err error) { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		key []byte | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if crypto.SSEC.IsRequested(r.Header) { | 
					
						
							|  |  |  | 		key, err = ParseSSECustomerRequest(r) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_, err = newEncryptMetadata(key, bucket, object, metadata, crypto.S3.IsRequested(r.Header)) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | // EncryptRequest takes the client provided content and encrypts the data
 | 
					
						
							|  |  |  | // with the client provided key. It also marks the object as client-side-encrypted
 | 
					
						
							|  |  |  | // and sets the correct headers.
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, error) { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		key []byte | 
					
						
							|  |  |  | 		err error | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if crypto.S3.IsRequested(r.Header) && crypto.SSEC.IsRequested(r.Header) { | 
					
						
							|  |  |  | 		return nil, crypto.ErrIncompatibleEncryptionMethod | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if crypto.SSEC.IsRequested(r.Header) { | 
					
						
							|  |  |  | 		key, err = ParseSSECustomerRequest(r) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return newEncryptReader(content, key, bucket, object, metadata, crypto.S3.IsRequested(r.Header)) | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | // DecryptCopyRequest decrypts the object with the client provided key. It also removes
 | 
					
						
							|  |  |  | // the client-side-encryption metadata from the object and sets the correct headers.
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func DecryptCopyRequest(client io.Writer, r *http.Request, bucket, object string, metadata map[string]string) (io.WriteCloser, error) { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		key []byte | 
					
						
							|  |  |  | 		err error | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if crypto.SSECopy.IsRequested(r.Header) { | 
					
						
							|  |  |  | 		key, err = ParseSSECopyCustomerRequest(r, metadata) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	delete(metadata, crypto.SSECopyKey) // make sure we do not save the key by accident
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 	return newDecryptWriter(client, key, bucket, object, 0, metadata) | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	switch { | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, errObjectTampered | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	case crypto.S3.IsEncrypted(metadata): | 
					
						
							|  |  |  | 		if globalKMS == nil { | 
					
						
							|  |  |  | 			return nil, errKMSNotConfigured | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		extKey, err := globalKMS.UnsealKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var objectKey crypto.ObjectKey | 
					
						
							|  |  |  | 		if err = objectKey.Unseal(extKey, sealedKey, crypto.S3.String(), bucket, object); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return objectKey[:], nil | 
					
						
							|  |  |  | 	case crypto.SSEC.IsEncrypted(metadata): | 
					
						
							|  |  |  | 		var extKey [32]byte | 
					
						
							|  |  |  | 		copy(extKey[:], key) | 
					
						
							|  |  |  | 		sealedKey, err := crypto.SSEC.ParseMetadata(metadata) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var objectKey crypto.ObjectKey | 
					
						
							|  |  |  | 		if err = objectKey.Unseal(extKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return objectKey[:], nil | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func newDecryptWriter(client io.Writer, key []byte, bucket, object string, seqNumber uint32, metadata map[string]string) (io.WriteCloser, error) { | 
					
						
							|  |  |  | 	objectEncryptionKey, err := decryptObjectInfo(key, bucket, object, metadata) | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return newDecryptWriterWithObjectKey(client, objectEncryptionKey, seqNumber, metadata) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newDecryptWriterWithObjectKey(client io.Writer, objectEncryptionKey []byte, seqNumber uint32, metadata map[string]string) (io.WriteCloser, error) { | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	writer, err := sio.DecryptWriter(client, sio.Config{ | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 		Key:            objectEncryptionKey, | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 		SequenceNumber: seqNumber, | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		return nil, crypto.ErrInvalidCustomerKey | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	delete(metadata, crypto.SSEIV) | 
					
						
							|  |  |  | 	delete(metadata, crypto.SSESealAlgorithm) | 
					
						
							|  |  |  | 	delete(metadata, crypto.SSECSealedKey) | 
					
						
							|  |  |  | 	delete(metadata, crypto.SSEMultipart) | 
					
						
							|  |  |  | 	delete(metadata, crypto.S3SealedKey) | 
					
						
							|  |  |  | 	delete(metadata, crypto.S3KMSSealedKey) | 
					
						
							|  |  |  | 	delete(metadata, crypto.S3KMSKeyID) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	return writer, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | // DecryptRequestWithSequenceNumber decrypts the object with the client provided key. It also removes
 | 
					
						
							|  |  |  | // the client-side-encryption metadata from the object and sets the correct headers.
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func DecryptRequestWithSequenceNumber(client io.Writer, r *http.Request, bucket, object string, seqNumber uint32, metadata map[string]string) (io.WriteCloser, error) { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if crypto.S3.IsEncrypted(metadata) { | 
					
						
							|  |  |  | 		return newDecryptWriter(client, nil, bucket, object, seqNumber, metadata) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	key, err := ParseSSECustomerRequest(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	delete(metadata, crypto.SSECKey) // make sure we do not save the key by accident
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 	return newDecryptWriter(client, key, bucket, object, seqNumber, metadata) | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | // DecryptRequest decrypts the object with client provided key for SSE-C and SSE-S3. It also removes
 | 
					
						
							|  |  |  | // the encryption metadata from the object and sets the correct headers.
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func DecryptRequest(client io.Writer, r *http.Request, bucket, object string, metadata map[string]string) (io.WriteCloser, error) { | 
					
						
							|  |  |  | 	return DecryptRequestWithSequenceNumber(client, r, bucket, object, 0, metadata) | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | // DecryptBlocksWriter - decrypts multipart parts, while implementing a io.Writer compatible interface.
 | 
					
						
							|  |  |  | type DecryptBlocksWriter struct { | 
					
						
							|  |  |  | 	// Original writer where the plain data will be written
 | 
					
						
							|  |  |  | 	writer io.Writer | 
					
						
							|  |  |  | 	// Current decrypter for the current encrypted data block
 | 
					
						
							|  |  |  | 	decrypter io.WriteCloser | 
					
						
							|  |  |  | 	// Start sequence number
 | 
					
						
							|  |  |  | 	startSeqNum uint32 | 
					
						
							|  |  |  | 	// Current part index
 | 
					
						
							|  |  |  | 	partIndex int | 
					
						
							|  |  |  | 	// Parts information
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 	parts          []objectPartInfo | 
					
						
							|  |  |  | 	req            *http.Request | 
					
						
							|  |  |  | 	bucket, object string | 
					
						
							|  |  |  | 	metadata       map[string]string | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	partEncRelOffset int64 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	copySource bool | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	// Customer Key
 | 
					
						
							|  |  |  | 	customerKeyHeader string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *DecryptBlocksWriter) buildDecrypter(partID int) error { | 
					
						
							|  |  |  | 	m := make(map[string]string) | 
					
						
							|  |  |  | 	for k, v := range w.metadata { | 
					
						
							|  |  |  | 		m[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Initialize the first decrypter, new decrypters will be initialized in Write() operation as needed.
 | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	var key []byte | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	if w.copySource { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		if crypto.SSEC.IsEncrypted(w.metadata) { | 
					
						
							|  |  |  | 			w.req.Header.Set(crypto.SSECopyKey, w.customerKeyHeader) | 
					
						
							|  |  |  | 			key, err = ParseSSECopyCustomerRequest(w.req, w.metadata) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		if crypto.SSEC.IsEncrypted(w.metadata) { | 
					
						
							|  |  |  | 			w.req.Header.Set(crypto.SSECKey, w.customerKeyHeader) | 
					
						
							|  |  |  | 			key, err = ParseSSECustomerRequest(w.req) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 	objectEncryptionKey, err := decryptObjectInfo(key, w.bucket, w.object, m) | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var partIDbin [4]byte | 
					
						
							|  |  |  | 	binary.LittleEndian.PutUint32(partIDbin[:], uint32(partID)) // marshal part ID
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mac := hmac.New(sha256.New, objectEncryptionKey) // derive part encryption key from part ID and object key
 | 
					
						
							|  |  |  | 	mac.Write(partIDbin[:]) | 
					
						
							|  |  |  | 	partEncryptionKey := mac.Sum(nil) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	// make sure we do not save the key by accident
 | 
					
						
							|  |  |  | 	if w.copySource { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		delete(m, crypto.SSECopyKey) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		delete(m, crypto.SSECKey) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	// make sure to provide a NopCloser such that a Close
 | 
					
						
							|  |  |  | 	// on sio.decryptWriter doesn't close the underlying writer's
 | 
					
						
							|  |  |  | 	// close which perhaps can close the stream prematurely.
 | 
					
						
							|  |  |  | 	decrypter, err := newDecryptWriterWithObjectKey(ioutil.NopCloser(w.writer), partEncryptionKey, w.startSeqNum, m) | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	if w.decrypter != nil { | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 		// Pro-actively close the writer such that any pending buffers
 | 
					
						
							|  |  |  | 		// are flushed already before we allocate a new decrypter.
 | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 		err = w.decrypter.Close() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	w.decrypter = decrypter | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *DecryptBlocksWriter) Write(p []byte) (int, error) { | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	var n1 int | 
					
						
							|  |  |  | 	if int64(len(p)) < w.parts[w.partIndex].Size-w.partEncRelOffset { | 
					
						
							|  |  |  | 		n1, err = w.decrypter.Write(p) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.partEncRelOffset += int64(n1) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		n1, err = w.decrypter.Write(p[:w.parts[w.partIndex].Size-w.partEncRelOffset]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// We should now proceed to next part, reset all values appropriately.
 | 
					
						
							|  |  |  | 		w.partEncRelOffset = 0 | 
					
						
							|  |  |  | 		w.startSeqNum = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		w.partIndex++ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err = w.buildDecrypter(w.partIndex + 1) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		n1, err = w.decrypter.Write(p[n1:]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		w.partEncRelOffset += int64(n1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return len(p), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close closes the LimitWriter. It behaves like io.Closer.
 | 
					
						
							|  |  |  | func (w *DecryptBlocksWriter) Close() error { | 
					
						
							|  |  |  | 	if w.decrypter != nil { | 
					
						
							|  |  |  | 		err := w.decrypter.Close() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if closer, ok := w.writer.(io.Closer); ok { | 
					
						
							|  |  |  | 		return closer.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | // DecryptAllBlocksCopyRequest - setup a struct which can decrypt many concatenated encrypted data
 | 
					
						
							|  |  |  | // parts information helps to know the boundaries of each encrypted data block, this function decrypts
 | 
					
						
							|  |  |  | // all parts starting from part-1.
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func DecryptAllBlocksCopyRequest(client io.Writer, r *http.Request, bucket, object string, objInfo ObjectInfo) (io.WriteCloser, int64, error) { | 
					
						
							|  |  |  | 	w, _, size, err := DecryptBlocksRequest(client, r, bucket, object, 0, objInfo.Size, objInfo, true) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	return w, size, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | // DecryptBlocksRequest - setup a struct which can decrypt many concatenated encrypted data
 | 
					
						
							|  |  |  | // parts information helps to know the boundaries of each encrypted data block.
 | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | func DecryptBlocksRequest(client io.Writer, r *http.Request, bucket, object string, startOffset, length int64, objInfo ObjectInfo, copySource bool) (io.WriteCloser, int64, int64, error) { | 
					
						
							| 
									
										
										
										
											2018-07-13 02:23:32 +08:00
										 |  |  | 	var seqNumber uint32 | 
					
						
							|  |  |  | 	var encStartOffset, encLength int64 | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if len(objInfo.Parts) == 0 || !crypto.IsMultiPart(objInfo.UserDefined) { | 
					
						
							| 
									
										
										
										
											2018-07-13 02:23:32 +08:00
										 |  |  | 		seqNumber, encStartOffset, encLength = getEncryptedSinglePartOffsetLength(startOffset, length, objInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 		var writer io.WriteCloser | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		if copySource { | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 			writer, err = DecryptCopyRequest(client, r, bucket, object, objInfo.UserDefined) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 			writer, err = DecryptRequestWithSequenceNumber(client, r, bucket, object, seqNumber, objInfo.UserDefined) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, 0, 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return writer, encStartOffset, encLength, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-13 02:23:32 +08:00
										 |  |  | 	seqNumber, encStartOffset, encLength = getEncryptedMultipartsOffsetLength(startOffset, length, objInfo) | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	var partStartIndex int | 
					
						
							|  |  |  | 	var partStartOffset = startOffset | 
					
						
							|  |  |  | 	// Skip parts until final offset maps to a particular part offset.
 | 
					
						
							|  |  |  | 	for i, part := range objInfo.Parts { | 
					
						
							| 
									
										
										
										
											2018-03-20 00:48:12 +08:00
										 |  |  | 		decryptedSize, err := sio.DecryptedSize(uint64(part.Size)) | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-20 00:48:12 +08:00
										 |  |  | 			return nil, -1, -1, errObjectTampered | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		partStartIndex = i | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Offset is smaller than size we have reached the
 | 
					
						
							|  |  |  | 		// proper part offset, break out we start from
 | 
					
						
							|  |  |  | 		// this part index.
 | 
					
						
							| 
									
										
										
										
											2018-03-20 00:48:12 +08:00
										 |  |  | 		if partStartOffset < int64(decryptedSize) { | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Continue to look for next part.
 | 
					
						
							| 
									
										
										
										
											2018-03-20 00:48:12 +08:00
										 |  |  | 		partStartOffset -= int64(decryptedSize) | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	startSeqNum := partStartOffset / sseDAREPackageBlockSize | 
					
						
							|  |  |  | 	partEncRelOffset := int64(startSeqNum) * (sseDAREPackageBlockSize + sseDAREPackageMetaSize) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w := &DecryptBlocksWriter{ | 
					
						
							|  |  |  | 		writer:            client, | 
					
						
							|  |  |  | 		startSeqNum:       uint32(startSeqNum), | 
					
						
							|  |  |  | 		partEncRelOffset:  partEncRelOffset, | 
					
						
							|  |  |  | 		parts:             objInfo.Parts, | 
					
						
							|  |  |  | 		partIndex:         partStartIndex, | 
					
						
							|  |  |  | 		req:               r, | 
					
						
							| 
									
										
										
										
											2018-07-10 08:18:28 +08:00
										 |  |  | 		bucket:            bucket, | 
					
						
							|  |  |  | 		object:            object, | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		customerKeyHeader: r.Header.Get(crypto.SSECKey), | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 		copySource:        copySource, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.metadata = map[string]string{} | 
					
						
							|  |  |  | 	// Copy encryption metadata for internal use.
 | 
					
						
							|  |  |  | 	for k, v := range objInfo.UserDefined { | 
					
						
							|  |  |  | 		w.metadata[k] = v | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	// Purge all the encryption headers.
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	delete(objInfo.UserDefined, crypto.SSEIV) | 
					
						
							|  |  |  | 	delete(objInfo.UserDefined, crypto.SSESealAlgorithm) | 
					
						
							|  |  |  | 	delete(objInfo.UserDefined, crypto.SSECSealedKey) | 
					
						
							|  |  |  | 	delete(objInfo.UserDefined, crypto.SSEMultipart) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if crypto.S3.IsEncrypted(objInfo.UserDefined) { | 
					
						
							|  |  |  | 		delete(objInfo.UserDefined, crypto.S3SealedKey) | 
					
						
							|  |  |  | 		delete(objInfo.UserDefined, crypto.S3KMSKeyID) | 
					
						
							|  |  |  | 		delete(objInfo.UserDefined, crypto.S3KMSSealedKey) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	if w.copySource { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		w.customerKeyHeader = r.Header.Get(crypto.SSECopyKey) | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-13 02:23:32 +08:00
										 |  |  | 	if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-03 09:24:02 +08:00
										 |  |  | 		return nil, 0, 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return w, encStartOffset, encLength, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-13 02:23:32 +08:00
										 |  |  | // getEncryptedMultipartsOffsetLength - fetch sequence number, encrypted start offset and encrypted length.
 | 
					
						
							|  |  |  | func getEncryptedMultipartsOffsetLength(offset, length int64, obj ObjectInfo) (uint32, int64, int64) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Calculate encrypted offset of a multipart object
 | 
					
						
							|  |  |  | 	computeEncOffset := func(off int64, obj ObjectInfo) (seqNumber uint32, encryptedOffset int64, err error) { | 
					
						
							|  |  |  | 		var curPartEndOffset uint64 | 
					
						
							|  |  |  | 		var prevPartsEncSize int64 | 
					
						
							|  |  |  | 		for _, p := range obj.Parts { | 
					
						
							|  |  |  | 			size, decErr := sio.DecryptedSize(uint64(p.Size)) | 
					
						
							|  |  |  | 			if decErr != nil { | 
					
						
							|  |  |  | 				err = errObjectTampered // assign correct error type
 | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if off < int64(curPartEndOffset+size) { | 
					
						
							|  |  |  | 				seqNumber, encryptedOffset, _ = getEncryptedSinglePartOffsetLength(off-int64(curPartEndOffset), 1, obj) | 
					
						
							|  |  |  | 				encryptedOffset += int64(prevPartsEncSize) | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			curPartEndOffset += size | 
					
						
							|  |  |  | 			prevPartsEncSize += p.Size | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Calculate the encrypted start offset corresponding to the plain offset
 | 
					
						
							|  |  |  | 	seqNumber, encStartOffset, _ := computeEncOffset(offset, obj) | 
					
						
							|  |  |  | 	// Calculate also the encrypted end offset corresponding to plain offset + plain length
 | 
					
						
							|  |  |  | 	_, encEndOffset, _ := computeEncOffset(offset+length-1, obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// encLength is the diff between encrypted end offset and encrypted start offset + one package size
 | 
					
						
							|  |  |  | 	// to ensure all encrypted data are covered
 | 
					
						
							|  |  |  | 	encLength := encEndOffset - encStartOffset + (64*1024 + 32) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Calculate total size of all parts
 | 
					
						
							|  |  |  | 	var totalPartsLength int64 | 
					
						
							|  |  |  | 	for _, p := range obj.Parts { | 
					
						
							|  |  |  | 		totalPartsLength += p.Size | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set encLength to maximum possible value if it exceeded total parts size
 | 
					
						
							|  |  |  | 	if encLength+encStartOffset > totalPartsLength { | 
					
						
							|  |  |  | 		encLength = totalPartsLength - encStartOffset | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return seqNumber, encStartOffset, encLength | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getEncryptedSinglePartOffsetLength - fetch sequence number, encrypted start offset and encrypted length.
 | 
					
						
							|  |  |  | func getEncryptedSinglePartOffsetLength(offset, length int64, objInfo ObjectInfo) (seqNumber uint32, encOffset int64, encLength int64) { | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	onePkgSize := int64(sseDAREPackageBlockSize + sseDAREPackageMetaSize) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	seqNumber = uint32(offset / sseDAREPackageBlockSize) | 
					
						
							|  |  |  | 	encOffset = int64(seqNumber) * onePkgSize | 
					
						
							|  |  |  | 	// The math to compute the encrypted length is always
 | 
					
						
							|  |  |  | 	// originalLength i.e (offset+length-1) to be divided under
 | 
					
						
							|  |  |  | 	// 64KiB blocks which is the payload size for each encrypted
 | 
					
						
							|  |  |  | 	// block. This is then multiplied by final package size which
 | 
					
						
							|  |  |  | 	// is basically 64KiB + 32. Finally negate the encrypted offset
 | 
					
						
							|  |  |  | 	// to get the final encrypted length on disk.
 | 
					
						
							|  |  |  | 	encLength = ((offset+length)/sseDAREPackageBlockSize)*onePkgSize - encOffset | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check for the remainder, to figure if we need one extract package to read from.
 | 
					
						
							|  |  |  | 	if (offset+length)%sseDAREPackageBlockSize > 0 { | 
					
						
							|  |  |  | 		encLength += onePkgSize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-13 02:23:32 +08:00
										 |  |  | 	if encLength+encOffset > objInfo.EncryptedSize() { | 
					
						
							|  |  |  | 		encLength = objInfo.EncryptedSize() - encOffset | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-02 03:37:57 +08:00
										 |  |  | 	return seqNumber, encOffset, encLength | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | // DecryptedSize returns the size of the object after decryption in bytes.
 | 
					
						
							|  |  |  | // It returns an error if the object is not encrypted or marked as encrypted
 | 
					
						
							|  |  |  | // but has an invalid size.
 | 
					
						
							|  |  |  | func (o *ObjectInfo) DecryptedSize() (int64, error) { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if !crypto.IsEncrypted(o.UserDefined) { | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 		return 0, errors.New("Cannot compute decrypted size of an unencrypted object") | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if len(o.Parts) == 0 || !crypto.IsMultiPart(o.UserDefined) { | 
					
						
							| 
									
										
										
										
											2018-07-13 00:29:56 +08:00
										 |  |  | 		size, err := sio.DecryptedSize(uint64(o.Size)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = errObjectTampered // assign correct error type
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return int64(size), err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var size int64 | 
					
						
							|  |  |  | 	for _, part := range o.Parts { | 
					
						
							|  |  |  | 		partSize, err := sio.DecryptedSize(uint64(part.Size)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, errObjectTampered | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		size += int64(partSize) | 
					
						
							| 
									
										
										
										
											2018-03-20 00:48:12 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-13 00:29:56 +08:00
										 |  |  | 	return size, nil | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // EncryptedSize returns the size of the object after encryption.
 | 
					
						
							|  |  |  | // An encrypted object is always larger than a plain object
 | 
					
						
							|  |  |  | // except for zero size objects.
 | 
					
						
							|  |  |  | func (o *ObjectInfo) EncryptedSize() int64 { | 
					
						
							| 
									
										
										
										
											2018-03-20 00:48:12 +08:00
										 |  |  | 	size, err := sio.EncryptedSize(uint64(o.Size)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 		// This cannot happen since AWS S3 allows parts to be 5GB at most
 | 
					
						
							|  |  |  | 		// sio max. size is 256 TB
 | 
					
						
							|  |  |  | 		reqInfo := (&logger.ReqInfo{}).AppendTags("size", strconv.FormatUint(size, 10)) | 
					
						
							|  |  |  | 		ctx := logger.SetReqInfo(context.Background(), reqInfo) | 
					
						
							|  |  |  | 		logger.CriticalIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-20 00:48:12 +08:00
										 |  |  | 	return int64(size) | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | // DecryptCopyObjectInfo tries to decrypt the provided object if it is encrypted.
 | 
					
						
							|  |  |  | // It fails if the object is encrypted and the HTTP headers don't contain
 | 
					
						
							|  |  |  | // SSE-C headers or the object is not encrypted but SSE-C headers are provided. (AWS behavior)
 | 
					
						
							|  |  |  | // DecryptObjectInfo returns 'ErrNone' if the object is not encrypted or the
 | 
					
						
							|  |  |  | // decryption succeeded.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // DecryptCopyObjectInfo also returns whether the object is encrypted or not.
 | 
					
						
							|  |  |  | func DecryptCopyObjectInfo(info *ObjectInfo, headers http.Header) (apiErr APIErrorCode, encrypted bool) { | 
					
						
							|  |  |  | 	// Directories are never encrypted.
 | 
					
						
							|  |  |  | 	if info.IsDir { | 
					
						
							|  |  |  | 		return ErrNone, false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	if apiErr, encrypted = ErrNone, crypto.IsEncrypted(info.UserDefined); !encrypted && crypto.SSECopy.IsRequested(headers) { | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 		apiErr = ErrInvalidEncryptionParameters | 
					
						
							|  |  |  | 	} else if encrypted { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		if (!crypto.SSECopy.IsRequested(headers) && crypto.SSEC.IsEncrypted(info.UserDefined)) || | 
					
						
							|  |  |  | 			(crypto.SSECopy.IsRequested(headers) && crypto.S3.IsEncrypted(info.UserDefined)) { | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 			apiErr = ErrSSEEncryptedObject | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		if info.Size, err = info.DecryptedSize(); err != nil { | 
					
						
							|  |  |  | 			apiErr = toAPIErrorCode(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | // DecryptObjectInfo tries to decrypt the provided object if it is encrypted.
 | 
					
						
							|  |  |  | // It fails if the object is encrypted and the HTTP headers don't contain
 | 
					
						
							|  |  |  | // SSE-C headers or the object is not encrypted but SSE-C headers are provided. (AWS behavior)
 | 
					
						
							|  |  |  | // DecryptObjectInfo returns 'ErrNone' if the object is not encrypted or the
 | 
					
						
							|  |  |  | // decryption succeeded.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // DecryptObjectInfo also returns whether the object is encrypted or not.
 | 
					
						
							| 
									
										
										
										
											2018-08-24 22:56:24 +08:00
										 |  |  | func DecryptObjectInfo(info *ObjectInfo, headers http.Header) (encrypted bool, err error) { | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	// Directories are never encrypted.
 | 
					
						
							|  |  |  | 	if info.IsDir { | 
					
						
							| 
									
										
										
										
											2018-08-24 22:56:24 +08:00
										 |  |  | 		return false, nil | 
					
						
							| 
									
										
										
										
											2018-02-24 07:07:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 	// disallow X-Amz-Server-Side-Encryption header on HEAD and GET
 | 
					
						
							|  |  |  | 	if crypto.S3.IsRequested(headers) { | 
					
						
							| 
									
										
										
										
											2018-08-24 22:56:24 +08:00
										 |  |  | 		err = errInvalidEncryptionParameters | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-24 22:56:24 +08:00
										 |  |  | 	if err, encrypted = nil, crypto.IsEncrypted(info.UserDefined); !encrypted && crypto.SSEC.IsRequested(headers) { | 
					
						
							|  |  |  | 		err = errInvalidEncryptionParameters | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} else if encrypted { | 
					
						
							| 
									
										
										
										
											2018-08-18 03:52:14 +08:00
										 |  |  | 		if (crypto.SSEC.IsEncrypted(info.UserDefined) && !crypto.SSEC.IsRequested(headers)) || | 
					
						
							|  |  |  | 			(crypto.S3.IsEncrypted(info.UserDefined) && crypto.SSEC.IsRequested(headers)) { | 
					
						
							| 
									
										
										
										
											2018-08-24 22:56:24 +08:00
										 |  |  | 			err = errEncryptedObject | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-24 22:56:24 +08:00
										 |  |  | 		info.Size, err = info.DecryptedSize() | 
					
						
							| 
									
										
										
										
											2017-11-08 07:18:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } |