Qué pasó
El 19 de agosto de 2026 la implementación de referencia de Stratum V2 integró la
pull request 2283, 27 commits en
noise_sv2, el crate que cifra el enlace entre un minero y su pool, y en el códec que
enmarca sus mensajes. La pull request cierra
seis issues del propio repositorio y doce hallazgos de un repositorio de auditoría,
project-loupe/audit-stratum, que no es legible públicamente. Nada de esto repara un cifrado
roto. Todo trata de lo que la implementación hace con el material criptográfico y con los
contadores a ambos lados del cifrado.
Qué cambia
Stratum V2 envuelve el enlace entre minero y pool en una sesión cifrada y autenticada, y la especificación es clara sobre el motivo: "Cualquier información sobre los shares enviados puede convertirse directamente en estimaciones de los ingresos de un minero y asociarse a un nombre de usuario concreto". La sesión usa ChaCha20-Poly1305, con una clave de 32 bytes y un nonce de 8 bytes que empieza en cero y se incrementa tras cada mensaje.
Cuatro de los cambios merecen seguirse.
Un contador que podía dar la vuelta. Con este tipo de cifrado autenticado, una misma clave nunca debe cifrar dos mensajes con el mismo nonce. El código incrementaba el contador sin comprobación, así que en su valor máximo la suma daba la vuelta y la misma clave volvía a empezar en el nonce cero. Ahora el cifrado y el descifrado fallan, con un test que fija el contador en su último valor y comprueba que ambos se niegan (commit 02630c9c).
Copiar un cifrador es copiar un nonce. Initiator, Responder y el códec derivaban
Clone, de modo que una copia llevaba la misma clave y el mismo contador y podía cifrar
mensajes distintos con ambos. Esas derivaciones ya no están. El issue que lo pedía resume el
motivo en una línea: "Esto lleva a reutilización de nonce, lo cual es particularmente
indeseable".
Secretos que sobrevivían al handshake. Cuando el handshake termina, sus pares de claves
efímero, estático y de autoridad, la chaining key y el hash del handshake se borran en lugar
de quedarse en el objeto
(commit 25194461),
y los búferes intermedios usados para derivar las claves de sesión, antes construidos con
.concat() en el heap y nunca limpiados, son ahora un búfer fijo en la pila que se borra. Las
escrituras volátiles a cero escritas a mano se sustituyeron por el crate zeroize
(commit caf81de8).
Una clave que ya se ha borrado no se puede recuperar de un volcado tras un fallo, de una
página en disco ni de un proceso que alguien lea después, que es todo el argumento para
borrarla.
Un campo firmado que nadie leía. Un pool demuestra su identidad con un certificado firmado
por una clave de autoridad, y el mensaje firmado es SHA-256(version || valid_from || not_valid_after || server_public_key). La versión estaba cubierta por la firma y nunca se
comparaba, así que un certificado escrito para un formato posterior verificaba y luego se leía
con la disposición actual. La verificación ahora rechaza cualquier versión distinta de la que
implementa el crate
(commit 15e5969c).
Qué no cambia
Nadie robó hashrate y no consta que se haya leído ninguna sesión cifrada. El caso del contador necesita en particular que una sola sesión envíe más de dieciocho trillones de mensajes, cosa que ninguna conexión de minería hará. Lo que se corrige es la forma del error, un incremento sin nada que lo comprobara, no una pérdida que alguien haya sufrido.
El borrado tampoco es absoluto. zeroize impide que el compilador elimine una escritura que
en apariencia nadie lee; no puede seguir una copia que el asignador de memoria, el sistema
operativo o el archivo de intercambio hayan hecho antes. El cambio estrecha la ventana en la
que el material criptográfico está en memoria. No promete que la ventana esté cerrada.
Nada de esto trata de quién controla la minería. El cifrado impide que la red entre un minero y un pool lea o reescriba el trabajo, y según la especificación es "opcional en la red local" y "obligatorio para el acceso remoto a los nodos superiores". Qué transacciones entran en el bloque es otro protocolo distinto, el que puso una plantilla construida por el minero en el bloque 955.318 en junio.
Y nada de esto está instalado. La versión etiquetada más reciente, la v1.11.1, es del 22 de julio de 2026, anterior a todo este trabajo; estos commits están en la rama principal.
Contexto
Lo interesante es de dónde salieron los hallazgos. Las implementaciones de Bitcoin se están leyendo este año a un ritmo al que no se habían leído antes, con auditorías que abren issues concretos, pequeños y bien descritos en lugar de anunciar un incidente. Seis issues numerados en el repositorio del propio proyecto, doce hallazgos en un repositorio de auditoría y una pull request que los cierra a la vez es el aspecto que tiene ese proceso desde dentro.
Conviene ser preciso sobre lo que produce una auditoría así. No un agujero espectacular, sino una lista de puntos donde el código era correcto en el caso que ocurre e indefinido en el que no: el contador que nadie esperaba que llegara a su límite, el campo que nadie esperaba que cambiara, el búfer que nadie esperaba que se leyera. La distancia entre "no puede pasar" y "está comprobado" es todo el asunto, y es la misma distancia en la que confía un minero cuando apunta una máquina a un pool cuyo interior no puede ver.
