Skip to content

feat(sea): add SEA.timelock - time-locked encryption & dead-man's switch - #1425

Open
ABsUP wants to merge 1 commit into
amark:masterfrom
OpenCodeWEB:feat/sea-timelock
Open

feat(sea): add SEA.timelock - time-locked encryption & dead-man's switch#1425
ABsUP wants to merge 1 commit into
amark:masterfrom
OpenCodeWEB:feat/sea-timelock

Conversation

@ABsUP

@ABsUP ABsUP commented Aug 15, 2026

Copy link
Copy Markdown

feat(sea): add SEA.timelock — time-locked encryption & dead-man's switch

Adds a pure-crypto time-lock primitive with no trusted party and no time oracle — built only on WebCrypto SHA-256.

How it works

  1. SEA.timelock(data, pair, opt) encrypts the payload with a key that is the endpoint of a sequential SHA-256 hash chain of length opt.rounds (default 100 000 ≈ a few seconds):
h(0) = salt:seed
h(i+1) = SHA-256(h(i))
chainKey = h(rounds)
  1. Only the chain's starting seed is published in the capsule: { until, rounds, salt, seed, c }.
  2. SEA.timelock.unlock(capsule, cb, opt) recomputes the chainrounds sequential hashes — then decrypts. Hash chains are inherently non-parallelizable, so rounds is a real lower bound on unlock time. The creator pays the cost once at lock time; everyone else (including the creator, who keeps no shortcut) must pay it to unlock.

Dead-man's switch

Set opt.until to a future timestamp and re-publish a fresh capsule (new until, new seed) on each heartbeat. When heartbeats stop, the last published capsule becomes unlockable by anyone once its until passes. until is advisory metadata consumed by executor/release agents; the cryptographic time-lock is rounds.

Security properties

  • No trusted party: no escrow, no time oracle, no server key hold.
  • Sequential work: cannot be parallelized or shortcut; difficulty is tunable via rounds.
  • Tamper-evident ciphertext: any modification of c fails AES-GCM auth → undefined.
  • Optional metadata signing: pass a pair to get { pub, sig } over a canonical pipe-joined payload — forged until/rounds/salt/seed is then detected before the expensive puzzle is solved.
  • Anti-DoS guard: unlock(capsule, cb, { max }) refuses capsules whose rounds exceed max.
  • No key material in the capsule: only the seed + ciphertext are published.
  • Supports both promise and callback styles, matching the rest of SEA.

Example

// Alice locks a secret that must stay sealed for ~5 minutes of compute,
// releasing to anyone after the dead-man deadline:
const capsule = await SEA.timelock('will', alice, null, {
  rounds: 1000000,
  until: Date.now() + 30 * 86400000 // e.g. executor publishes it at this time
});
gun.get('estate').get('will').put(capsule);

// Anyone (later, after `until`): solve the puzzle, get the data
const will = await SEA.timelock.unlock(capsule); // 'will'

Tests

8 new tests in test/sea/sea.js (roundtrip, object data, no key-material leak, tamper blocking, wrong-seed blocking, signed/forged metadata, opt.max enforcement, callback style). Full SEA suite: 43 passing / 1 pending / 0 failing.

…witch

Encrypts data with a key that is the endpoint of a sequential SHA-256
hash chain of length opt.rounds; only the chain's starting seed is
published. Unlocking requires recomputing the chain (rounds sequential
hashes), which is inherently non-parallelizable, so rounds is a real
lower bound on unlock time - no trusted party or time oracle needed.

Dead-man's switch: set opt.until and re-publish a fresh capsule on each
heartbeat; when heartbeats stop, the last capsule becomes unlockable
after until passes. Opt-in signing ({pub, sig}) over a canonical
pipe-joined payload makes capsule metadata tamper-evident. opt.max on
unlock guards against malicious capsules with absurd rounds (anti-DoS).

8 tests: roundtrip, object data, no key-material leak, tamper blocking,
wrong-seed blocking, signed/forged metadata, opt.max, callback style.
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