Firma mediante QR
Después de la vinculación, cada firma es un único viaje de ida y vuelta: tu cartera muestra un código QR de solicitud y BitTool Signer muestra un código QR de respuesta.
| Cadena | Solicitud (tú → firmante) | Respuesta (firmante → tú) | Página |
|---|---|---|---|
| Bitcoin | ur:crypto-psbt (también ur:psbt, BBQr, texto Base64/hex) | El mismo tipo, con las firmas añadidas | Bitcoin |
| EVM | ur:eth-sign-request | ur:eth-signature | EVM |
| TRON | ur:tron-sign-request | ur:tron-signature | TRON |
| Solana | ur:sol-sign-request | ur:sol-signature | Solana |
Estos son los tipos del registro público de UR de Keystone. El formato de vinculación a nivel de cartera sirve solo para importar cuentas; no existe ningún envoltorio de firma propio de BitTool, y no hay que inventar ninguno.
Componentes básicos de una solicitud
Las solicitudes de EVM, TRON y Solana son mapas CBOR con claves enteras, transportados directamente como cuerpo del UR (una sola capa CBOR, a diferencia de la vinculación a nivel de cartera). Comparten tres componentes básicos.
ID de solicitud: 37(bytes)
Un identificador aleatorio: el tag CBOR 37 alrededor de una cadena de bytes. BitTool Signer lo copia sin cambios en la respuesta, para que puedas asociar la respuesta con tu solicitud pendiente.
- Genera 16 bytes aleatorios para cada solicitud (los bytes de un UUID, no su texto).
sol-sign-requestytron-sign-requestexigen exactamente 16 bytes.eth-sign-requesttambién acepta de 1 a 64 bytes, por compatibilidad con carteras que usan IDs opacos más largos.- Compara los IDs como bytes. Que el ID coincida no hace válida una firma; aun así tienes que verificarla.
Ruta de derivación: 304(crypto-keypath)
La ruta completa de la clave que debe firmar, más la huella maestra:
304({
1: [44, true, 60, true, 0, true, 0, false, 0, false], ; m/44'/60'/0'/0/0: index, hardened, index, hardened …
2: 1942346250 ; master fingerprint 0x73c5da0a as a big-endian uint32
})
- Los componentes alternan
index, hardened?. No sumes además0x80000000a los índices hardened. - Usa la ruta concreta hasta la hoja (
…/0/5), nunca una ruta de cuenta ni un comodín*. - Incluye siempre la huella (clave 2). Sin ella, la identificación de la cartera es más débil y algunas solicitudes no se pueden asociar a ninguna.
Una función auxiliar que la construye con cborg:
import { Tagged } from 'cborg';
/** crypto-keypath (tag 304): "m/44'/195'/0'/0/0" + "73c5da0a" →
* 304({1: [44, true, 195, true, 0, true, 0, false, 0, false], 2: 1942346250}) */
export function keypath(path, masterFingerprint) {
const components = path.replace(/^m\//, '').split('/')
.flatMap((part) => [Number.parseInt(part, 10), part.endsWith("'")]);
return new Tagged(304, new Map([[1, components], [2, Number.parseInt(masterFingerprint, 16)]]));
}
Origen: texto
Una etiqueta breve que se muestra al usuario como origen de la solicitud, por ejemplo "BitTool Wallet". Como máximo 256 caracteres. Se muestra, pero no se autentica: BitTool Signer nunca la trata como prueba de nada.
Sin campos adicionales
Solo se aceptan las claves definidas para cada tipo: 1–7 para eth-sign-request y 1–6 para sol- y tron-sign-request. Una clave desconocida invalida toda la solicitud, así que no añadas tus propios metadatos.
Códigos QR animados
Las solicitudes suelen ser más grandes de lo que cabe en un código QR. Codifícalas como UR multiparte y reproduce los fotogramas en bucle:
- Pasa el texto del UR a mayúsculas:
UR:ETH-SIGN-REQUEST/…cabe en el modo alfanumérico de QR, que necesita alrededor de un 30 % menos de bits que el modo byte. - Entre 200 y 400 bytes por fragmento y de 3 a 5 fotogramas por segundo se escanean de forma fiable con los móviles.
- Sigue llamando a
nextPart()del codificador en lugar de repetir la primera pasada. Después de la primera pasada, el codificador bc-ur produce partes «fountain» mezcladas, que permiten al escáner terminar aunque se hayan perdido fotogramas.
BitTool Signer codifica sus propias respuestas con fragmentos de 180 bytes, así que las firmas cortas son un único código QR estático y los PSBT firmados pueden ser animados. Tu escáner debe admitir ambos casos.
La respuesta
- Comprueba primero el tipo de UR: la respuesta a un
eth-sign-requestdebe ser uneth-signature, y así sucesivamente. Bitcoin responde con el mismo tipo que enviaste. - Comprueba el ID de solicitud (Bitcoin: comprueba que la transacción sin firmar dentro del PSBT no ha cambiado).
- Verifica criptográficamente la firma con la clave esperada y sobre los bytes exactos que pediste firmar; después, ensambla y difunde.
No existe respuesta de rechazo. Si el usuario cancela en BitTool Signer, no se muestra nada. Tu interfaz debe permitir al usuario volver atrás, mostrar de nuevo el QR o empezar desde el principio, y nunca debe esperar una respuesta indefinidamente.
Mantén un registro pendiente
Guarda cada solicitud hasta que se responda o se abandone:
pendingId, walletId, chain/network, signer path + key/address,
requestId, request type, exact bytes to sign, digest,
unsigned transaction, created at, expiry (nonce / blockhash / TRON expiration),
status: awaitingSignature → signatureVerified → broadcast → confirmed | failed | cancelled
Si algo de la transacción cambia (comisión, nonce, blockhash, importe), crea una solicitud nueva con un ID nuevo. No reutilices nunca una firma para bytes distintos y rechaza cualquier respuesta que llegue para una solicitud cancelada o caducada.
Cómo encuentra BitTool Signer la clave
No le dices a BitTool Signer qué cartera usar; lo deduce a partir de la ruta, la huella y la dirección o clave pública de tu solicitud, comparándolas con las claves públicas de todas las carteras del móvil. Debe coincidir exactamente una cartera. Consulta Qué comprueba el firmante para ver todas las reglas. En la práctica: envía la ruta completa, la huella y la dirección (o la clave pública) en cada solicitud, y BitTool Signer encontrará la clave directamente.