Indirizzi monouso
Stealth address: inviare denaro a un indirizzo senza scriverlo sulla blockchain
Il mittente sceglie un r casuale, pubblica R = r·G e invia il denaro alla chiave monouso P = Hs(8·r·A ‖ i)·G + B. Il destinatario la trova calcolando la stessa cosa tramite a: Hs(8·a·R ‖ i)·G + B. La spende con la chiave privata x = Hs(8·a·R ‖ i) + b.
Il compito
Vogliamo che:
- sulla blockchain non compaia né l'indirizzo del destinatario né nulla che si ripeta tra i pagamenti alla stessa persona;
- il destinatario trovi da solo i propri output, senza parlare con il mittente;
- solo il destinatario possa spendere l'output.
La soluzione è uno scambio di chiavi Diffie–Hellman integrato in ogni transazione.
Passo dopo passo
1. Il mittente sceglie un r casuale
Un r casuale nuovo (chiave privata della transazione) per ogni transazione. La sua versione pubblica R = r·G (chiave pubblica della transazione) viene scritta nel campo tx_extra della transazione.
2. Il segreto condiviso
Il mittente calcola 8·r·A (A è la view key pubblica presa dall'indirizzo). Più tardi il destinatario calcola 8·a·R. È lo stesso punto, perché a·R = a·r·G = r·a·G = r·A. Nel codice di Monero si chiama key derivation.
Il fattore 8 (il cofattore) "ripulisce" il punto da qualsiasi componente di ordine piccolo: anche se qualcuno fornisce un R malevolo, il risultato finisce nel sottogruppo corretto.
3. La chiave monouso
Questo è l'indirizzo monouso (chiave pubblica dell'output). Sembra un punto casuale: senza a o r non c'è nulla con cui confrontarlo.
4. Il destinatario scansiona
Per ogni nuova transazione e ogni output i il wallet calcola Hs(8·a·R ‖ i)·G + B e lo confronta con P. Se coincidono, l'output è nostro. Servono solo la view key a e la B pubblica: per questo un wallet in sola visualizzazione può trovare i pagamenti in entrata.
5. Il destinatario spende
La chiave privata dell'output monouso:
Serve la spend key b. La chiave x firma la spesa, e da essa si calcola la key image.
Anche l'importo è cifrato con il segreto condiviso
Dallo stesso segreto condiviso si ricava una maschera per l'importo. L'importo dell'output (8 byte) viene memorizzato come amount ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8], e il fattore di accecamento del commitment come Hs("commitment_mask" ‖ Hs(8rA ‖ i)). Il destinatario calcola le stesse cose e decifra l'importo. Maggiori dettagli in commitment di Pedersen.
Punti deboli
- La scansione è costosa: una moltiplicazione di punto per ogni output di ogni transazione. In parte risolto dai view tag.
- L'attacco Janus: un mittente che sospetta che due subaddress appartengano alla stessa persona può costruire un output con parti di entrambi e vedere se il destinatario "si accorge" del pagamento. I wallet hanno una protezione parziale; Carrot lo chiude del tutto.
- Il mittente conosce r e può dimostrare a terzi di aver pagato un certo indirizzo (a volte utile: una "prova di pagamento").