From 2ba65f1f266803d692b0c2b8cf5e16b5a750638d Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Sat, 31 Jan 2026 23:56:04 +0300 Subject: [PATCH] libstore/s3-binary-cache-store: Add Content-MD5 header as message integrity check aws-sdk-cpp used to include a checksum for uploads (CRC64 since ~September 2025). Content-MD5 [1] should be universally supported by all s3 compatible services, since the SDK used to include it unconditionally too. [1]: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html --- src/libstore/s3-binary-cache-store.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index d2e964edcf4..2a3be047463 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -138,6 +138,20 @@ void S3BinaryCacheStore::upsertFile( if (auto storageClass = s3Config->storageClass.get()) { uploadHeaders.emplace_back("x-amz-storage-class", *storageClass); } + + { + HashSink hashSink(HashAlgorithm::MD5); + src.drainInto(hashSink); + auto [hash, gotLength] = hashSink.finish(); + /* Use this opportunity to check that the upload size matches what we expect. */ + if (gotLength != size) + throw Error("unexpected size for upload '%s', expected %d, got: %d", path, size, gotLength); + /* The Base64 encoded 128-bit MD5 digest of the message (without the headers) according to RFC 1864: + https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html */ + uploadHeaders.push_back({"Content-MD5", hash.to_string(HashFormat::Base64, /*includeAlgo=*/false)}); + src.restart(); /* Seek to the beginning. */ + } + if (s3Config->multipartUpload && size > s3Config->multipartThreshold) { uploadMultipart(path, src, size, mimeType, std::move(uploadHeaders)); } else {