La falla de entropía de COLDCARD: cómo un fallback silencioso del RNG costó 38 millones de dólares a los usuarios

AbbieAbbie
/Actualizado el 3 ago 2026
La falla de entropía de COLDCARD: cómo un fallback silencioso del RNG costó 38 millones de dólares a los usuarios

Puntos clave

  • Los usuarios que generaron y usaron una seed phrase en un COLDCARD Mk3 deben generar de inmediato una nueva seed phrase en otro wallet y migrar sus fondos.
  • Los usuarios de COLDCARD Mk4 o Mk5 deben migrar si la seed phrase original fue generada con una versión de firmware inferior a 5.6.0.
  • Los usuarios de COLDCARD Q deben migrar si la seed phrase original fue generada con una versión de firmware inferior a 1.5.0Q.
  • Los hardware wallets OneKey no están afectados por este bug y pueden seguir usándose con confianza.

En criptografía, “aleatorio” es una métrica exigente. Es algo que se puede cuantificar y que debe resistir frente a potencia de cómputo real.

Una frase de recuperación de 12 palabras está respaldada por un número aleatorio de 128 bits, codificado en palabras legibles para humanos bajo la especificación BIP-39. Qué tan impredecible sea ese número determina directamente si el wallet puede resistir fuerza bruta. 128 bits significan 2¹²⁸ posibilidades. Aun juntando toda la potencia de cómputo de la Tierra, no podrías terminar de enumerarlas antes de que termine el universo.

Por eso, en teoría, los hardware wallets deberían ser extremadamente rigurosos con esto. Debe existir un generador verdadero de números aleatorios dedicado (TRNG), que tome aleatoriedad física del ruido de los circuitos, junto con un secure element (SE) certificado EAL, y un algoritmo maduro que mezcle todo al final.

El 30 de julio de 2026, aproximadamente 594 BTC (unos 38 millones de dólares) fueron barridos de casi 500 wallets COLDCARD en menos de media hora. Al día siguiente, el fabricante Coinkite confirmó la causa raíz en “Technical Deep Dive into the Entropy Issue” [1]: desde un cambio de firmware en marzo de 2021, un error de configuración de build había estado haciendo que el código cayera silenciosamente en el generador pseudoaleatorio de software (PRNG) integrado de MicroPython.

Las consecuencias varían según el modelo. La estimación preliminar de Coinkite es que el Mk3 conserva apenas unos 40 bits de espacio de búsqueda efectivo, y los Mk4, Mk5 y Q alrededor de 72, frente a un objetivo de diseño de 128. Cuarenta bits son aproximadamente 1.1 billones de posibilidades, lo que todavía suena astronómico, pero para un atacante que puede alquilar un clúster de GPU pasó de “físicamente imposible” a un proyecto de ingeniería viable en expectativa. De 128 a 40, el espacio de búsqueda se redujo por un factor de 2⁸⁸, o 300 septillones.

Este robo de 38 millones de dólares convirtió esa expectativa en un hecho consumado.

Nota: los fondos de los usuarios de OneKey están seguros. Ningún código de OneKey toca este problema, y OneKey no usa las implementaciones ni dependencias involucradas. Declaración completa: https://x.com/OneKeyHQ/status/2083106588022509974?s=20

TL;DR

  • Los usuarios que generaron y usaron una seed phrase en un COLDCARD Mk3: generen de inmediato una seed phrase en otro wallet y migren sus fondos al nuevo wallet.
  • Los usuarios que generaron y usaron una seed phrase en un COLDCARD Mk4 o Mk5: si la versión de firmware que generó la seed phrase original era inferior a 5.6.0, generen de inmediato una seed phrase en otro wallet y migren sus fondos al nuevo wallet.
  • Los usuarios que generaron y usaron una seed phrase en un COLDCARD Q: si la versión de firmware que generó la seed phrase original era inferior a 1.5.0Q, generen de inmediato una seed phrase en otro wallet y migren sus fondos al nuevo wallet.
  • Los usuarios de hardware wallets OneKey no están afectados por este bug y pueden seguir usándolos con confianza.

Ahora viene la parte técnica dura 👇

