fix: honor SSE-C chunk offsets in decryption for large chunked uploads (#8216)
* fix: honor SSE-C chunk offsets in decryption for large chunked uploads Fixes issue #8215 where SSE-C decryption for large objects could corrupt data by ignoring per-chunk PartOffset values. Changes: - Add TestSSECLargeObjectChunkReassembly unit test to verify correct decryption of 19MB object split into 8MB chunks using PartOffset - Update decryptSSECChunkView and createMultipartSSECDecryptedReaderDirect to extract PartOffset from SSE-C metadata and pass to CreateSSECDecryptedReaderWithOffset for offset-aware decryption - Fix createCTRStreamWithOffset to use calculateIVWithOffset for proper block-aligned counter advancement, matching SSE-KMS/S3 behavior - Update comments to clarify SSE-C IV handling uses per-chunk offsets (unlike base IV approach used by KMS/S3) All tests pass: go test ./weed/s3api ✓ * fix: close chunkReader on error paths in createMultipartSSECDecryptedReader Address resource leak issue reported in PR #8216: ensure chunkReader is properly closed before returning on all error paths, including: - DeserializeSSECMetadata failures - IV decoding errors - Invalid PartOffset values - SSE-C reader creation failures - Missing per-chunk metadata This prevents leaking network connections and file handles during SSE-C multipart decryption error scenarios. * docs: clarify SSE-C IV handling in decryptSSECChunkView comment Replace misleading warning 'Do NOT call calculateIVWithOffset' with accurate explanation that: - CreateSSECDecryptedReaderWithOffset internally uses calculateIVWithOffset to advance the CTR counter to reach PartOffset - calculateIVWithOffset is applied only to the per-part IV, NOT to derive a global base IV for all parts - This differs fundamentally from SSE-KMS/SSE-S3 which use base IV + calculateIVWithOffset(ChunkOffset) This clarifies the IV advancement mechanism while contrasting it with the base IV approach used by other encryption schemes.
This commit is contained in:
@@ -263,18 +263,13 @@ func CreateSSECDecryptedReaderWithOffset(r io.Reader, customerKey *SSECustomerKe
|
||||
|
||||
// createCTRStreamWithOffset creates a CTR stream positioned at a specific counter offset
|
||||
func createCTRStreamWithOffset(block cipher.Block, iv []byte, counterOffset uint64) cipher.Stream {
|
||||
// Create a copy of the IV to avoid modifying the original
|
||||
offsetIV := make([]byte, len(iv))
|
||||
copy(offsetIV, iv)
|
||||
|
||||
// Calculate the counter offset in blocks (AES block size is 16 bytes)
|
||||
blockOffset := counterOffset / 16
|
||||
|
||||
// Add the block offset to the counter portion of the IV
|
||||
// In AES-CTR, the last 8 bytes of the IV are typically used as the counter
|
||||
addCounterToIV(offsetIV, blockOffset)
|
||||
|
||||
return cipher.NewCTR(block, offsetIV)
|
||||
adjustedIV, skip := calculateIVWithOffset(iv, int64(counterOffset))
|
||||
stream := cipher.NewCTR(block, adjustedIV)
|
||||
if skip > 0 {
|
||||
dummy := make([]byte, skip)
|
||||
stream.XORKeyStream(dummy, dummy)
|
||||
}
|
||||
return stream
|
||||
}
|
||||
|
||||
// addCounterToIV adds a counter value to the IV (treating last 8 bytes as big-endian counter)
|
||||
|
||||
Reference in New Issue
Block a user