From 38978eb2aac6312825b51068f5d34435bdcfa769 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Thu, 8 Nov 2018 10:27:21 -0800 Subject: [PATCH] Avoid decrypting encrypted multipart final size (#6776) Multipart object final size is not a contiguous encrypted object representation, so trying to decrypt this size will lead to an error in some cases. The multipart object should be detected first and then decoded with its respective parts instead. This PR handles this situation properly, added a test as well to detect these in the future. --- cmd/encryption-v1.go | 25 +++++++++------- cmd/encryption-v1_test.go | 60 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index 0dfa2bf81..c3e5604be 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -930,26 +930,29 @@ func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, sk } // Assemble slice of (decrypted) part sizes in `sizes` + var sizes []int64 var decObjSize int64 // decrypted total object size - var partSize uint64 - partSize, err = sio.DecryptedSize(uint64(o.Size)) - if err != nil { - return - } - sizes := []int64{int64(partSize)} - decObjSize = sizes[0] if isEncryptedMultipart(*o) { sizes = make([]int64, len(o.Parts)) - decObjSize = 0 for i, part := range o.Parts { + var partSize uint64 partSize, err = sio.DecryptedSize(uint64(part.Size)) if err != nil { + err = errObjectTampered return } - t := int64(partSize) - sizes[i] = t - decObjSize += t + sizes[i] = int64(partSize) + decObjSize += int64(partSize) + } + } else { + var partSize uint64 + partSize, err = sio.DecryptedSize(uint64(o.Size)) + if err != nil { + err = errObjectTampered + return } + sizes = []int64{int64(partSize)} + decObjSize = sizes[0] } var off, length int64 diff --git a/cmd/encryption-v1_test.go b/cmd/encryption-v1_test.go index f2af329f6..9cd4221d7 100644 --- a/cmd/encryption-v1_test.go +++ b/cmd/encryption-v1_test.go @@ -334,6 +334,66 @@ func TestDecryptObjectInfo(t *testing.T) { } } +// Tests for issue reproduced when getting the right encrypted +// offset of the object. +func TestGetDecryptedRange_Issue50(t *testing.T) { + rs, err := parseRequestRangeSpec("bytes=594870256-594870263") + if err != nil { + t.Fatal(err) + } + + objInfo := ObjectInfo{ + Bucket: "bucket", + Name: "object", + Size: 595160760, + UserDefined: map[string]string{ + crypto.SSEMultipart: "", + crypto.SSEIV: "HTexa=", + crypto.SSESealAlgorithm: "DAREv2-HMAC-SHA256", + crypto.SSECSealedKey: "IAA8PGAA==", + ReservedMetadataPrefix + "actual-size": "594870264", + "content-type": "application/octet-stream", + "etag": "166b1545b4c1535294ee0686678bea8c-2", + }, + Parts: []objectPartInfo{ + { + Number: 1, + Name: "part.1", + ETag: "etag1", + Size: 297580380, + ActualSize: 297435132, + }, + { + Number: 2, + Name: "part.2", + ETag: "etag2", + Size: 297580380, + ActualSize: 297435132, + }, + }, + } + + encOff, encLength, skipLen, seqNumber, partStart, err := objInfo.GetDecryptedRange(rs) + if err != nil { + t.Fatalf("Test: failed %s", err) + } + if encOff != 595127964 { + t.Fatalf("Test: expected %d, got %d", 595127964, encOff) + } + if encLength != 32796 { + t.Fatalf("Test: expected %d, got %d", 32796, encLength) + } + if skipLen != 32756 { + t.Fatalf("Test: expected %d, got %d", 32756, skipLen) + } + if seqNumber != 4538 { + t.Fatalf("Test: expected %d, got %d", 4538, seqNumber) + } + if partStart != 1 { + t.Fatalf("Test: expected %d, got %d", 1, partStart) + } +} + func TestGetDecryptedRange(t *testing.T) { var ( pkgSz = int64(64) * humanize.KiByte