Causa raíz: un fallback de software silencioso

De la interfaz RNG de hardware a libNgU

La migración de la pila criptográfica en 2021 cambió la generación de seeds del wallet de ckcc.rng_bytes() a ngu.random.bytes() [6]:

ngu.random.bytes() → libNgU rng_get() → el símbolo rng_get al que finalmente enlaza

El riesgo no está en el nombre de la función ni en la longitud de la salida. Está en qué archivo objeto suministra realmente ese rng_get() final.

#ifndef solo comprueba si algo está definido, no su valor

La configuración de la placa objetivo definía MICROPY_HW_ENABLE_RNG como 0, lo que significaba que la ruta de RNG de hardware de MicroPython no estaba habilitada.

Pero en ese momento, libNgU usaba [8]:

#ifndef MICROPY_HW_ENABLE_RNG
#error ...
#endif

#ifndef solo comprueba si la macro existe. Una macro definida como 0 sigue estando “definida”, así que el #error que debía bloquear un build incorrecto nunca se activó.

Un símbolo con el mismo nombre permitió que la implementación equivocada entrara en el firmware final

Cuando el RNG de hardware no está habilitado, MicroPython aun así proporciona un rng_get() con el mismo nombre, pero es el fallback PRNG de software Yasmarang [9].

Eso produjo toda la cadena de falla:

la macro existe, pero su valor es 0 → la guarda de build de libNgU no se activa → el firmware todavía compila correctamente → el rng_get con el mismo nombre se resuelve al fallback de MicroPython → la generación de seed nunca recibe la entrada del RNG de hardware que debía recibir

Tomado de forma aislada, cada eslabón de esta cadena es solo un pequeño descuido. Encadenados, vuelven inútil el control del sistema de build: que una implementación segura exista en el código fuente o en el binario no significa que la llamada crítica de seguridad llegue realmente a ella.

Mezclar fuentes de entropía no compensa la entropía ausente

El estado inicial del fallback involucra parte del UID, SysTick, la hora RTC y un estado subsegundo, después de lo cual Yasmarang lo avanza de manera determinista. libNgU también lo mezcla con un segundo flujo Yasmarang que parte de un estado inicial fijo.

Dos flujos deterministas que pueden reconstruirse o enumerarse no ganan nueva imprevisibilidad física por ser combinados con XOR o pasados por SHA-256. El hashing puede acondicionar la entrada, pero no puede expandir un conjunto finito de candidatos en un verdadero espacio desconocido de 2^256.

Qué significan realmente 40 bits y 72 bits

Las estimaciones de espacio de búsqueda efectivo que usa Coinkite no son valores de entropía medidos ni certificados. Los dos números describen el tamaño del espacio de estados candidatos que un atacante debe recorrer. No son una medición de min-entropía, no se convierten en un número promedio de intentos y, desde luego, no dan un tiempo o costo de ataque específico.

El costo real de un ataque depende de una cadena de precondiciones: si el atacante conoce o puede adivinar el UID del dispositivo; si puede acotar el rango de RTC, SysTick y tiempo de arranque; cuántas llamadas RNG ocurrieron antes de que se generara la seed; si tiene a mano un xpub, una dirección o una clave pública para validar candidatos offline; y qué derivación BIP-39/BIP-32 y costo de hardware paralelo debe ejecutar.

Así que la forma precisa de decirlo es esta: bajo los supuestos preliminares actuales de ataque de Coinkite, el espacio de búsqueda efectivo del Mk3 se estima en unos 40 bits, y el de Mk4/Q/Mk5 en unos 72. Eso no equivale a decir “cada wallet Mk3 tiene exactamente 40 bits de entropía”, ni “los modelos más nuevos han demostrado tener seguridad criptográfica de 72 bits”, y mucho menos “cualquier computadora común puede recuperar todos los wallets en una cantidad fija de tiempo”.

Por qué los Mk4, Mk5 y Q también están afectados

The two secure elements on the Mk5 and Q boards (source: coldcard.com)The two secure elements on the Mk5 and Q boards (source: coldcard.com)

Los dos secure elements en las placas Mk5 y Q (fuente: coldcard.com)

