From 91bc452b9aea9d08076bf1c4abd7ee98ed7e90a1 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Sat, 29 Aug 2026 13:51:20 +0200 Subject: [PATCH] internal/stream: reject negative and overflowing encrypted sizes --- internal/stream/stream.go | 4 ++++ internal/stream/stream_test.go | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/internal/stream/stream.go b/internal/stream/stream.go index a2fc532..58fd9fa 100644 --- a/internal/stream/stream.go +++ b/internal/stream/stream.go @@ -12,6 +12,7 @@ import ( "errors" "fmt" "io" + "math" "sync/atomic" "golang.org/x/crypto/chacha20poly1305" @@ -20,6 +21,9 @@ import ( const ChunkSize = 64 * 1024 func EncryptedChunkCount(encryptedSize int64) (int64, error) { + if encryptedSize < 0 || encryptedSize > math.MaxInt64-encChunkSize+1 { + return 0, fmt.Errorf("invalid encrypted payload size: %d", encryptedSize) + } chunks := (encryptedSize + encChunkSize - 1) / encChunkSize plaintextSize := encryptedSize - chunks*chacha20poly1305.Overhead diff --git a/internal/stream/stream_test.go b/internal/stream/stream_test.go index c5ef40f..bf955c0 100644 --- a/internal/stream/stream_test.go +++ b/internal/stream/stream_test.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "io" + "math" "testing" "testing/iotest" @@ -631,6 +632,17 @@ func TestDecryptReaderAtInvalidSize(t *testing.T) { if err == nil { t.Error("invalid size (empty final chunk): expected error, got nil") } + + // Negative sizes and sizes that would overflow the chunk count computation + // must be rejected with an error rather than a panic. + for _, size := range []int64{-1, -70000, math.MaxInt64, math.MaxInt64 - 200, math.MaxInt64 - 65550} { + if _, err := stream.EncryptedChunkCount(size); err == nil { + t.Errorf("EncryptedChunkCount(%d): expected error, got nil", size) + } + if _, err := stream.NewDecryptReaderAt(key, bytes.NewReader(ciphertext), size); err == nil { + t.Errorf("NewDecryptReaderAt(%d): expected error, got nil", size) + } + } } func TestDecryptReaderAtTruncated(t *testing.T) {