Skip to content

Add typed checksum enforcement to presigned upload URLs - #43

Merged
alexis- merged 1 commit into
mainfrom
develop
Aug 24, 2026
Merged

Add typed checksum enforcement to presigned upload URLs#43
alexis- merged 1 commit into
mainfrom
develop

Conversation

@alexis-

@alexis- alexis- commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

R2ChecksumAlgorithm is a closed set of the five digest algorithms R2 was verified to enforce (probed against live R2, 2026-08-24): crc32, crc32c, sha1, sha256, and md5. Each value carries its wire name, the header its digest travels in (x-amz-checksum-{name}, or Content-MD5 for md5), its exact digest byte length, and whether R2 accepts it on multipart part uploads. UploadChecksum pairs an algorithm with a base64 digest and validates at construction that the digest decodes to exactly the algorithm's digest length; TryCreate validates untrusted algorithm and digest strings without exceptions.

PresignedPutRequest and PresignedUploadPartRequest take an optional Checksum, and PresignedUploadPartsRequest takes optional per-part checksums keyed by part number. The client signs the digest header into the URL, so the upload fails with 400 BadDigest and stores nothing when the arriving bytes do not hash to the stated digest. On a collision with the same header in HeadersToSign, the typed checksum wins.

R2 answers 501 NotImplemented to any part upload carrying a sha1 or sha256 checksum header, regardless of the digest, so the part URL methods refuse those two algorithms with ArgumentException before signing. The batch generator also rejects a checksum keyed to a part number absent from PartNumberAndLength, and now builds a fresh GetPreSignedUrlRequest per part because the AWS SDK's HeadersCollection cannot remove a header once set, which would leak one part's digest header into later parts' URLs.

Integration tests pin the live matrix: every algorithm accepted with a correct digest and rejected with a wrong one, on single-part PUTs (all five) and part uploads (crc32, crc32c, md5), plus a batch multipart upload with per-part digests completing end to end. The presigned GET expiry test gets a wider validity window so the pre-expiry fetch cannot race the window closing under parallel suite load. Version 2.3.0.

R2ChecksumAlgorithm is a closed set of the five digest algorithms R2 was
verified to enforce (probed against live R2, 2026-08-24): crc32, crc32c,
sha1, sha256, and md5. Each value carries its wire name, the header its
digest travels in (x-amz-checksum-{name}, or Content-MD5 for md5), its
exact digest byte length, and whether R2 accepts it on multipart part
uploads. UploadChecksum pairs an algorithm with a base64 digest and
validates at construction that the digest decodes to exactly the
algorithm's digest length; TryCreate validates untrusted algorithm and
digest strings without exceptions.

PresignedPutRequest and PresignedUploadPartRequest take an optional
Checksum, and PresignedUploadPartsRequest takes optional per-part
checksums keyed by part number. The client signs the digest header into
the URL, so the upload fails with 400 BadDigest and stores nothing when
the arriving bytes do not hash to the stated digest. On a collision with
the same header in HeadersToSign, the typed checksum wins.

R2 answers 501 NotImplemented to any part upload carrying a sha1 or
sha256 checksum header, regardless of the digest, so the part URL
methods refuse those two algorithms with ArgumentException before
signing. The batch generator also rejects a checksum keyed to a part
number absent from PartNumberAndLength, and now builds a fresh
GetPreSignedUrlRequest per part because the AWS SDK's HeadersCollection
cannot remove a header once set, which would leak one part's digest
header into later parts' URLs.

Integration tests pin the live matrix: every algorithm accepted with a
correct digest and rejected with a wrong one, on single-part PUTs (all
five) and part uploads (crc32, crc32c, md5), plus a batch multipart
upload with per-part digests completing end to end. The presigned GET
expiry test gets a wider validity window so the pre-expiry fetch cannot
race the window closing under parallel suite load. Version 2.3.0.
@alexis-
alexis- merged commit 7504ce8 into main Aug 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant