Sign and Burn Base Sepolia · test network only

Bunker mode, rehearsed

A Safe owner that burns its key after every approval.

Each press approves a real transaction on a test network with a one-time key, and names the next key in the same transaction. The key that signed is dead. The next one has never been seen, so there is nothing yet to attack. If elliptic curves fall, a stolen curve key gets nowhere.

  1. Revealthe one-time signature: key n is spent
  2. Runthe Safe transaction it approves
  3. Rotateto key n+1: only its fingerprint goes on chain
Console MicroPython 1.26 in WebAssembly Could run on a Raspberry Pi Pico 2 W or an ESP32

Starting MicroPython…

Serial: every line the page and the console said

On a board these lines would cross USB. Here they cross into MicroPython. Your passkey's seeds are secret and are never shown.


      

The attack room

Try to use what your approvals put on chain. Each attack asks the live seat with a simulation (eth_call): it costs nothing and moves nothing. The seat's own error comes back.

The attacks use your last approval. Make one first.

How it works

The Safe stays the multisig

An ordinary Safe 1.4.1 holds the money, keeps the owners, counts approvals and runs transactions. Nothing about it changes. Its one owner here is a seat: a small contract that approves only when two signatures check out.

  • A curve signature from your passkey, P-256, checked by Safe's own passkey signer.
  • A one-time signature that uses only SHA-256, and only once: Winternitz, 67 chains of 15 steps.

Every approval names the next key's fingerprint. The seat records it and accepts nothing else. The key that just signed is dead.

One passkey, two jobs, one tap

Your passkey signs c with its curve key. In the same tap, its PRF extension turns two labels into two 32-byte seeds, from a secret that never leaves it. The console turns the seed of key n into 67 secrets and signs m; the seed of key n+1 gives the next fingerprint.

c = sha256("sign-and-burn/approve/v1" ‖ chain ‖ seat ‖ safe ‖ n ‖ safeTxHash)
m = sha256("sign-and-burn/one-time/v1" ‖ c ‖ nextKey)

The passkey signs c; key n signs m. Swap the next key and the one-time signature no longer fits.

The console

The console is about a thousand lines of MicroPython: PicoQuorum's Safe hash and decoder, the one-time keys, and a core that answers one JSON line with another. It works out the Safe transaction hash itself, says what the transaction does, refuses what it can't explain, and keeps the guardrail.

It runs here in WebAssembly. Copy the same files to a Pico or an ESP32 and main.py answers the same lines over USB. That hasn't been tried on a board yet.

The guardrail: one signature per key

The seat makes sure a spent key is dead. It can't make sure a key signs only once: two signatures with one key reveal enough to forge a third. So the console records every approval before it lets one out, and while key n's approval waits it signs nothing else with key n, only sends that one again. The attack room's danger case shows why.

What it protects, and what it doesn't

ThreatHolds?Why
Curves broken; the attacker sees everything publicyesThey can make curve signatures, but not the next one-time signature.
Copying or replaying an approvalyesThe chain, the seat, the Safe and n are all in what is signed.
One key signs two messages, both public, and curves are brokennoThe guardrail is the only defence.
A bug in the one-time codepartlyThe curve signature still guards, unless curves are broken too.
A hostile copy of this page, or a hostile browser extensionnoThe page sees the seeds. Check its fingerprint, or run your own copy.
A stolen, unlocked device, or a taken-over account that syncs your passkeysnoWhoever has the passkey has both halves.
Lost passkeystuckHere the seat can't approve again. In a real multisig the other owners replace it.

Nobody has shown that elliptic curves can be broken this way. This is a rehearsal on a test network, not a response to a known attack, and it has not been audited.

Check this page

The console fingerprint, worked out in your browser over the files it runs:

…

The README's build table names the same one, and so would a board running these files (node tools/fingerprint.mjs).

  1. Save a copy. SHA256SUMS lists every file in this folder. Fetch them, then sha256sum -c SHA256SUMS.
  2. Rebuild it. cd site && npm ci && npm run build writes the same bytes into docs/. CI checks that on every push.
  3. Run your copy. python3 -m http.server in the folder, then open localhost:8000. A passkey made there belongs to your copy, and this site can never ask it to sign.