fix size computation for en/decrypted objects (#6147)

This PR fixes the size calculation for encrypted multipart
objects.
master
Andreas Auernhammer 6 years ago committed by kannappanr
parent b11a8eb3f4
commit adf7340394
  1. 70
      cmd/encryption-v1.go
  2. 13
      cmd/object-handlers.go

@ -640,15 +640,12 @@ func DecryptAllBlocksCopyRequest(client io.Writer, r *http.Request, bucket, obje
// DecryptBlocksRequest - setup a struct which can decrypt many concatenated encrypted data
// parts information helps to know the boundaries of each encrypted data block.
func DecryptBlocksRequest(client io.Writer, r *http.Request, bucket, object string, startOffset, length int64, objInfo ObjectInfo, copySource bool) (io.WriteCloser, int64, int64, error) {
seqNumber, encStartOffset, encLength := getEncryptedStartOffset(startOffset, length)
// Encryption length cannot be bigger than the file size, if it is
// which is allowed in AWS S3, we simply default to EncryptedSize().
if encLength+encStartOffset > objInfo.EncryptedSize() {
encLength = objInfo.EncryptedSize() - encStartOffset
}
var seqNumber uint32
var encStartOffset, encLength int64
if len(objInfo.Parts) == 0 || !objInfo.IsEncryptedMultipart() {
seqNumber, encStartOffset, encLength = getEncryptedSinglePartOffsetLength(startOffset, length, objInfo)
var writer io.WriteCloser
var err error
if copySource {
@ -662,6 +659,7 @@ func DecryptBlocksRequest(client io.Writer, r *http.Request, bucket, object stri
return writer, encStartOffset, encLength, nil
}
seqNumber, encStartOffset, encLength = getEncryptedMultipartsOffsetLength(startOffset, length, objInfo)
var partStartIndex int
var partStartOffset = startOffset
// Skip parts until final offset maps to a particular part offset.
@ -716,15 +714,62 @@ func DecryptBlocksRequest(client io.Writer, r *http.Request, bucket, object stri
w.customerKeyHeader = r.Header.Get(SSECopyCustomerKey)
}
if err := w.buildDecrypter(partStartIndex + 1); err != nil {
if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil {
return nil, 0, 0, err
}
return w, encStartOffset, encLength, nil
}
// getEncryptedStartOffset - fetch sequence number, encrypted start offset and encrypted length.
func getEncryptedStartOffset(offset, length int64) (seqNumber uint32, encOffset int64, encLength int64) {
// 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) {
onePkgSize := int64(sseDAREPackageBlockSize + sseDAREPackageMetaSize)
seqNumber = uint32(offset / sseDAREPackageBlockSize)
@ -742,6 +787,9 @@ func getEncryptedStartOffset(offset, length int64) (seqNumber uint32, encOffset
encLength += onePkgSize
}
if encLength+encOffset > objInfo.EncryptedSize() {
encLength = objInfo.EncryptedSize() - encOffset
}
return seqNumber, encOffset, encLength
}
@ -786,7 +834,7 @@ func (o *ObjectInfo) DecryptedSize() (int64, error) {
if !o.IsEncrypted() {
return 0, errors.New("Cannot compute decrypted size of an unencrypted object")
}
if len(o.Parts) == 0 {
if len(o.Parts) == 0 || !o.IsEncryptedMultipart() {
size, err := sio.DecryptedSize(uint64(o.Size))
if err != nil {
err = errObjectTampered // assign correct error type

@ -1038,6 +1038,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
var writer io.WriteCloser = pipeWriter
var reader io.Reader = pipeReader
var getLength = length
srcInfo.Reader, err = hash.NewReader(reader, length, "", "")
if err != nil {
pipeWriter.CloseWithError(err)
@ -1057,7 +1058,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
// Response writer should be limited early on for decryption upto required length,
// additionally also skipping mod(offset)64KiB boundaries.
writer = ioutil.LimitedWriter(writer, startOffset%(64*1024), length)
writer, startOffset, length, err = DecryptBlocksRequest(writer, r, srcBucket, srcObject, startOffset, length, srcInfo, true)
writer, startOffset, getLength, err = DecryptBlocksRequest(writer, r, srcBucket, srcObject, startOffset, length, srcInfo, true)
if err != nil {
pipeWriter.CloseWithError(err)
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
@ -1098,12 +1099,8 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
return
}
size := length
if !sseCopyC {
info := ObjectInfo{Size: length}
size = info.EncryptedSize()
}
info := ObjectInfo{Size: length}
size := info.EncryptedSize()
srcInfo.Reader, err = hash.NewReader(reader, size, "", "")
if err != nil {
pipeWriter.CloseWithError(err)
@ -1117,7 +1114,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
// Copy source object to destination, if source and destination
// object is same then only metadata is updated.
partInfo, err := objectAPI.CopyObjectPart(ctx, srcBucket, srcObject, dstBucket,
dstObject, uploadID, partID, startOffset, length, srcInfo)
dstObject, uploadID, partID, startOffset, getLength, srcInfo)
if err != nil {
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return

Loading…
Cancel
Save