Direcciones de un solo uso
Stealth addresses: enviar dinero a una dirección sin escribirla en la blockchain
El remitente elige un r aleatorio, publica R = r·G y envía el dinero a la clave de un solo uso P = Hs(8·r·A ‖ i)·G + B. El destinatario la encuentra calculando lo mismo mediante a: Hs(8·a·R ‖ i)·G + B. La gasta con la clave privada x = Hs(8·a·R ‖ i) + b.
El objetivo
Queremos:
- que en la blockchain no aparezca ni la dirección del destinatario ni nada que se repita entre pagos a una misma persona;
- que el destinatario encuentre sus salidas por sí mismo, sin hablar con el remitente;
- que solo el destinatario pueda gastar la salida.
La solución es un intercambio de claves Diffie–Hellman integrado en cada transacción.
Paso a paso
1. El remitente elige un r aleatorio
Un r aleatorio nuevo (clave privada de la transacción) en cada transacción. Su versión pública R = r·G (clave pública de la transacción) se escribe en el campo tx_extra de la transacción.
2. El secreto compartido
El remitente calcula 8·r·A (A es la clave pública de visualización que figura en la dirección). Más tarde el destinatario calcula 8·a·R. Es el mismo punto, porque a·R = a·r·G = r·a·G = r·A. En el código de Monero se llama key derivation (derivación de clave).
El factor 8 (el cofactor) «limpia» el punto de cualquier componente de orden pequeño: aunque alguien proporcione un R malicioso, el resultado cae en el subgrupo correcto.
3. La clave de un solo uso
Esta es la dirección de un solo uso (clave pública de la salida). Parece un punto aleatorio: sin a o r no hay nada con qué compararla.
4. El destinatario escanea
Para cada transacción nueva y cada salida i, el monedero calcula Hs(8·a·R ‖ i)·G + B y lo compara con P. Si coincide, la salida es nuestra. Para esto solo hace falta la clave de visualización a y la pública B, así que un monedero de solo lectura puede encontrar los pagos entrantes.
5. El destinatario gasta
La clave privada de la salida de un solo uso:
Para esto hace falta la clave de gasto b. La clave x firma el gasto, y a partir de ella se calcula la key image.
El importe también se cifra con el secreto compartido
A partir del mismo secreto compartido se deriva una máscara para el importe. El importe de la salida (8 bytes) se guarda como amount ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8], y el factor de cegado del compromiso como Hs("commitment_mask" ‖ Hs(8rA ‖ i)). El destinatario calcula lo mismo y descifra el importe. Más en compromisos de Pedersen.
Puntos débiles
- Escanear es costoso: una multiplicación de punto por cada salida de cada transacción. Lo resuelven en parte los view tags.
- El ataque Janus: un remitente que sospecha que dos subdirecciones pertenecen a la misma persona puede construir una salida con partes de ambas y ver si el destinatario «nota» el pago. Los monederos tienen una protección parcial; Carrot lo cierra por completo.
- El remitente conoce r y puede demostrar a un tercero que pagó a una dirección concreta (a veces es útil: una «prueba de pago»).