Los Mk4, Mk5 y Q sí extraen material aleatorio de dos secure elements, pero la implementación previa al fix no alimentaba toda esa salida a un DRBG criptográfico.

El código fuente público muestra que, después de hashear el material del secure element, toma solo cuatro bytes y los pasa a ngu.random.reseed(), reemplazando apenas una palabra de estado de 32 bits de Yasmarang.

Así, cuando el resto del estado del fallback y la traza de llamadas están fijos:

número de flujos de salida distinguibles por el reseed del secure element ≤ 2³²

Esto no contradice el “alrededor de 72 bits” oficial. El modelo de 72 bits también cuenta incertidumbre en estados como el UID, timers, RTC e historial de llamadas. El límite superior combinado y aproximado de Block Engineering queda por debajo de 2^73.3, y ellos declaran explícitamente que esto no es una certificación de seguridad criptográfica de 73 bits [10].

En otras palabras: el Mk3 es el peor caso, porque no hay reseed seguro en absoluto. La entrada del secure element en Mk4/Q/Mk5 reduce el riesgo, pero un reseed de cuatro bytes no restaura la seguridad de 128 bits a la que apuntaba el diseño.

El aviso oficial y el alcance real

El aviso actual de Coinkite empieza en Mk3 4.0.1. Pero el código fuente inmutable v4.0.0 ya contiene la ruta relevante de generación de entropía [7], y no hay un fix RNG correspondiente entre 4.0.0 y 4.0.1.

Block Engineering va más allá e incluye Mk2/Mk3 v4.0.0–v4.1.9 en su análisis de código fuente [10].

Por eso hay dos niveles que deben mantenerse separados:

  • El alcance oficial de Coinkite: Mk3 4.0.1+;
  • El alcance técnico respaldado por el código fuente: Mk2/Mk3 v4.0.0–v4.1.9.

No se puede decir que Coinkite haya confirmado oficialmente Mk2 o v4.0.0. Tampoco se puede inferir que son seguros solo porque el aviso no los menciona.

Cómo funciona el fix

El hotfix en Mk4/Mk5 5.6.0 y Q 1.5.0Q hizo más que cambiar una expresión condicional. Añadió enforcement a nivel de build [3][4][5]:

  • Excluir el objeto RNG fallback de MicroPython;
  • Asegurar que el objeto a nivel de placa proporcione el rng_get() global;
  • Usar nm para inspeccionar los símbolos en los archivos objeto;
  • Hacer fallar el build de inmediato si el fallback todavía exporta el símbolo, o si el objeto a nivel de placa no proporciona correctamente rng_get().

Qué deben hacer los usuarios

Mk4, Mk5 y Q

  1. Primero actualiza a 5.6.0 o posterior (Mk4/Mk5), o a 1.5.0Q o posterior (Q).
  2. Genera una seed completamente nueva en el firmware corregido.
  3. Registra la copia de seguridad y completa una verificación de restauración.
  4. Verifica la huella del wallet y la dirección de recepción en la pantalla del dispositivo.
  5. Envía una pequeña transacción de prueba antes de migrar el resto de tus fondos.
  6. Conserva la copia antigua hasta que todas las transferencias estén confirmadas, pero deja de recibir fondos en el wallet antiguo.

Mk3

Actualmente no hay un firmware fix publicado para el Mk3, y 4.1.9 no es un fix. Los usuarios no deberían esperar un firmware que quizá llegue más adelante, porque ningún firmware nuevo puede añadir entropía a una seed existente.

Sigue la guía de migración actual de Coinkite: genera una nueva seed por una ruta confiable, verifica la copia de seguridad, las direcciones y una pequeña transacción, y luego migra tus activos.

Dos umbrales distintos para los dados

Hay dos números para dados: 50 y 99. Sirven para dos objetivos diferentes y no se pueden usar indistintamente.

50+ lanzamientos añadidos durante la generación original de la seed. Si al menos 50 lanzamientos justos, independientes y privados de un dado de seis caras, que nunca fueron registrados, almacenados ni divulgados, se mezclaron cuando la seed se generó por primera vez, Coinkite dice que no considera esa seed en riesgo por este problema de RNG por sí solo [11].

