Einmal-Adressen
Stealth-Adressen: Geld an eine Adresse senden, ohne sie auf die Blockchain zu schreiben
Der Sender wählt ein zufälliges r, veröffentlicht R = r·G und sendet das Geld an den Einmalschlüssel P = Hs(8·r·A ‖ i)·G + B. Der Empfänger findet ihn, indem er dasselbe über a berechnet: Hs(8·a·R ‖ i)·G + B. Ausgeben kann er ihn mit dem privaten Schlüssel x = Hs(8·a·R ‖ i) + b.
Die Aufgabe
Wir wollen:
- dass weder die Adresse des Empfängers noch irgendetwas, das sich bei Zahlungen an dieselbe Person wiederholt, auf der Blockchain erscheint;
- dass der Empfänger seine Outputs selbst findet, ohne mit dem Sender zu sprechen;
- dass nur der Empfänger den Output ausgeben kann.
Die Lösung ist ein Diffie-Hellman-Schlüsselaustausch, der in jede Transaktion eingebaut ist.
Schritt für Schritt
1. Der Sender wählt ein zufälliges r
Ein frisches zufälliges r (privater Transaktionsschlüssel) für jede Transaktion. Seine öffentliche Version R = r·G (öffentlicher Transaktionsschlüssel) wird in das Feld tx_extra der Transaktion geschrieben.
2. Das gemeinsame Geheimnis
Der Sender berechnet 8·r·A (A ist der öffentliche View Key aus der Adresse). Später berechnet der Empfänger 8·a·R. Es ist derselbe Punkt, denn a·R = a·r·G = r·a·G = r·A. Im Code von Monero heißt das Key Derivation.
Der Faktor 8 (der Kofaktor) „säubert“ den Punkt von jedem Anteil kleiner Ordnung: Selbst wenn jemand ein bösartiges R liefert, landet das Ergebnis in der richtigen Untergruppe.
3. Der Einmalschlüssel
Das ist die Einmal-Adresse (öffentlicher Output-Schlüssel). Sie sieht aus wie ein zufälliger Punkt: Ohne a oder r gibt es nichts, womit man sie vergleichen könnte.
4. Der Empfänger sucht
Für jede neue Transaktion und jeden Output i berechnet die Wallet Hs(8·a·R ‖ i)·G + B und vergleicht es mit P. Übereinstimmung – der Output gehört uns. Dafür braucht es nur den View Key a und das öffentliche B – deshalb kann eine View-only-Wallet eingehende Zahlungen finden.
5. Der Empfänger gibt aus
Der private Schlüssel des Einmal-Outputs:
Dafür braucht es den Spend Key b. Mit dem Schlüssel x wird die Ausgabe signiert, und aus ihm wird das Key Image berechnet.
Auch der Betrag wird mit dem gemeinsamen Geheimnis verschlüsselt
Aus demselben gemeinsamen Geheimnis wird eine Maske für den Betrag abgeleitet. Der Betrag des Outputs (8 Bytes) wird als amount ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8] gespeichert, der Blinding-Faktor des Commitments als Hs("commitment_mask" ‖ Hs(8rA ‖ i)). Der Empfänger berechnet dasselbe und entschlüsselt den Betrag. Mehr dazu unter Pedersen-Commitments.
Schwachstellen
- Das Durchsuchen ist teuer: eine Punktmultiplikation für jeden Output jeder Transaktion. Teilweise gelöst durch View Tags.
- Der Janus-Angriff: Ein Sender, der vermutet, dass zwei Subadressen derselben Person gehören, kann einen Output aus Teilen beider bauen und beobachten, ob der Empfänger die Zahlung „bemerkt“. Wallets haben einen teilweisen Schutz; Carrot schließt die Lücke vollständig.
- Der Sender kennt r und kann einem Dritten beweisen, dass er an eine bestimmte Adresse gezahlt hat (manchmal nützlich – ein „Zahlungsnachweis“).