Tecnico › Chi ha ricevuto?

Indirizzi monouso

Stealth address: inviare denaro a un indirizzo senza scriverlo sulla blockchain

In breve

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:

  1. sulla blockchain non compaia né l'indirizzo del destinatario né nulla che si ripeta tra i pagamenti alla stessa persona;
  2. il destinatario trovi da solo i propri output, senza parlare con il mittente;
  3. solo il destinatario possa spendere l'output.

La soluzione è uno scambio di chiavi Diffie–Hellman integrato in ogni transazione.

Passo dopo passo

Mittente Destinatario r (casuale) R = r·G conosce l'indirizzo: A, B segreto condiviso: 8·r·A P = Hs(8rA ‖ i)·G + B sulla catena: R e P (e nient'altro sull'indirizzo) a, b (chiavi proprie) vede R nella transazione segreto condiviso: 8·a·R = 8·a·r·G = 8·r·A verifica: Hs(8aR ‖ i)·G + B == P ?

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

P = Hs( 8·r·A ‖ i )·G + B i è l'indice dell'output nella transazione (varint), così due output della stessa transazione ottengono chiavi diverse

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:

x = Hs( 8·a·R ‖ i ) + b verifica: x·G = Hs(…)·G + b·G = Hs(…)·G + B = P ✓

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").