Qué verifica el firmador antes de firmar
Un código QR que proviene de un dispositivo en línea es una entrada no confiable. BitTool Signer trata cada solicitud como potencialmente hostil y ejecuta el mismo proceso para todas las cadenas:
- 1Reúne los fotogramas QR y decodifica la solicitud de forma estricta (CBOR, PSBT, bytes de la transacción).
- 2Analiza los bytes exactos que se firmarán y construye la revisión: montos, destinatarios, comisiones, llamadas a contratos, advertencias.
- 3Asocia la solicitud con una billetera local usando solo claves públicas y fija un plan de firma vinculado al hash de la solicitud.
- el usuario revisa, confirma los riesgos e ingresa la contraseña ▶
- 4Vuelve a derivar las claves a partir de la semilla y las compara con el plan antes de usarlas.
- 5Firma, verifica la nueva firma y codifica la respuesta en el mismo formato de la solicitud.
Decodificación estricta
Antes de que se ejecute cualquier lógica específica de la cadena, se valida el transporte:
- El CBOR debe usar longitudes definidas, sin claves de mapa duplicadas ni bytes sobrantes, con un máximo de 24 niveles y 20,000 elementos. Se rechazan los campos de solicitud desconocidos.
- Los códigos QR animados deben provenir de una sola sesión. Mezclar fotogramas de dos solicitudes es un error, no una combinación.
- Límites de tamaño: 2,048 fotogramas, 4,300 caracteres por fotograma, 1 MiB de texto escaneado, 128 KiB de carga útil decodificada.
Los bytes CBOR (o PSBT) originales permanecen asociados a la solicitud decodificada. Justo antes de firmar, el motor los vuelve a decodificar y se niega a firmar si ya no coinciden con lo que se revisó.
La revisión muestra los bytes reales
El motor analiza la transacción o el mensaje por sí mismo; nunca confía en resúmenes proporcionados por la billetera en línea. Por ejemplo:
- Bitcoin: cada entrada y salida con su monto y dirección, la comisión y cuál salida es el cambio. Una salida cuenta como cambio solo si el firmador vuelve a derivar su script a partir de sus propias claves públicas. Las etiquetas del PSBT por sí solas nunca convierten una salida en "cambio".
- EVM: red, nonce, comisiones, destinatario, valor nativo y llamadas decodificadas (transferencias y aprobaciones ERC-20/721/1155, Permit, Permit2, transacciones de Safe y más). Las direcciones de spender y operator se comparan con una lista integrada de contratos públicos conocidos; un spender desconocido eleva el nivel de riesgo.
- TRON: tipo de contrato, montos, llamadas TRC-20/721, límite de comisión, ID de permiso.
- Solana: cada instrucción que conoce (System, SPL Token, Token-2022, ATA, Compute Budget, Memo, Stake y programas de NFT), el pagador de la comisión y los firmantes.
Cada solicitud recibe dos etiquetas independientes:
| Decodificación | Riesgo |
|---|---|
| Totalmente decodificada · Parcialmente decodificada · Opaca | Normal · Advertencia · Alto |
Una solicitud válida pero no totalmente decodificada (por ejemplo, una llamada a un contrato desconocido) aún se puede firmar, pero solo después de que el usuario acepte explícitamente el riesgo para esa solicitud. No existe una configuración de "firma a ciegas" (blind signing) que quede activada. Los bytes malformados, los tipos de transacción no compatibles y los datos de red contradictorios se rechazan de plano y no se pueden anular.
Salvo que se indique lo contrario, están en el motor que comparten ambas apps. Consulta Revisiones de seguridad.
- Confirmación del riesgo (BTS-OP5-01 en iPhone, BTS-12 en Android, Baja). Las solicitudes de riesgo alto totalmente decodificadas, como una aprobación ilimitada o
setApprovalForAll, también necesitan la confirmación explícita, pero esa regla la impone la interfaz de la app (el botón Firmar sigue desactivado); el motor solo la impone en solicitudes parcialmente decodificadas u opacas. No se puede evitar desde un código QR. - Resúmenes de Solana (BTS-07, Media). Un cambio de autoridad, una delegación o una instrucción opaca enviados junto con una transferencia diminuta se resumen como esa transferencia. En iPhone 1.0.0 una quema puede ocultarse de la misma forma. Las advertencias y la confirmación se mantienen.
- Cambio de Bitcoin (BTS-08, Media). Una salida cuenta como cambio en cualquier índice de los perfiles de firma única de la propia cartera, incluidos índices que una cartera de solo lectura nunca revisará.
- Resúmenes de EVM y TRON (BTS-09, Baja–Media). El resumen guardado omite el valor nativo enviado con una llamada a un contrato desconocido; la tarjeta de revisión sí lo muestra.
Cómo se elige la billetera correcta
El usuario nunca elige qué billetera firma. El motor compara la solicitud con los datos públicos de cada billetera local y acepta exactamente una coincidencia:
| Cadena | Evidencia utilizada |
|---|---|
| Bitcoin | Huella digital y ruta de bip32Derivation, comprobadas con el script real de la entrada. Sin datos de derivación, solo se prueba /0/0 de cada cuenta de firma única. |
| EVM | Ruta de derivación (+ huella digital maestra) y/o dirección. Sin ruta, solo se compara el índice 0. |
| TRON | Ruta de derivación (cualquier índice), o una dirección comparada con los índices 0–99. |
| Solana | Una de las tres claves preset, que además debe ocupar un espacio de firmante requerido en el mensaje. |
La ausencia de coincidencia, un firmante faltante, una coincidencia ambigua (encajan dos billeteras) y los datos de billetera incompletos son errores distintos. El firmador nunca prueba la semilla de cada billetera.
El resultado es un plan inmutable que vincula el hash canónico de la solicitud, los bytes originales, los bytes exactos que se firmarán y las claves y entradas de destino. Después de que se ingresa la contraseña, el motor vuelve a derivar las claves, reconstruye los datos públicos, vuelve a ejecutar la asociación y compara el plan completo antes de tocar una clave privada.
Incluye siempre los datos de derivación: bip32Derivation en los PSBT, y la ruta y la huella digital en eth-, tron- y sol-sign-request. Las solicitudes con datos completos del firmante coinciden de inmediato; las que no los tienen recurren al índice 0 y pueden fallar para cualquier otra cuenta.
Vincular la firma a la solicitud
- El motor calcula el hash de toda la solicitud canónica, firma, verifica su propia firma y devuelve el resultado junto con el hash de la solicitud original. Si el usuario sale de la app o una nueva solicitud reemplaza a la anterior durante la firma, el resultado tardío se descarta.
- Las respuestas de EVM, TRON y Solana repiten el ID de solicitud, y la firma cubre los bytes exactos que se revisaron. Las respuestas de Bitcoin devuelven el mismo PSBT con las firmas agregadas; todos los demás campos deben permanecer sin cambios.
- Un PSBT puede necesitar varias billeteras locales (un multisig con dos claves en el mismo teléfono). Cada billetera es una tarea independiente con su propia contraseña, y las firmas parciales se conservan entre tareas.
Ambas revisiones de seguridad de la versión 1.0.0 comprobaron esta cadena de verificaciones. La revisión de Opus 5 confirmó mediante ejecución que un plan manipulado se rechaza y que, en las compilaciones Release, el único punto de entrada para firmar pasa por todas estas verificaciones.
Lo que queda fuera del alcance sin conexión
La revisión lo indica explícitamente cuando no puede saber algo: el comportamiento del contrato de un token, los saldos y las autorizaciones (allowances) actuales, si un miembro de un multisig de TRON sigue autorizado, si un blockhash de Solana sigue siendo válido o con qué se compromete realmente una raíz Merkle de Blur. Tu billetera, que está en línea, está en mejor posición para mostrar ese contexto antes de crear el código QR.