Generación de la semilla y aleatoriedad
Una cartera es tan robusta como la aleatoriedad que la creó. BitTool Signer obtiene toda su aleatoriedad del generador criptográficamente seguro del sistema operativo y nunca de las funciones pseudoaleatorias integradas de JavaScript.
Esta página describe la app de iPhone. BitTool Signer para Android obtiene su aleatoriedad de los equivalentes de Android, que se enumeran en En Android.
La fuente de entropía
Las frases de recuperación nuevas parten de bytes aleatorios obtenidos de SecRandomCopyBytes(kSecRandomDefault, …) de Apple, el CSPRNG del sistema que iOS alimenta con entropía de hardware. La llamada se hace en código Swift nativo:
enum SecureRandom {
static func bytes(count: Int) throws -> Data {
guard (1...65_536).contains(count) else { throw VaultError.randomFailure }
var data = Data(count: count)
let status = data.withUnsafeMutableBytes { buffer in
guard let address = buffer.baseAddress else { return errSecAllocate }
return SecRandomCopyBytes(kSecRandomDefault, count, address)
}
guard status == errSecSuccess else { throw VaultError.randomFailure }
return data
}
static func entropyHex(wordCount: Int = 24) throws -> String {
guard [12, 15, 18, 21, 24].contains(wordCount) else { throw VaultError.invalidSecret }
return try bytes(count: wordCount / 3 * 4).map { String(format: "%02x", $0) }.joined()
}
}
Si el generador informa de cualquier error, la creación de la cartera se detiene con un error. No hay ninguna alternativa a una fuente más débil.
De la entropía a las palabras
La app ofrece 24 palabras (la opción por defecto, 256 bits de entropía) o 12 palabras (128 bits). Los bytes se convierten en palabras con el algoritmo estándar de BIP39:
- Se toma la entropía: 32 bytes para 24 palabras, 16 bytes para 12 palabras.
- Se añaden como checksum los primeros
entropy bits ÷ 32bits deSHA-256(entropy)(8 bits para 24 palabras, 4 bits para 12). - Se divide el resultado en grupos de 11 bits. Cada grupo es un índice en la lista inglesa de 2048 palabras.
La conversión se ejecuta en el motor incluido en la app (@scure/bip39), que recibe la entropía desde el código nativo en hex:
// engine/src/keys.js
export function mnemonic(entropy) {
const b = hexToBytes(entropy, 32);
check([16, 20, 24, 28, 32].includes(b.length), 'ENTROPY', 'Entropy must contain 128–256 bits in 32-bit steps.');
return { mnemonic: entropyToMnemonic(b, wordlist), wordCount: b.length * 3 / 4 };
}
Las carteras nuevas usan siempre la lista de palabras en inglés, que admiten todas las carteras. Las frases en los otros nueve idiomas de BIP39 se pueden importar.
Por ejemplo, 32 bytes a cero (no los uses nunca) dan:
abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art
De la frase a la semilla
La semilla BIP39 de 64 bytes se calcula de forma nativa con CommonCrypto, no en JavaScript, porque en iOS el motor se ejecuta sin JIT y allí PBKDF2 tarda casi un segundo:
seed = PBKDF2-HMAC-SHA512(
password = NFKD(phrase words joined by single spaces),
salt = NFKD("mnemonic" + passphrase),
iterations = 2048,
length = 64 bytes)
El motor recibe solo la semilla de 64 bytes (en hex) para derivar y firmar, nunca la frase, y los búferes nativos se sobrescriben con memset_s después de usarlos. La página de la passphrase BIP39 muestra la semilla de la frase de prueba con y sin passphrase.
Todos los demás valores aleatorios
| Valor | Fuente |
|---|---|
| Entropía de la frase de recuperación | SecRandomCopyBytes, 16 o 32 bytes |
| Sal de la bóveda (PBKDF2) | SecRandomCopyBytes, 32 bytes, nueva en cada cifrado |
| Clave de dispositivo | SecRandomCopyBytes, 32 bytes, una por cartera |
| Nonces de AES-GCM | CryptoKit, 96 bits, nuevos en cada cifrado |
crypto.getRandomValues() dentro del motor | Conectado a SecRandomCopyBytes antes de que se cargue el motor |
Math.random() dentro del motor | Sustituido por una función basada en SecRandomCopyBytes (algunas bibliotecas lo llaman para generar IDs internos) |
| Aleatoriedad auxiliar de Taproot (BIP340) | 32 bytes aleatorios nuevos por firma |
| Nonces de ECDSA (Bitcoin, EVM, TRON) | Deterministas, RFC 6979 |
| Ed25519 (Solana) | Determinista por diseño (RFC 8032) |
El objeto crypto del motor se instala antes de que se ejecute el código de cualquier biblioteca y se congela, de modo que ninguna dependencia puede sustituirlo:
Object.defineProperty(globalThis, 'crypto', {value: Object.freeze({
getRandomValues(array) { /* fills the array from native SecRandomCopyBytes */ }
}), writable: false, configurable: false});
Object.defineProperty(Math, 'random', {value: /* also SecRandomCopyBytes */, writable: false, configurable: false});
Algunos dispositivos de firma permiten que los usuarios añadan su propia entropía (dados o lanzamientos de moneda). BitTool Signer no lo hace: la entropía generada por personas es fácil de hacer mal, y el generador de iOS está bien estudiado. Si un usuario quiere usar su propia entropía, puede generar una frase en otro sitio e importarla.
En Android
| Valor | Origen |
|---|---|
| Entropía de la frase de recuperación, sales de la bóveda, nonces de AES-GCM | java.security.SecureRandom: el generador de BoringSSL (a través de Conscrypt), sembrado por el núcleo de Linux. Cualquier error detiene la operación; no hay alternativa. |
| Clave del dispositivo | Se genera dentro del Keystore de Android y nunca se exporta (en StrongBox si el teléfono lo tiene) |
crypto.getRandomValues() y Math.random() dentro del motor | Se instalan antes de que cargue el motor y quedan congelados; usan arc4random_buf de la biblioteca C de Android, que siembra el núcleo |
| Firmas | El mismo motor que en iPhone: RFC 6979 para ECDSA, RFC 8032 para Ed25519 y aleatoriedad auxiliar nueva para Taproot |
El paso de la frase a la semilla es el mismo cálculo BIP39 PBKDF2-HMAC-SHA512, que la app hace fuera del motor; el motor, de nuevo, solo recibe la semilla.
Qué encontraron las revisiones de seguridad
Las dos revisiones de seguridad de la versión 1.0.0 siguieron este recorrido y lo confirmaron: la entropía procede únicamente de SecRandomCopyBytes, un estado de error detiene la creación de la cartera, no hay ninguna alternativa basada en la hora ni en otra fuente débil, y el objeto crypto y Math.random del motor se instalan antes que el código de cualquier biblioteca y no se pueden sustituir. El informe de GPT-6 Astra añade una matización justa: se trata de un generador criptográficamente seguro siempre que iOS y el dispositivo sean de confianza, no de un flujo certificado de aleatoriedad física, y una frase importada desde otro sitio solo es tan aleatoria como la cartera que la generó.
Cómo se prueba
El conjunto de pruebas nativo comprueba los límites de la función de entropía, un vector PBKDF2 generado de forma independiente con Python y los vectores BIP39 publicados a través del puente real de JavaScriptCore. Las pruebas del motor comprueban que la derivación de la semilla a partir de la frase y a partir de una semilla nativa dan las mismas claves.