clockworklabs / clockworklabs/SpacetimeDB
Clients can influence RNG output by calling reducers at specified times
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 25.2k
- Forks
- 1.1k
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 46
Description
Because ReducerContext.rng() is "seeded by the timestamp of the reducer call" clients can send the call at a specified time to influence the RNG output.
While we cannot hit a specified µs which would give us 100% control we can still hit bigger timing windows. They have a chance of being more "lucky" due to RNG output not being perfectly uniform over time. (as an example for coin toss it's perfectly reasonable to get a few heads in a row).
Here's an example table of attack window vs waiting time for a reducer rolling true with 10% probability:
#[spacetimedb::reducer]
pub fn rand(ctx: &ReducerContext) {
let mut rng = ctx.rng();
let result = rng.gen_ratio(1, 10);
ctx.db.rng_results().insert(RngResult {
id: 0,
result,
});
}
| v Attack window - Waiting Time > | 2 s | 5 s | 10 s | 30 s | 60 s |
|---|---|---|---|---|---|
| 0.1 ms | 26.00 % | 27.00 % | 27.00 % | 27.00 % | 27.00 % |
| 0.5 ms | 16.20 % | 16.60 % | 17.00 % | 17.00 % | 17.00 % |
| 1.0 ms | 14.60 % | 14.60 % | 14.70 % | 14.70 % | 14.70 % |
| 5.0 ms | 11.52 % | 11.64 % | 11.64 % | 11.90 % | 11.90 % |
| 10.0 ms | 11.07 % | 11.18 % | 11.18 % | 11.29 % | 11.29 % |
| 50.0 ms | 10.33 % | 10.49 % | 10.49 % | 10.51 % | 10.51 % |
| 100.0 ms | 10.21 % | 10.25 % | 10.28 % | 10.30 % | 10.37 % |
| 500.0 ms | 10.06 % | 10.11 % | 10.14 % | 10.14 % | 10.15 % |
| 1000.0 ms | 10.00 % | 10.06 % | 10.11 % | 10.11 % | 10.11 % |
From my tests in real-world scenarios it's reasonable to hit a 500µs-2ms window.
Results for attacking a reducer rolling true 10% of the time hosted on Maincloud from a host in phoenixNAP Ashburn DC: ATTACK_RESULTS: 1379 / 10000 (10% -> 13.7900%)
Longer writeup: https://koz.omg.lol/2025/10/spacetimedb-rng-manipulation-attack
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the ReducerContext.rng() entry point and the reducer example in the issue, then reproduce the timing attack described in the writeup. Done means reducer callers can no longer improve RNG outcomes by choosing when to invoke the reducer, with coverage for the reported attack scenario.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100