feat: add optional hashFn to generateKey for wider key entropy#1267
feat: add optional hashFn to generateKey for wider key entropy#1267juliusmarminge wants to merge 3 commits intomainfrom
Conversation
The `generateKey` function uses Effect's `Hash.string` (32-bit) to hash the file seed, creating a collision bottleneck even when users provide high-entropy inputs via `getFileHashParts`. This adds an optional `hashFn` parameter to `RouteOptions` that lets users supply their own hash function, returning either a single number or an array of numbers for wider SQIds encoding. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 0e818d4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
WalkthroughThe PR adds support for a custom hash function: a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 🧹 Recent nitpick comments
Comment |
More templates
commit: |
📦 Bundle size comparison
|
Greptile OverviewGreptile SummaryThis PR adds an optional Key changes:
Test coverage:
The implementation is clean, well-tested, and maintains backward compatibility since Confidence Score: 5/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Handler
participant GenerateKey
participant CustomHashFn
participant SQIds
Handler->>GenerateKey: Call generateKey with file and options
GenerateKey->>GenerateKey: Extract and stringify file properties
alt Custom hashFn provided
GenerateKey->>CustomHashFn: Invoke custom function
CustomHashFn-->>GenerateKey: Return number or array
else Default behavior
GenerateKey->>GenerateKey: Use built-in hash function
end
GenerateKey->>GenerateKey: Convert to array and normalize values
GenerateKey->>SQIds: Encode file seed portion
SQIds-->>GenerateKey: Return encoded file seed
GenerateKey->>SQIds: Encode appId portion
SQIds-->>GenerateKey: Return encoded appId
GenerateKey-->>Handler: Concatenate and return final key
|
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add optional hash function to generateKey for improved key entropy.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/shared/src/crypto.ts (1)
89-116:⚠️ Potential issue | 🟠 MajorGuard against empty hash arrays before SQIds encoding.
If
hashFnreturns[]or produces no valid numbers,SQIds.encode([])returns an empty string, collapsing the file seed and risking key collisions across different files. Additionally, ifhashFnreturnsNaNor non-integers, the current code doesn't validate these invalid inputs before encoding. Add validation to ensure the hash array contains at least one finite, non-negative integer before encoding.🔧 Proposed fix
- const rawHash = hashFn ? hashFn(hashParts) : Hash.string(hashParts); - const hashArray = Array.isArray(rawHash) ? rawHash : [rawHash]; - const encodedFileSeed = new SQIds({ alphabet, minLength: 36 }).encode( - hashArray.map((n) => Math.abs(n)), - ); + const rawHash = hashFn ? hashFn(hashParts) : Hash.string(hashParts); + const hashArray = Array.isArray(rawHash) ? rawHash : [rawHash]; + const normalized = hashArray + .map((n) => (Number.isFinite(n) ? Math.abs(Math.trunc(n)) : NaN)) + .filter((n) => Number.isFinite(n)); + if (normalized.length === 0) { + throw new UploadThingError({ + code: "BAD_REQUEST", + message: "hashFn must return at least one finite number", + }); + } + const encodedFileSeed = new SQIds({ alphabet, minLength: 36 }).encode( + normalized, + );
Summary
HashFntype and optionalhashFnfield toRouteOptionsin@uploadthing/sharedgenerateKeynow accepts an optionalhashFnparameter that replaces the defaultHash.string(32-bit) for hashing the file seed portion of the keyencodedAppIdprefix remains unchanged (still usesHash.string) soverifyKeycontinues to workMotivation
Hash.stringis a 32-bit hash, so even if users pass 128 bits of randomness viagetFileHashParts, it all gets crushed through a ~31-bit funnel. This creates an unnecessary collision bottleneck. WithhashFn, users can bypass this limitation for the file seed portion while keeping backward compatibility.Test plan
hashFnreturning a single number generates a valid key (passesverifyKey)hashFnreturning an array of numbers generates a valid key (passesverifyKey)hashFnoutputs produce different keys🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests
Chores