Revisiones de seguridad
Antes de su lanzamiento, el código fuente de BitTool Signer 1.0.0 pasó por dos revisiones de seguridad asistidas por IA en cada plataforma, cada una por separado. Los cuatro informes se publican íntegros más abajo. Ninguna encontró problemas de gravedad crítica o alta. Las revisiones de iPhone encontraron un problema de gravedad baja cada una. Las de Android encontraron problemas de gravedad media: cuatro siguen abiertos en la versión publicada de Android 1.0.0, y tres de ellos están en el motor de firma que comparten ambas apps.
- Ninguna es una auditoría de una empresa de seguridad, y en ninguna participó un auditor humano.
- Las revisiones Opus 5 y Opus 5.5 provienen de la misma familia de modelos que escribió o modificó una parte importante del código revisado. Los informes dicen que no son una certificación ni una atestación de terceros.
- Las revisiones GPT-6 Astra se describen como revisiones de ingeniería asistidas por IA preparadas para el propietario del proyecto, no como una certificación de una firma auditora ni una aprobación humana.
- Ambos informes de Android recomiendan corregir sus hallazgos de gravedad media abiertos antes de que la app proteja carteras valiosas. BitTool Signer para Android 1.0.0 se publicó con ellos abiertos; consulta todos los hallazgos.
- Todavía no se ha hecho una auditoría independiente de terceros. Los informes la señalan, junto con pruebas en dispositivos reales y pruebas de ida y vuelta con carteras de terceros, como trabajo pendiente antes de guardar fondos importantes.
BitTool Signer para iPhone
Los dos informes
| Opus 5 | GPT-6 Astra | |
|---|---|---|
| Fecha | 19 de octubre de 2026 | 18 de octubre de 2026 (informe BTS-SEC-2026-1018-R3) |
| Código revisado | Tag v1.0.0, commit f621372 | Árbol de trabajo del 18 de octubre: commit 3d706b7 más 59 archivos modificados y una prueba nueva |
Motor revisado (signer.js, SHA-256) | 6933d0d8…f21077 | 6933d0d8…f21077 |
| Método | Revisión del código fuente línea a línea (3546 líneas de JavaScript y 11 858 de Swift), 11 sondeos de ataque, prueba de mutación de 4000 casos sobre la entrada QR, conjuntos de pruebas, recompilación en un entorno limpio | Revisión del código fuente por subsistemas, comparación con la ronda anterior, datos de prueba (fixtures) independientes de Bitcoin y Solana, prueba de mutación de 1509 casos, conjuntos de pruebas, compilación reproducible, avisos de seguridad de las dependencias |
| Pruebas ejecutadas | 210 del motor, 168 nativas | 210 del motor, 177 nativas y de interfaz |
| Resultado | 0 Críticas · 0 Altas · 0 Medias · 1 Baja · 3 Informativas | 0 Críticas · 0 Altas · 0 Medias · 1 Baja · 4 observaciones |
| Independencia | Autorrevisión del autor de buena parte del código | Asistida por IA, preparada para el propietario del proyecto |
Las dos revisiones examinaron el mismo archivo del motor que incluye BitTool Signer 1.0.0: en él están todos los analizadores de transacciones, todas las reglas de riesgo y todos los planes de firma. El resto de la app (interfaz, Keychain, almacenamiento) se revisó leyendo el código fuente en Swift.
Todos los hallazgos
| ID | Gravedad | Hallazgo | Estado en 1.0.0 |
|---|---|---|---|
| BTS-OP5-01 | Baja | La aceptación de los riesgos altos solo se exige en la interfaz. El motor exige una aceptación explícita para las solicitudes parcialmente decodificadas y opacas, pero no para las de alto riesgo totalmente decodificadas (aprobaciones ilimitadas, setApprovalForAll, cambios de permisos de Safe, quemas de tokens). La app desactiva Sign (firmar) hasta que se marca la casilla, así que un atacante no puede llegar a esta situación en 1.0.0; importa porque el motor es la parte que los usuarios pueden verificar por su hash. | Abierto. El informe recomienda añadir la comprobación al motor en 1.0.1. |
| R3-01 | Baja | Resumen de Bitcoin para una quema con cambio. En una transacción con una salida OP_RETURN de importe distinto de cero y un cambio verificado, el resumen breve de la pantalla de resultado y de Activity (actividad) dice «Every output returns to your wallet» (todas las salidas vuelven a tu cartera). La propia pantalla de firma muestra correctamente la quema, su importe y el riesgo Alto. | Abierto (el motor incluido en 1.0.0 es el que se revisó). |
| BTS-OP5-02 | Informativa | Una clave de localización de Activity se construye a partir de la salida del motor, y SwiftUI la representa como Markdown. Todos los valores que puede contener hoy son etiquetas fijas, así que es un problema latente. | Abierto, latente. |
| BTS-OP5-03 | Informativa | La KDF de contraseñas de la bóveda (PBKDF2-HMAC-SHA256, 600 000 iteraciones) no es memory-hard. Lo mitiga la segunda capa de cifrado bajo una clave del Keychain vinculada al dispositivo: un archivo de bóveda copiado, por sí solo, ni siquiera se puede atacar. | Aceptado; se sugiere Argon2id para un futuro formato de bóveda. |
| BTS-OP5-04 | Informativa | El material de claves Ed25519 intermedio de la derivación de Solana no se borra explícitamente; el control real es descartar el contexto de JavaScript. | Abierto, limitación inherente. |
| O-01 | Informativa | Durante la importación, una búsqueda lenta de «¿ya está aquí esta cartera?» puede terminar después de que se edite la frase y ofrecer restablecer la contraseña de la cartera anterior. El propio restablecimiento vuelve a derivar la identidad de la cartera y rechaza la cartera equivocada; no se modifica ninguna bóveda. | Abierto en la fecha del informe. |
| O-02 | Informativa | Los resúmenes de Safe y Solana proceden de decodificadores acotados, no de ejecutar la transacción. El estado actual del contrato no se conoce sin conexión. | Por diseño; se indica en la pantalla de revisión. |
| O-03 | Informativa | En Swift y JavaScript no se puede garantizar la puesta a cero completa de la memoria; no se probaron el dispositivo físico, el generador aleatorio del sistema operativo ni el binario distribuido. | Limitación. |
| O-04 | Informativa | Seis avisos de seguridad en dependencias exclusivas de desarrollo (3 moderados, 3 bajos). Los paquetes afectados no están en el motor incluido en la app. | Mantenimiento. |
Los dos informes también volvieron a comprobar los hallazgos de dos rondas de revisión anteriores, entre ellos uno de gravedad Alta (el resumen de las transacciones multifirma de Safe con DELEGATECALL y MultiSend anidado) y varios de gravedad Baja en los resúmenes de comisiones de Bitcoin y de quemas de Solana, y confirmaron que estaban corregidos. Opus 5 señala además, como una propiedad del protocolo TRON y no como un defecto, que las firmas de TRON no llevan ningún identificador de red; la app lo indica.
Encontrado después en el motor compartido
BitTool Signer para iPhone 1.0.0 incluye el motor con SHA-256 6933d0d8…f21077. La revisión GPT-6 Astra de la app de Android reprodujo sus hallazgos sobre el motor exactamente con este archivo, y el código de Bitcoin, EVM y TRON detrás de los hallazgos de Opus 5.5 es idéntico en él. Por eso estos hallazgos del motor también se aplican a iPhone 1.0.0:
| ID | Gravedad | Qué significa en iPhone 1.0.0 |
|---|---|---|
| BTS-02, BTS-07 | Media | Una quema, un cambio de autoridad o una delegación de Solana enviados junto con una transferencia diminuta se resumen como esa transferencia. Las advertencias de los detalles y la confirmación del riesgo se mantienen. |
| BTS-08 | Media | El cambio de Bitcoin se acepta en cualquier índice de dirección de los perfiles de firma única de la cartera, incluidos índices que una cartera de solo lectura nunca revisará. |
| BTS-09 | Baja–Media | El resumen guardado omite la moneda nativa enviada con una llamada a un contrato EVM o TRON desconocido. La tarjeta de revisión sí la muestra. |
R3-01, más arriba, es el mismo defecto que BTS-04 de Android, que el motor de Android corrige. El informe de Opus 5.5 recomienda corregir los demás en el motor compartido, y la corrección llega entonces a ambas apps.
Qué confirmaron las revisiones
| Área | Confirmado | Detalles |
|---|---|---|
| Firma | En las compilaciones Release, la única forma de firmar es mediante un plan fijo. Seis comprobaciones independientes vinculan la firma a la solicitud revisada, a la cartera del plan y a la clave aprobada, y el motor vuelve a derivarlo todo a partir de la semilla descifrada antes de firmar. Confirmado mediante ejecución. | Antes de firmar |
| Aleatoriedad | Las frases de recuperación proceden de SecRandomCopyBytes; un error detiene la creación sin ninguna alternativa. crypto.getRandomValues y Math.random del motor se conectan a la misma fuente. | Generación de la semilla |
| Almacenamiento de claves | Dos capas AES-GCM con cabeceras autenticadas, una clave del Keychain exclusiva del dispositivo y una limitación de los intentos fallidos que se mantiene tras reiniciar. | Almacenamiento de claves |
| Rechazos | Se cumplen todos los rechazos que describe esta documentación: SIGHASH_NONE/SINGLE/ANYONECANPAY, entradas SegWit v0 sin la transacción anterior, tablas de búsqueda de direcciones de Solana, EIP-7702, transacciones blob, eth_sign sobre un hash sin procesar y chain IDs no compatibles. | Límites y errores |
| Entrada no fiable | 5509 entradas QR, CBOR, PSBT y JSON mutadas entre las dos revisiones: ninguna se aceptó siendo no válida y no hubo ninguna excepción no controlada. | UR y CBOR |
| Sin conexión | No se enlaza ni CFNetwork ni Network.framework, y el puente de JavaScript no expone ninguna API de red. | Modelo de seguridad |
| Cadena de suministro | El motor se recompila byte a byte a partir del tag; las dependencias de ejecución están fijadas a versiones exactas; la auditoría de las dependencias de producción no notificó ninguna vulnerabilidad. | Más abajo |
Qué no cubrieron
Las pruebas en dispositivos físicos (inyección de fallos, volcados de memoria, canales laterales), el generador aleatorio del sistema operativo y el funcionamiento interno del Secure Enclave, la procedencia del binario de la App Store más allá del archivo del motor, las transacciones en redes reales, las pruebas de ida y vuelta con carteras de terceros, una auditoría completa de las dependencias criptográficas, la revisión lingüística de los 21 idiomas de la interfaz y la verificación formal. Un conjunto de pruebas superado cubre los casos que prueba, no todas las transacciones ni todos los dispositivos.
Verifica el motor por tu cuenta
El ejecutable de la App Store no se puede reproducir byte a byte (Apple lo vuelve a firmar y lo cifra), pero el motor es un recurso sin cifrar que se puede comparar. Compílalo a partir del tag:
git clone https://github.com/bitbill/bittool-signer-ios
cd bittool-signer-ios && git checkout v1.0.0
cd engine && npm ci --ignore-scripts && npm run build
shasum -a 256 ../BitToolSigner/Resources/signer.js
# 6933d0d8a6f60faa8222506ac0bd7eef80eb884c9f73e31e9f354b4740f21077 (1,181,026 bytes)
Después compáralo con el archivo de la app que tienes instalada: descarga la app como .ipa con Apple Configurator, descomprímela y calcula el hash de Payload/BitToolSigner.app/signer.js. El procedimiento completo, incluido el punto en el que termina la verificación, está en el archivo Reproducible-Build.md del repositorio.
BitTool Signer para Android
Los dos informes
| Opus 5.5 | GPT-6 Astra | |
|---|---|---|
| Fecha | 25 de octubre de 2026 | 24 de octubre de 2026 |
| Código revisado | Versión publicada 1.0.0 (código de versión 1000001): commit de código 71af693, registro de publicación 62996b8 | Commit e7e71fb, anterior a la publicación: una nueva auditoría de los cambios desde la referencia anterior bb1e656 |
Motor revisado (signer.js, SHA-256) | 4b371759…00fa19 | 6933d0d8…f21077, el motor que incluye iPhone 1.0.0 |
| Método | Revisión del código fuente en cinco áreas, cada una seguida de principio a fin; cada hallazgo comprobado de nuevo en el código; reproducciones del motor con datos sintéticos; revisión de dependencias y artefactos | Revisión del código modificado y de quienes lo llaman, verificación de tres correcciones, reproducciones del motor con el paquete confirmado, comprobaciones automáticas de regresión |
| Pruebas ejecutadas | 2.075 pruebas de la app, 217 del motor y el script de verificación de la publicación, 25 de 25 | 1.616 pruebas de Android; 210 del motor, una fallida y otra que pasó sin ejecutarse (BTS-06) |
| Resultado | 0 críticos · 0 altos · 4 medios (uno calificado Baja–Media) · 9 bajos · notas informativas | 0 críticos · 0 altos · abiertos: 1 medio, 1 bajo, 1 de validación · 3 cerrados |
| Recomendación | Corregir BTS-07, BTS-08 y BTS-10 antes de un lanzamiento general que proteja carteras valiosas | Posponer, en esa revisión, el lanzamiento general para carteras valiosas |
| Independencia | La misma familia de modelos escribió gran parte del código revisado | Asistida por IA; no se modificó código de producción |
Ambas revisiones examinaron la app de Android completa: los módulos de Kotlin, el anfitrión nativo (JNI sobre QuickJS y el PBKDF2 nativo) y el motor de firma compartido con iPhone.
Todos los hallazgos
Opus 5.5 revisó el código publicado, así que estos son los hallazgos de BitTool Signer para Android 1.0.0.
| ID | Gravedad | Hallazgo | Dónde |
|---|---|---|---|
| BTS-07 | Media | Los cambios de autoridad en Solana se esconden tras una transferencia diminuta. Una solicitud que cambia el propietario o la autoridad de una cuenta, aprueba a un delegado o lleva una instrucción opaca, junto con una transferencia de 1 lamport, recibe esa transferencia como titular y como resumen guardado. Las advertencias de riesgo alto siguen en los detalles y la confirmación del riesgo sigue siendo obligatoria. | Motor compartido, también en iPhone |
| BTS-08 | Media | El cambio de Bitcoin se acepta en cualquier índice. Una salida cuenta como cambio verificado si su script coincide con cualquiera de los perfiles de firma única de la cartera, en la rama 0 o 1, en cualquier índice hasta 2³¹−1. Los fondos enviados allí se revisan como "Todas las salidas vuelven a tu cartera", pero una cartera que revisa un rango limitado de direcciones puede no encontrarlos nunca. | Motor compartido, también en iPhone |
| BTS-09 | Baja–Media | El resumen guardado omite el valor nativo en llamadas a contratos. Una llamada a un contrato EVM o TRON desconocido que también envía la moneda nativa se guarda como "Llamada a contrato desconocido". La tarjeta de revisión y su advertencia sí muestran el valor. | Motor compartido, también en iPhone |
| BTS-10 | Media | Clave de firma de las versiones en la máquina de desarrollo. La clave y sus contraseñas estaban guardadas en texto plano en el árbol de trabajo, excluidas de Git. | Proceso de publicación |
| BTS-11 | Baja | La cámara de QR puede seguir escaneando detrás de la pantalla del bloqueo de la app. Firmar sigue requiriendo la contraseña de la cartera. | App de Android |
| BTS-12 | Baja | La confirmación del riesgo para solicitudes de riesgo alto totalmente decodificadas solo se exige en la interfaz. | App de Android (iPhone: BTS-OP5-01) |
| BTS-13 | Baja | Tras un error del Keystore en Android 8–11, la clave que autentica los datos públicos de las carteras puede regenerarse, y las carteras necesitan entonces Reparar. No se pierden secretos ni fondos. | App de Android |
| BTS-14 | Baja | Dos claves auxiliares no comprueban si hay bloqueo de pantalla, y parte de la documentación exagera la protección. La bóveda en sí sigue exigiéndolo. | App de Android |
| BTS-15 | Baja | La afinidad de tareas predeterminada permite el secuestro de tareas en versiones antiguas de Android. | App de Android |
| BTS-16 | Baja | La comprobación de la copia de seguridad se puede superar por ensayo y error. | App de Android (igual en iPhone, según el informe) |
| BTS-17 | Baja | Una Tarjeta de recuperación de una frase de 12 palabras deja unos 40 bits de entropía. | App de Android |
| BTS-18 | Baja | Cancelar el emparejamiento con la cartera puede dejar la revisión en "Buscando la cartera correspondiente…". | App de Android |
| BTS-19 | Baja | Android 8.0–9 no puede detectar superposiciones que cubren solo parte de un control de consentimiento. La contraseña sigue siendo obligatoria. | App de Android |
El informe también incluye notas informativas, entre ellas: el hilo del motor no tiene límite de tiempo de CPU (solo afecta a la disponibilidad), la versión de Google Play se firma con la clave del APK hasta que se configure una clave de subida aparte, la espera entre reintentos se basa en el reloj del sistema y hay copias breves de la frase que la JVM no puede borrar.
GPT-6 Astra revisó un commit anterior. Sus hallazgos y su situación en la versión publicada:
| ID | Gravedad | Hallazgo | En e7e71fb | En Android 1.0.0 |
|---|---|---|---|---|
| BTS-01 | Media | Los controles de firma y de riesgo parcialmente cubiertos podían pulsarse a través de una superposición | Cerrado | Cerrado |
| BTS-02 | Media | Una quema o un cambio de autoridad de Solana mezclados con una transferencia diminuta se resumen como la transferencia | Abierto | Quemas corregidas; los cambios de autoridad siguen como BTS-07 |
| BTS-03 | Baja | Los búferes con secretos podían sobrevivir a una operación cancelada | Cerrado | Cerrado |
| BTS-04 | Baja | Una quema de Bitcoin con cambio se resume como "Todas las salidas vuelven a tu cartera" (R3-01 de iPhone) | Abierto | Corregido |
| BTS-05 | Funcional | Un escaneo de QR interrumpido no se reanudaba | Cerrado | Cerrado |
| BTS-06 | Validación | Rutas de archivo obsoletas hacían fallar una prueba del motor y que otra pasara sin ejecutarse | Abierto | Corregido (pasan las 217 pruebas del motor) |
Lo que confirmaron las revisiones
| Área | Confirmado |
|---|---|
| Firma | No hay forma de saltarse la contraseña ni de firmar nada que no sea la solicitud revisada y emparejada. El plan queda ligado al hash de la solicitud, al hash del plan y al resumen de los bytes que se firman; el motor lo vuelve a resolver con la semilla descifrada y rechaza cualquier cambio en una transacción de Bitcoin sin firmar. Los resultados que llegan tarde tras una cancelación, el bloqueo de la app o una nueva solicitud se descartan. |
| Almacenamiento de claves | PBKDF2-HMAC-SHA256 con 600.000 iteraciones en código nativo, AES-256-GCM con un nonce y una sal nuevos en cada cifrado y, encima, una clave del Android Keystore por cartera, no exportable (en StrongBox cuando el teléfono lo tiene). Cada intento de contraseña se cuenta antes de derivar la clave, y un contador dañado falla de forma cerrada. |
| Secretos en memoria | Los secretos desbloqueados viven en contenedores que se cierran tras su uso y se borran incluso si la operación se cancela. La frase de recuperación se escribe en el teclado propio de la app, que ningún método de entrada ve. |
| Anfitrión del motor | El paquete está fijado por su SHA-256 y su longitud; QuickJS funciona sin eval ni Function, en un entorno de ejecución nuevo para cada operación que toca un secreto o una entrada no confiable, con límites de memoria y de pila. |
| Sin conexión y plataforma | Sin permiso de Internet y sin código de red. Solo la pantalla principal es accesible desde fuera, sin enlaces profundos; las copias de seguridad y la transferencia entre dispositivos están excluidas; ninguna ventana aparece en capturas, grabaciones ni en Recientes; las superposiciones de otras apps se ocultan en Android 12 y posteriores. El bloqueo de la app nunca autoriza una firma, y la versión publicada no registra nada. |
| Cadena de suministro | El motor recompilado tras un npm ci --ignore-scripts limpio es idéntico byte a byte al archivo confirmado y a la copia del APK. La auditoría de dependencias de producción no informa de avisos. |
Lo que no cubrieron
Pruebas en dispositivos reales (el comportamiento del Keystore y de StrongBox de distintos fabricantes, la biometría, las superposiciones y la captura de pantalla), ejercicios con superposiciones maliciosas, canales laterales, la ejecución del código nativo en ARM, transacciones en cadenas reales, un análisis de CVE de las dependencias de Maven y nativas, y la copia que compila Google Play. Ambos informes piden comprobar la versión publicada en dispositivos representativos desde Android 8 hasta la versión más reciente, y Opus 5.5 recomienda una auditoría humana independiente antes de que la app proteja fondos importantes.
Verifica el APK y el motor
El APK publicado en bittool.com/signer se puede comprobar por completo: su hash, su certificado de firma, el motor que contiene y la ausencia del permiso de Internet (aapt2 dump badging no muestra android.permission.INTERNET).
# 1. The APK from bittool.com/signer
shasum -a 256 BitToolSigner-1.0.0-1000001.apk
# 109dbf5c622ee77672904c34a10229df3c9b0eac45b07096c510b1e1e169c2a4
apksigner verify --print-certs BitToolSigner-1.0.0-1000001.apk
# Signer #1 certificate DN: CN=BITTOOL PTE. LTD., O=BITTOOL PTE. LTD., C=SG
# Signer #1 certificate SHA-256 digest: af41ed101765393ae89038eb4c6fc7fa96d930f6fcd76b881437a39b5a9e4631
# 2. The engine inside it
unzip -p BitToolSigner-1.0.0-1000001.apk assets/engine/signer.js | shasum -a 256
# 4b3717599e8ee3ed9d2480e640e7203731543cc8ab0f2c3cec3b78e6f400fa19 (1,181,432 bytes)
# 3. The same engine, rebuilt from source
git clone https://github.com/bitbill/bittool-signer-android
cd bittool-signer-android && git checkout 71af693
cd engine && npm ci --ignore-scripts && npm run build
shasum -a 256 ../core/engine/src/main/assets/engine/signer.js
El certificado nunca cambia entre versiones, Android solo instala una actualización que lleve el mismo y la app lo muestra en Ajustes › Totalmente de código abierto › Esta versión. Una copia instalada desde Google Play es un archivo distinto, porque Google Play la compila a partir del paquete subido, así que su hash no se puede comparar; su assets/engine/signer.js no cambia y se puede comparar del mismo modo tras un adb pull desde un ordenador. El procedimiento completo está en el Reproducible-Build.md del repositorio. Comprobamos los valores anteriores con el APK publicado el 25 de octubre de 2026.
Descarga los informes
| Informe | Páginas | SHA-256 |
|---|---|---|
| iPhone · Opus 5, 19 de octubre de 2026 (PDF, 118 KB) | 17 | ff7ddd17e5dffccd99db6aaa43b8f0041167b58bd9b2bd7cc11d185e779bca1a |
| iPhone · GPT-6 Astra, informe R3, 18 de octubre de 2026 (PDF, 46 KB) | 16 | 02d7a358f8f35b2978eab7db1292add4365a94b736ff79e158dee3afb842b64a |
| Android · Opus 5.5, 25 de octubre de 2026 (PDF, 252 KB) | 8 | 8242d58aeec1aa88823eb0a938c9461b0681abfab3f37805e2f77a41b3bba34c |
| Android · GPT-6 Astra, nueva auditoría, 24 de octubre de 2026 (PDF, 22 KB) | 7 | b37aaefd40d0d522384a86255accadeb976067175d76e38466a1b4c90b036f53 |
Los resúmenes de esta página son nuestros; cuando su redacción difiera de la de un informe, prevalece el informe.