diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index 20f074797..ab058e0ab 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -99,9 +99,9 @@ func kmsKeyIDFromMetadata(metadata map[string]string) string { // DecryptETags dectypts all ObjectInfo ETags, if encrypted, using the KMS. func DecryptETags(ctx context.Context, KMS kms.KMS, objects []ObjectInfo, batchSize int) error { var ( - metadata []map[string]string - buckets []string - names []string + metadata = make([]map[string]string, 0, batchSize) + buckets = make([]string, 0, batchSize) + names = make([]string, 0, batchSize) ) for len(objects) > 0 { var N int @@ -111,14 +111,20 @@ func DecryptETags(ctx context.Context, KMS kms.KMS, objects []ObjectInfo, batchS N = batchSize } - SSES3Batch := true + // We have to conntect the KMS only if there is at least + // one SSE-S3 single-part object. SSE-C and SSE-KMS objects + // don't return the plaintext MD5 ETag and the ETag of + // SSE-S3 multipart objects is not encrypted. + // Therefore, we can skip the expensive KMS calls whenever + // there is no single-part SSE-S3 object entirely. + var containsSSES3SinglePart bool for _, object := range objects[:N] { - if kind, ok := crypto.IsEncrypted(object.UserDefined); !ok || kind != crypto.S3 { - SSES3Batch = false + if kind, ok := crypto.IsEncrypted(object.UserDefined); ok && kind == crypto.S3 && !crypto.IsMultiPart(object.UserDefined) { + containsSSES3SinglePart = true break } } - if !SSES3Batch { + if !containsSSES3SinglePart { for i := range objects[:N] { size, err := objects[i].GetActualSize() if err != nil { @@ -131,33 +137,38 @@ func DecryptETags(ctx context.Context, KMS kms.KMS, objects []ObjectInfo, batchS continue } - // Now, decrypt all ETags using the a specialized bulk decryption API, if available. - // We check the cap of all slices first, to avoid allocating them over and over again. - if cap(metadata) >= N { - metadata = metadata[:0:N] - } else { - metadata = make([]map[string]string, 0, N) - } - if cap(buckets) >= N { - buckets = buckets[:0:N] - } else { - buckets = make([]string, 0, N) - } - if cap(names) >= N { - names = names[:0:N] - } else { - names = make([]string, 0, N) - } - for _, object := range objects[:N] { - metadata = append(metadata, object.UserDefined) - buckets = append(buckets, object.Bucket) - names = append(names, object.Name) + // Now, there are some SSE-S3 single-part objects. + // We only request the decryption keys for them. + // We don't want to get the decryption keys for multipart + // or non-SSE-S3 objects. + // + // Therefore, we keep a map of indicies to remember which + // object was an SSE-S3 single-part object. + // Then we request the decryption keys for these objects. + // Finally, we decrypt the ETags of these objects using + // the decryption keys. + // However, we must also adjust the size and ETags of all + // objects (not just the SSE-S3 single part objects). + // For example, the ETag of SSE-KMS objects are random values + // and the size of an SSE-KMS object must be adjusted as well. + SSES3Objects := make(map[int]bool, 10) + metadata = metadata[:0:N] + buckets = buckets[:0:N] + names = names[:0:N] + for i, object := range objects[:N] { + if kind, ok := crypto.IsEncrypted(object.UserDefined); ok && kind == crypto.S3 && !crypto.IsMultiPart(object.UserDefined) { + metadata = append(metadata, object.UserDefined) + buckets = append(buckets, object.Bucket) + names = append(names, object.Name) + + SSES3Objects[i] = true + } } keys, err := crypto.S3.UnsealObjectKeys(KMS, metadata, buckets, names) if err != nil { return err } - + var keyIndex int for i := range objects[:N] { size, err := objects[i].GetActualSize() if err != nil { @@ -165,17 +176,22 @@ func DecryptETags(ctx context.Context, KMS kms.KMS, objects []ObjectInfo, batchS } objects[i].Size = size - ETag, err := etag.Parse(objects[i].ETag) - if err != nil { - return err - } - if ETag.IsEncrypted() { - tag, err := keys[i].UnsealETag(ETag) + if !SSES3Objects[i] { + objects[i].ETag = objects[i].GetActualETag(nil) + } else { + ETag, err := etag.Parse(objects[i].ETag) if err != nil { return err } - ETag = etag.ETag(tag) - objects[i].ETag = ETag.String() + if ETag.IsEncrypted() { + tag, err := keys[keyIndex].UnsealETag(ETag) + if err != nil { + return err + } + ETag = etag.ETag(tag) + objects[i].ETag = ETag.String() + } + keyIndex++ } } objects = objects[N:]