Verify the master key against the database at startup (B-89, SEC-49, SEC-50, SEC-09)
The lifespan decrypts the key and compares a fixed sample (reserve/round pool addresses, the 10 lowest and highest user addresses, unrevealed round secrets) with the database, and refuses to serve on any mismatch without printing key material. The key scripts refuse a new or imported key that would not reproduce the stored addresses, and write 0600. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
32df50cb75
commit
55d852fe08
14 files changed
+281
-46
No files matched your search
@@ -343,7 +343,7 @@ sequenceDiagram
|
||||
L->>L: validate_runtime_secrets()
|
||||
L->>L: parse_endpoints(...)
|
||||
L->>L: quorum minimo (validate_quorum_servers)
|
||||
L->>L: verifica della master key (B-89, da implementare)
|
||||
L->>L: verifica della master key (B-89)
|
||||
L->>L: START_PAUSED (B-90, da implementare)
|
||||
L->>L: app.state.electrum_listener, app.state.user_locks
|
||||
L->>L: create_task ×6
|
||||
@@ -448,9 +448,8 @@ implementare» non esistono ancora nel codice (vedi [ARC-60] e [ARC-22]).
|
||||
([operativita.md](operativita.md#ripristino), B-90).
|
||||
*Codice:* `app/main.py:lifespan` · `app/config.py:validate_runtime_secrets` · `app/config.py:validate_quorum_servers` ·
|
||||
*Test:* `tests/unit/test_electrum_listener.py`
|
||||
*Stato:* da implementare (B-89, B-90), per i controlli 2 e 3 · *Oggi:* dopo
|
||||
i segreti, gli endpoint e il quorum il lifespan crea subito i task; la
|
||||
chiave si decifra alla prima registrazione o al primo pagamento.
|
||||
*Stato:* da implementare (B-90), per il controllo 3 · *Oggi:* il lifespan non
|
||||
ferma la registrazione né mette in pausa dopo un ripristino.
|
||||
- **[ARC-21]** Lo stato condiviso con le route vive su `app.state`:
|
||||
`electrum_listener` (l'`ElectrumListener`) e `user_locks` (un
|
||||
`UserLocks`), creati nel lifespan; `auth_rate_limiters` viene invece creato
|
||||
|
||||
+2
-5
@@ -69,9 +69,6 @@ dovranno allora rispettare quel proprietario.
|
||||
`app/config.py:validate_runtime_secrets`,
|
||||
`app/electrum/client.py:parse_endpoints`, `app/wallet/hd.py:_load_account_key` ·
|
||||
*Test:* `tests/unit/test_config.py`, `tests/unit/test_electrum_client.py`
|
||||
*Stato:* da implementare (B-89) · *Oggi:* della master key si controlla
|
||||
solo che il file esista e che `XPRV_ENCRYPTION_KEY` non sia vuota; una
|
||||
chiave sbagliata emerge alla prima derivazione.
|
||||
|
||||
- **[OPS-03]** Il database SQLite lavora in modalità **WAL**: accanto a
|
||||
`plm_lottery.db` esistono `plm_lottery.db-wal` e `plm_lottery.db-shm`, e le
|
||||
@@ -754,7 +751,7 @@ con `docker compose logs app` ([architettura.md](architettura.md) ARC-09).
|
||||
copia cifrata con essa: la copia nella radice (`./master.xprv.enc`, B-71)
|
||||
e i backup precedenti del file. Il file scritto dal container appartiene a
|
||||
root con permessi `0644`: `sudo chmod 600 data/keys/master.xprv.enc`.
|
||||
Dopo B-89 gli script scrivono `0600` e rifiutano una xprv i cui
|
||||
Gli script scrivono `0600` e rifiutano una xprv i cui
|
||||
indirizzi non coincidono con quelli del database
|
||||
([SEC-50](sicurezza.md#sec-50)); dopo B-91 la chiave sta in un file e non
|
||||
in `.env` ([SEC-53](sicurezza.md#sec-53)).
|
||||
@@ -923,7 +920,7 @@ master key usare il container.
|
||||
`generate_master_key.py` non ha il ripiego su `./data/keys/` degli altri
|
||||
due: dal venv scrive esattamente dove dice `MASTER_KEY_PATH` di `.env`. I
|
||||
file scritti hanno i permessi predefiniti del sistema (di norma `0644`) e,
|
||||
nel container, appartengono a root. Dopo B-89 gli script scrivono `0600` e
|
||||
nel container, appartengono a root. Gli script scrivono `0600` e
|
||||
rifiutano una xprv diversa da quella degli indirizzi già nel database
|
||||
([SEC-50](sicurezza.md#sec-50)).
|
||||
*Codice:* `scripts/generate_master_key.py`, `app/wallet/hd.py:generate_master_key`,
|
||||
|
||||
+6
-16
@@ -182,10 +182,7 @@ flowchart TD
|
||||
`0600` (senza passare da permessi più larghi) e lo riscrivono con gli
|
||||
stessi permessi.
|
||||
*Codice:* `scripts/generate_master_key.py`, `scripts/encrypt_master_key.py`,
|
||||
`app/wallet/hd.py:generate_master_key` · *Test:* —
|
||||
*Stato:* da implementare (B-89) · *Oggi:* gli script accettano qualunque
|
||||
xprv senza guardare il database e scrivono il file con i permessi
|
||||
predefiniti del sistema (di norma `0644`).
|
||||
`app/wallet/hd.py:generate_master_key`, `app/wallet/verify.py` · *Test:* `tests/unit/test_master_key_check.py`
|
||||
|
||||
### Cifratura a riposo
|
||||
|
||||
@@ -237,10 +234,8 @@ sì che un backup dei dati non contenga entrambe.
|
||||
verificata ([SEC-49]). Da quel momento la chiave dell'account
|
||||
`m/84'/746'/0'` resta in memoria nel processo per tutta la sua durata; la
|
||||
root xprv non viene conservata.
|
||||
*Codice:* `app/wallet/hd.py:_load_account_key`, `app/main.py:lifespan`
|
||||
*Stato:* da implementare (B-89) · *Oggi:* la chiave è decifrata al primo
|
||||
uso (la prima derivazione, tipicamente una registrazione o il primo
|
||||
pagamento), non all'avvio.
|
||||
*Codice:* `app/wallet/hd.py:_load_account_key`, `app/main.py:lifespan` ·
|
||||
*Test:* `tests/unit/test_master_key_check.py`
|
||||
|
||||
### Recupero
|
||||
|
||||
@@ -299,14 +294,9 @@ riserva è [ELC-19](connessione-electrum.md#1-rotazione-tra-server) (vedi
|
||||
decifri. Lo scopo è non partire con una master key diversa da quella che
|
||||
ha prodotto gli indirizzi salvati, che sposterebbe in silenzio il
|
||||
montepremi e farebbe firmare transazioni che la rete rifiuta.
|
||||
*Codice:* `app/main.py:lifespan`, `app/wallet/hd.py:_load_account_key`,
|
||||
`app/wallet/hd.py:derive_user_address`, `app/wallet/hd.py:derive_pool_address` ·
|
||||
*Test:* —
|
||||
*Stato:* da implementare (B-89) · *Oggi:* all'avvio non si decifra nulla;
|
||||
una chiave di cifratura sbagliata o malformata emerge solo al primo uso
|
||||
della master key (prima registrazione o primo pagamento), e una xprv
|
||||
diversa da quella originale non emerge affatto: produce indirizzi nuovi,
|
||||
compreso quello del montepremi.
|
||||
*Codice:* `app/main.py:lifespan`, `app/wallet/verify.py:verify_master_key`,
|
||||
`app/wallet/verify.py:sample_problems` ·
|
||||
*Test:* `tests/unit/test_master_key_check.py`
|
||||
|
||||
## Autenticazione degli utenti
|
||||
|
||||
|
||||
+2
-2
@@ -237,7 +237,7 @@ cifrava di nuovo il file sbagliato (vedi [operativita.md](operativita.md)).
|
||||
| B-86 | Un server connesso ma fermo non viene mai sostituito | Alta | *da registrare* | `test_electrum_listener.py` | ELC, OPS |
|
||||
| B-87 | L'RBF può lasciare un resto sotto la soglia di dust | Alta | *da registrare* | `test_rbf_dust.py`, `test_broadcast.py` | TX |
|
||||
| B-88 | Le transazioni SQLite non sono quelle che il codice presuppone; `synchronous=NORMAL` può perdere gli ultimi commit | Alta | *da registrare* | `test_sqlite_transactions.py`, `test_db_base.py` | DAT, ARC |
|
||||
| B-89 | Problemi della chiave rilevati solo al primo uso; gli script accettano qualunque xprv e scrivono file leggibili da tutti | Alta | **aperto** | — | SEC, OPS |
|
||||
| B-89 | Problemi della chiave rilevati solo al primo uso; gli script accettano qualunque xprv e scrivono file leggibili da tutti | Alta | *da registrare* | `test_master_key_check.py` | SEC, OPS |
|
||||
| B-90 | Il ripristino di un backup riapre round già pagati e riassegna indici di derivazione | Alta | **aperto** | — | OPS, REG |
|
||||
| B-91 | Container eseguito come root, permessi larghi, dipendenze e immagini non fissate | Alta | **aperto** | — | SEC, OPS |
|
||||
| B-92 | Nessuna verifica di salute o prontezza, stato Electrum invisibile, audit non filtrabile, log incompleti | Media | **aperto** | — | OPS, AUD, API |
|
||||
@@ -254,7 +254,7 @@ cifrava di nuovo il file sbagliato (vedi [operativita.md](operativita.md)).
|
||||
|
||||
- **Corretti:** B-01 … B-70, tranne B-16, che è chiuso nella sostanza (il
|
||||
crash è eliminato e `/guida` serve un segnaposto) ma lascia la guida vera
|
||||
come limite accettato; dalla revisione del 2026-09-28: B-88, B-75, B-97, B-80, B-84, B-76, B-77, B-78, B-83, B-74, B-85, B-86, B-73, B-81, B-79, B-82, B-87, B-95, B-98, B-99, B-100.
|
||||
come limite accettato; dalla revisione del 2026-09-28: B-88, B-75, B-97, B-80, B-84, B-76, B-77, B-78, B-83, B-74, B-85, B-86, B-73, B-81, B-79, B-82, B-87, B-89, B-95, B-98, B-99, B-100.
|
||||
- **Aperti:** B-71, B-72 (gravità bassa, operativi o di documentazione) e
|
||||
B-73 … B-98 dalla revisione del 2026-09-28 tranne i corretti (0 critici,
|
||||
3 alti, 5 medi), da implementare nell'ordine di
|
||||
|
||||
Reference in new issue
Block a user