Expand description
Durable namespace reservation before deriving or loading one-time keys.
A lease is a permanent, exclusive-create record keyed by trusted signing identity, epoch and batch number. Both the file and containing directory are synchronized before a lease is returned. A crashed/abandoned reservation is burned, never recovered or recycled. No private key material is stored.
§The problem
DSig signs every message with a one-time key: a W-OTS+ key that is safe to use for exactly one message. Signing two different messages with the same one-time key reveals enough hash-chain nodes for a forger to sign other messages. So the most important rule of the whole project is: a one-time key is never reused, not after it was admitted, not after a timeout, not after a reset and not after a launch whose outcome is uncertain.
The keys are not stored anywhere. They are derived: the hardware (and the
software reference in crate::reference) computes every key of a batch
from the master seed, the epoch and the batch number. So the triple
(signer identity, epoch, batch number) is the real name of 128 one-time
keys (crate::BATCH_KEYS). If two runs ever derive the same triple from
the same seed, they derive the same keys, and reuse has happened, even if
each run on its own was perfectly correct.
§The decision
Before any keys are derived or loaded, the host leases the batch numbers by creating a small text file on disk. The file is the proof that those names have been used, and it is never deleted:
- The file is created with
create_new(the operating system’s exclusive create). If it already exists the call fails, so two processes racing for the same batch cannot both win. - The file and its directory are
fsynced (forced to stable storage) before the lease is handed out. A power cut after that point cannot make the reservation disappear. - There is no “release”, “resume” or “reopen”. A crashed or abandoned reservation is burned: its batch numbers are simply lost. Losing some numbers out of a 64-bit space costs nothing; reusing one could cost everything.
- No private key material is ever written. The record holds only the public identity, the epoch and the batch numbers. The master seed is never stored.
§Two kinds of lease
| Type | Covers | Durable writes | Epoch half |
|---|---|---|---|
BatchLease (via BatchLease::reserve) | one batch (128 keys) | one record per batch | low half: bit 63 clear, nonzero |
RangeLease | RANGE_BATCHES = 2^20 batches (2^27 keys) | one record per range | high half: bit 63 (RANGE_EPOCH_BIT) set |
A record per batch is simple but means one synchronous disk write per 128 keys, far too slow for a card that makes keys continuously. A range lease burns a whole fixed interval of batch numbers with one durable write, then hands the batches out one by one from memory. Because single leases and range leases live in different halves of the epoch space, a single lease can never overlap a range.
§Lifecycle
single batch fixed range
──────────── ───────────
BatchLease::reserve(dir, id, e, b) RangeLease::reserve(dir, id, E|bit63, r)
│ create_new + write record │ create_new + write record
│ fsync file, fsync directory │ fsync file, fsync directory
▼ ▼
┌───────────────┐ ┌──────────────────────────────┐
│ BatchLease │ │ RangeLease next = r × 2^20 │
│ (not Clone) │ │ end = next+2^20 │
└──────┬────────┘ └──────┬───────────────────────┘
│ │ take_next(): next += 1
│◀───────────────────────────────────┘ (memory only, no fsync)
│ prepare(master_seed, signing key)
│ identity mismatch → error, lease consumed (burned)
▼
PreparedBatch (keys derived; the local seed copy is zeroized)
Every path ends in "burned": drop, crash, error, depletion or use.
The record file stays on disk forever; nothing reads it back to resume.§Where it fits
The host program dsig_engine_host (src/bin/dsig_engine_host.rs) calls
RangeLease::reserve in its prepare step before it draws the master
seed, so a crash at any later point burns the range instead of leaving it
reusable. The card (crate::engine) then derives every key of that run
from the loaded parameters. For software users, BatchLease::prepare
wraps PreparedBatch::new so a batch is burned on disk before its keys
exist in memory.
§What it does not protect against
This protects cooperating processes on a trusted local filesystem, not a
malicious administrator, filesystem rollback, restored snapshots or cloned
machines. Keep the directory persistent and backed by storage with correct
fsync semantics. Reusing a master seed after losing this journal is unsafe;
use new independent entropy in that case. Lease files must not be deleted.
Structs§
- Batch
Lease - A non-cloneable allocation capability, returned only after durable creation.
- Range
Lease - Persist-ahead allocation of a fixed, disjoint range of batch identifiers.
Constants§
- RANGE_
BATCHES - One permanent range contains this many 128-key batches.
- RANGE_
EPOCH_ BIT - Epoch high bit reserved for fixed-range allocation; single leases reject it.