From d07fb41fe80abcb68dc595c676b121c0102598b6 Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Mon, 5 Nov 2018 19:26:10 +0100 Subject: [PATCH] add key-rotation for SSE-S3 objects (#6755) This commit adds key-rotation for SSE-S3 objects. To execute a key-rotation a SSE-S3 client must - specify the `X-Amz-Server-Side-Encryption: AES256` header for the destination - The source == destination for the COPY operation. Fixes #6754 --- cmd/encryption-v1.go | 24 ++++++++++++++++++++++++ cmd/object-handlers.go | 25 +++++++++++++++---------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index 401efd2b9..0dfa2bf81 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -156,6 +156,30 @@ func rotateKey(oldKey []byte, newKey []byte, bucket, object string, metadata map sealedKey = objectKey.Seal(extKey, sealedKey.IV, crypto.SSEC.String(), bucket, object) crypto.SSEC.CreateMetadata(metadata, sealedKey) return nil + case crypto.S3.IsEncrypted(metadata): + if globalKMS == nil { + return errKMSNotConfigured + } + keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata) + if err != nil { + return err + } + oldKey, err := globalKMS.UnsealKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)}) + if err != nil { + return err + } + var objectKey crypto.ObjectKey + if err = objectKey.Unseal(oldKey, sealedKey, crypto.S3.String(), bucket, object); err != nil { + return err + } + + newKey, encKey, err := globalKMS.GenerateKey(globalKMSKeyID, crypto.Context{bucket: path.Join(bucket, object)}) + if err != nil { + return err + } + sealedKey = objectKey.Seal(newKey, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object) + crypto.S3.CreateMetadata(metadata, globalKMSKeyID, encKey, sealedKey) + return nil } } diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 632bd5cfc..d394f1eb4 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -829,19 +829,24 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re return } } - // AWS S3 implementation requires us to only rotate keys - // when/ both keys are provided and destination is same - // otherwise we proceed to encrypt/decrypt. - if sseCopyC && sseC && cpSrcDstSame { - // Get the old key which needs to be rotated. - oldKey, err = ParseSSECopyCustomerRequest(r.Header, srcInfo.UserDefined) - if err != nil { - writeErrorResponse(w, toAPIErrorCode(err), r.URL) - return + + // If src == dst and either + // - the object is encrypted using SSE-C and two different SSE-C keys are present + // - the object is encrypted using SSE-S3 and the SSE-S3 header is present + // than execute a key rotation. + if cpSrcDstSame && ((sseCopyC && sseC) || (sseS3 && sseCopyS3)) { + if sseCopyC && sseC { + oldKey, err = ParseSSECopyCustomerRequest(r.Header, srcInfo.UserDefined) + if err != nil { + writeErrorResponse(w, toAPIErrorCode(err), r.URL) + return + } } for k, v := range srcInfo.UserDefined { encMetadata[k] = v } + + // In case of SSE-S3 oldKey and newKey aren't used - the KMS manages the keys. if err = rotateKey(oldKey, newKey, srcBucket, srcObject, encMetadata); err != nil { writeErrorResponse(w, toAPIErrorCode(err), r.URL) return @@ -914,7 +919,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re // metadataOnly is true indicating that we are not overwriting the object. // if encryption is enabled we do not need explicit "REPLACE" metadata to // be enabled as well - this is to allow for key-rotation. - if !isMetadataReplace(r.Header) && srcInfo.metadataOnly && !crypto.SSEC.IsEncrypted(srcInfo.UserDefined) { + if !isMetadataReplace(r.Header) && srcInfo.metadataOnly && !crypto.IsEncrypted(srcInfo.UserDefined) { // If x-amz-metadata-directive is not set to REPLACE then we need // to error out if source and destination are same. writeErrorResponse(w, ErrInvalidCopyDest, r.URL)