Generación de la semilla y aleatoriedad
Una billetera 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 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 reporta cualquier error, la creación de la billetera se detiene con un error. No hay una alternativa a una fuente más débil.
De la entropía a las palabras
La app ofrece 24 palabras (el valor predeterminado, 256 bits de entropía) o 12 palabras (128 bits). Los bytes se convierten en palabras con el algoritmo BIP39 estándar:
- Toma la entropía: 32 bytes para 24 palabras, 16 bytes para 12 palabras.
- Agrega como checksum los primeros
entropy bits ÷ 32bits deSHA-256(entropy)(8 bits para 24 palabras, 4 bits para 12). - Divide el resultado en grupos de 11 bits. Cada grupo es un índice en la lista de 2,048 palabras en inglés.
La conversión se ejecuta en el motor integrado (@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 billeteras nuevas siempre usan la lista de palabras en inglés, que todas las billeteras admiten. Las frases en los otros nueve idiomas BIP39 se pueden importar.
Por ejemplo, 32 bytes en cero (nunca uses esto) 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 la derivación y la firma, nunca la frase, y los búferes nativos se sobrescriben con memset_s después de usarse. La página de 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 del dispositivo | SecRandomCopyBytes, 32 bytes, una por billetera |
| 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 | Reemplazado por una función respaldada por SecRandomCopyBytes (algunas bibliotecas lo llaman para 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 cualquier código de bibliotecas y se congela, así que ninguna dependencia puede reemplazarlo:
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 firmadores permiten que los usuarios agreguen su propia entropía (tiradas de dados o 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 lugar 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.
Lo que encontraron las revisiones de seguridad
Ambas revisiones de seguridad de la versión 1.0.0 siguieron este recorrido y lo confirmaron: la entropía proviene únicamente de SecRandomCopyBytes, un estado de error detiene la creación de la billetera, no hay ninguna alternativa basada en la hora ni ninguna otra alternativa débil, y el objeto crypto y Math.random del motor se instalan antes de cualquier código de bibliotecas y no se pueden reemplazar. El informe de GPT-6 Astra agrega una salvedad justa: se trata de un generador criptográficamente seguro bajo el supuesto de que iOS y el dispositivo son confiables, no de un flujo certificado de aleatoriedad física, y una frase importada de otro lugar es solo tan aleatoria como la billetera que la generó.
Cómo se prueba
La suite de pruebas nativa verifica 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 verifican que la derivación de la semilla a partir de la frase y a partir de una semilla nativa produzca las mismas claves.