50 × log₂(6) ≈ 129.25 bits

Ese es el umbral aproximado de 128 bits para este incidente específico, no una prueba absoluta de seguridad de todo tu proceso operativo.

El flujo Dice Roll Import en un Mk3 en blanco. Coinkite también ofrece un flujo diferente: en un Mk3 en blanco con 4.1.9, selecciona Import Existing > Dice Rolls e introduce al menos 99 lanzamientos justos. Esta ruta dedicada consume directamente la secuencia de dados y no usa el generador afectado del dispositivo.

99 × log₂(6) ≈ 255.91 bits

99 es el umbral del flujo oficial; si se trabaja hacia atrás desde un estándar literal de “al menos 256 bits”, necesitarías 100.

Una passphrase es una barrera separada, no un fix

Una passphrase BIP-39 fuerte, única, aleatoria y secreta deriva un wallet diferente y puede añadir un factor de búsqueda independiente; el PIN del dispositivo solo controla el acceso local y no participa en la derivación de la clave raíz BIP-39 [12].

Pero una passphrase no puede compensar la entropía que el mnemónico nunca tuvo. Las passphrases cortas, citas famosas, patrones predecibles y contraseñas reutilizadas aún pueden adivinarse. Un error tipográfico también generará un wallet que parece válido pero es completamente distinto, así que debes verificar la huella y mantener una copia de seguridad confiable cuya restauración hayas probado.

Cierre

El problema central que expuso este incidente es que el sistema de build nunca hizo cumplir la alcanzabilidad real de una implementación crítica de seguridad. “Alguien se equivocó con una macro” se queda muy corto para describirlo.

Como mínimo, los hardware wallets y otros sistemas de generación de claves deberían:

  • Verificar a qué objeto y símbolo enlaza finalmente una API de seguridad, no solo comprobar la firma de la función;
  • Hacer que los controles de capacidad verifiquen tanto la existencia como el valor de una macro;
  • Hacer que los fallbacks críticos de seguridad fallen cerrados, nunca suministrando silenciosamente un PRNG ordinario;
  • Hacer que CI inspeccione la composición de objetos, link maps, procedencia de símbolos y flujo de datos de extremo a extremo;
  • Probar mediante tests que la entropía real de hardware llega a la seed final, en lugar de comprobar solo la longitud de salida, que no sea cero o que no se repita;
  • Escribir avisos que separen claramente la versión que generó la seed, el firmware actual, la versión corregida y qué hacer con seeds antiguas.

El código abierto de COLDCARD fue lo que permitió a investigadores externos reconstruir esta cadena de llamadas, y Coinkite publicó tanto un informe técnico formal como una versión corregida.

Afirmar que una marca es “absolutamente segura” no vale nada. Lo que sí vale es convertir “de dónde viene la aleatoriedad, a qué enlaza finalmente, adónde va cuando falla y cómo verificarlo en el producto entregado” en una propiedad del sistema que se pueda volver a comprobar continuamente.

Referencias

[1] Coinkite: Technical Deep Dive into the Entropy Issue [2] Coinkite: Mk3 Security Advisory [3] Coldcard 5.6.0 / 1.5.0Q fix commit [4] Coldcard 5.6.0 release commit [5] Coldcard 1.5.0Q release commit [6] 2021 libNgU seed generation migration commit [7] Official v4.0.0 source snapshot [8] libNgU random.c [9] MicroPython commit introducing the fallback [10] Block Engineering technical analysis [11] Coldcard dice roll math [12] Coldcard passphrase documentation [13] OneKey trezorcrypto.random source branch [14] OneKey seed generation logic [15] OneKey standard Makefile [16] OneKey SCons config

Asegura tu viaje cripto con OneKey

View details for Comprar OneKeyComprar OneKey

Comprar OneKey

La cartera de hardware más avanzada del mundo.

View details for Descargar aplicaciónDescargar aplicación

Descargar aplicación

Opera con activos globales. Empieza en minutos solo con tu email.

View details for OneKey SifuOneKey Sifu

OneKey Sifu

Claridad cripto — a una llamada de distancia.

Seguir leyendo