QR a nivel de billetera (bittool-signer-wallet v1)
El código QR a nivel de billetera exporta todas las cuentas de una billetera de BitTool Signer en un solo escaneo: cuatro cuentas de Bitcoin (una por cada tipo de dirección de firma única), las cuentas de EVM y TRON, y las tres direcciones preset de Solana. En la app está en Connect › Wallet Level.
Codificación
pairing object (text keys, see below)
→ canonical CBOR (RFC 8949 §4.2.1) "inner", 1561 bytes for the test wallet
→ CBOR byte string containing those bytes "outer"
→ UR type "bytes" (BCR-2020-006)
→ multi-part UR, 400-byte fragments, upper-case 4 frames for the test wallet
Hay cuatro detalles importantes:
- Dos capas CBOR. El cuerpo del UR es una cadena de bytes CBOR; el mapa de emparejamiento está dentro de ella. Decodifica la capa externa para obtener los bytes y luego decodifica esos bytes. No es JSON, ni un elemento con tag 24, ni un tipo personalizado
ur:bittool-…. - Claves de texto. Las claves del mapa son los nombres de campo JSON como cadenas de texto CBOR, así que el CBOR y la vista JSON de abajo son los mismos datos.
- Codificación canónica. Longitudes definidas, la forma más corta de cada entero y claves de mapa ordenadas por los bytes de su codificación (para claves de texto: primero las más cortas, luego byte a byte). Solo aparecen texto, enteros sin signo, arreglos y mapas; los campos ausentes se omiten.
- Siempre animado. El tamaño de fragmento es de 400 bytes, limitado a la mitad de la carga útil, así que siempre hay al menos dos fotogramas. BitTool Signer rechaza cargas útiles de más de 4,096 bytes. Tu escáner debe reunir todos los fotogramas (en cualquier orden) antes de decodificar.
Ejemplo de orden canónico de claves: el objeto {version: 1, mode: "full", b: "x", aa: 2} se codifica como
a4 61 62 61 78 62 61 61 02 64 6d6f6465 64 66756c6c 67 76657273696f6e 01
"b" "x" "aa" 2 "mode" "full" "version" 1
El objeto de emparejamiento
Sobre (envelope)
| Campo | Tipo | Valor |
|---|---|---|
protocol | texto | "bittool-signer-wallet" |
version | uint | 1. Rechaza cualquier otra versión; nunca leas una versión superior como si fuera v1. |
mode | texto | "full". BitTool Signer solo produce full. |
network | texto | "mainnet" |
masterFingerprint | texto | Huella digital de la clave maestra BIP32, 8 dígitos hex en minúsculas |
accounts | arreglo | 7 entradas, descritas abajo. Búscalas por chain y script/profile, nunca por posición. |
Cuentas
chain | Campos | Notas |
|---|---|---|
btc (×4) | accountPath, script, xpub, derive: {receive: "0/*", change: "1/*"} | Una por script: p2wpkh (m/84'/0'/0'), p2tr (m/86'/0'/0'), p2sh-p2wpkh (m/49'/0'/0'), p2pkh (m/44'/0'/0') |
evm | accountPath m/44'/60'/0', xpub, derive: "0/*" | Una cuenta para todas las redes EVM |
tron | accountPath m/44'/195'/0', xpub, derive: "0/*" | |
sol | profile: "bip44-solana", accounts: [{index, profile, path, address}] | Tres entradas: standard m/44'/501'/0'/0', trust m/44'/501'/0', legacy m/44'/501' |
Cada xpub usa los bytes de versión estándar de mainnet 0x0488B21E, incluso para SegWit y Taproot: es el campo script, y no un prefijo ypub/zpub, el que indica qué direcciones derivar. Las claves de cofirmante multisig (BIP48) no se incluyen; una billetera multisig necesita a todos los cofirmantes y el umbral, así que para eso usa la exportación de cofirmante a nivel de moneda.
Vector de prueba
A partir de la frase de prueba pública abandon … about, sin passphrase. Apunta tu escáner a la animación o usa los fotogramas de abajo.
{
"protocol": "bittool-signer-wallet",
"version": 1,
"mode": "full",
"network": "mainnet",
"masterFingerprint": "73c5da0a",
"accounts": [
{
"chain": "btc",
"accountPath": "m/84'/0'/0'",
"script": "p2wpkh",
"xpub": "xpub6CatWdiZiodmUeTDp8LT5or8nmbKNcuyvz7WyksVFkKB4RHwCD3XyuvPEbvqAQY3rAPshWcMLoP2fMFMKHPJ4ZeZXYVUhLv1VMrjPC7PW6V",
"derive": {
"receive": "0/*",
"change": "1/*"
}
},
{
"chain": "btc",
"accountPath": "m/86'/0'/0'",
"script": "p2tr",
"xpub": "xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ",
"derive": {
"receive": "0/*",
"change": "1/*"
}
},
{
"chain": "btc",
"accountPath": "m/49'/0'/0'",
"script": "p2sh-p2wpkh",
"xpub": "xpub6C6nQwHaWbSrzs5tZ1q7m5R9cPK9eYpNMFesiXsYrgc1P8bvLLAet9JfHjYXKjToD8cBRswJXXbbFpXgwsswVPAZzKMa1jUp2kVkGVUaJa7",
"derive": {
"receive": "0/*",
"change": "1/*"
}
},
{
"chain": "btc",
"accountPath": "m/44'/0'/0'",
"script": "p2pkh",
"xpub": "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj",
"derive": {
"receive": "0/*",
"change": "1/*"
}
},
{
"chain": "evm",
"accountPath": "m/44'/60'/0'",
"xpub": "xpub6DCoCpSuQZB2jawqnGMEPS63ePKWkwWPH4TU45Q7LPXWuNd8TMtVxRrgjtEshuqpK3mdhaWHPFsBngh5GFZaM6si3yZdUsT8ddYM3PwnATt",
"derive": "0/*"
},
{
"chain": "tron",
"accountPath": "m/44'/195'/0'",
"xpub": "xpub6D1AabNHCupeiLM65ZR9UStMhJ1vCpyV4XbZdyhMZBiJXALQtmn9p42VTQckoHVn8WNqS7dqnJokZHAHcHGoaQgmv8D45oNUKx6DZMNZBCd",
"derive": "0/*"
},
{
"chain": "sol",
"profile": "bip44-solana",
"accounts": [
{
"index": 0,
"profile": "standard",
"path": "m/44'/501'/0'/0'",
"address": "HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk"
},
{
"index": 1,
"profile": "trust",
"path": "m/44'/501'/0'",
"address": "GjJyeC1r2RgkuoCWMyPYkCWSGSGLcz266EaAkLA27AhL"
},
{
"index": 2,
"profile": "legacy",
"path": "m/44'/501'",
"address": "D2PPQSYFe83nDzk96FqGumVU8JA7J8vj2Rhjc2oXzEi5"
}
]
}
]
}
Fotogramas QR (ur:bytes, 4 partes)
UR:BYTES/1-4/LPADAACFAMCECYJYHYKIWKHKADLTHKAMCFOLIEJNJLIEIHIEIYKPJZJZIOJTIHJYKTJLJPJEIOJNHSINJTJTIHJYIOKOIHJPJKINJLJTADISHSIAIAJLKPJTJYJKLTONIEKSJOKPIDKSJLKSJOKPIDENFXHSJYHGIEINHTINJLIEJNGOIHGHFYJOETGSGHECJLJPETJTJNIDGRGLIAKPKKKOKNEMHGKKJEJKHFFGJEGRFWEEGMFDKTFXFYEOHDKKKPKOGDFEIDKOJSFPGYHKEOJPFPGDJKISHGIAGTGSJLGDEYIYGTFGGTGRFDGDGEEEHTIHHTHDHKHFGOISGSKOEHHFGTJPIMGDFXEMGDHGENHFIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIYJOEYKTJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLETEEDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFWIOFWIOJKIHJKJOHGKOFEGMFGEOGSFDGYKPENFXJTJSIEKOIYFEKOJYGTIAGYIMHKJPIAGMKNKSECEOGYGEIMGUKSHSJPIMEYHSIYHKHGIAGSJYIHJLFLHFJEKKEMFYEOGOGRFYGDESGYKKJPGSJOJPGYEOHFFXFEFXJLHKEEESKKIYIEFYFEFDFLFXJYGTGTIMESEYJOGMIHGOJKGYIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHUYSWLYTA
UR:BYTES/2-4/LPAOAACFAMCECYJYHYKIWKHKADLTIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIEJOEYJYJPJEHSIAIAJLKPJTJYGDHSJYISJEJNDLETENDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFXENJTGYKTFDHSHGIDGUJPKNJKECJYHTEHJSEMJNECGMESIAGDGRESIHHKJOGLGTFGIHJKINHDJKHKJPIOIAEHGDETIDKOGSGSFPIHJYESGEIYFDIMHKHDGRIMGHJLFYETIAFWGMJKKTGEHDHDIDIDFGJOHDIOKTJKJKKTHFGDFPHTKNGRGTHSEHIMGOJOEYJEHFJEFLHFGOHSGEHSEMIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYJEJOEYJKISDPJOEYKTJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLEEESDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFWJLJKIYFXJTINIYKNKSIAFGKTJPGUKNGYINJSKPEYFYFWHFGHJKISJEFXHDHSIAKOGLJKHGFLHKGEHFHFISISHSKTFPEMIEEEGMECHGGUHGFLFGGLIDINETFPKTENHTGMIAEHIDJPKSGTKKHGGTKNFLEOFYGUGUGUGUJLIHJEJEKPIEISGOIEESKKGSIDENJSKSEOESGHESJTGTIEIMIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHBZSSDEKE
UR:BYTES/3-4/LPAXAACFAMCECYJYHYKIWKHKADLTOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIHJOEYJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLEEEEDIDLDYDIDLDYDIOXIEKSJOKPIDKSJLKSJOKPIDENFYFXJLFXJOGUKPGYHTFWEYIMHSKTJSJTFLGTFEGDGUENEOIHGDGRHGJEKTHGGDFDEEGHGOEEECGYEMGSGDHDHGKPGLIEETGHGTJYHFKSGMJPIOIMJYFEJKISKPJSJOGREOJNIEISHSHGFDGDFGJKFWJTIOISECFLFGHTHSGTENJKINEOKKHTIEGOJKGHETIEIEHKGTEOGDKTJTFPGHJYIHIAISHSINJTIAIHKOJNIYIEIHJPINKOIHIADYDLDRJEHSIAIAJLKPJTJYGDHSJYISJZJNDLEEEEDIDLENDYDIDLDYDIOXIEKSJOKPIDKSJLKSJOKPIDENFYEHFPHSIDGLFDFXKPJOIHINGSGTENECHTGMESGOGUJYGTISGEEHKOFXJOKKHFEEHDIDHTIEKKISGTHTFWINGEHDFPGSGYJYJNJTESJOEEEYHFGHGYIAJEJLFDHFJTETHGGLJSGUEMIEJSJTGEJLJEHTFDFPFDIAFDFLJLHSGYIOJNKOETFYEEECJLGLGOGRKSENFYHTGTGLHTFWFXIEIHIAISHSINJTIEJYJPJLJTIYIEIHJPINKOIHIADYDLDRJEHSIAIAJLKPJTJYGDHSJYISJNJNDLEEEEDIDLEHESECDIDLPRFLWYJT
UR:BYTES/4-4/LPAAAACFAMCECYJYHYKIWKHKADLTDYDIOTIHIAISHSINJTIAJKJLJZIOJOJPJLIYINJZIHJZIDINJOEEEEDPJKJLJZHSJTHSISHSIAIAJLKPJTJYJKLSOXIEJOHSJYISJOJNDLEEEEDIDLECDYEHDIDLDYDIDLDYDIIHINJTIEIHKSAEIOHSIEIEJPIHJKJKKSDWFDFPIOJEEHEEGEJOGTGYGSIOJYENJPHFIOKOEMIAFWGYFGGEHGFGJYJLECFYJSKSINEEEMEYKPGHEOFYGRJOJSJEIOJOJPJLIYINJZIHISJKJYHSJTIEHSJPIEOXIEJOHSJYISJNJNDLEEEEDIDLECDYEHDIDLDYDIIHINJTIEIHKSADIOHSIEIEJPIHJKJKKSDWFLIMGEKKIHFXEHJPEYGMIOJEKPJLFXHGGTKKGDHKJEFXHGGUFLGUFLGSIAKNEYENENFEHSFPJEGSFPEYEMFPISGSIOJOJPJLIYINJZIHIHJYJPKPJKJYOXIEJOHSJYISIMJNDLEEEEDIDLECDYEHDIIHINJTIEIHKSAOIOHSIEIEJPIHJKJKKSDWFYEYGDGDGYGUHKFGIHETEOJTFYKNJEESENFGJSFLKPJNHFGOETGEFPEMGEETKOIMEYGMISIMIAEYJLHDKNFEINECIOJOJPJLIYINJZIHIYJZIHIOHSIAKKISJOJPJLJYJLIAJLJZKPIDINJYJYJLJLJZDPJKINIOJTIHJPDPKTHSJZJZIHJYJSJNHSJKJYIHJPFGINJTIOIHJPJOJPINJTJYISEMEOIAECIEHSDYHSVOTIMSMD
CBOR canónico interno (hex) y su SHA-256
a6646d6f64656466756c6c676e6574776f726b676d61696e6e65746776657273696f6e01686163636f756e747387a56478707562786f78707562364361745764695a696f646d5565544470384c54356f72386e6d624b4e637579767a3757796b7356466b4b423452487743443358797576504562767141515933724150736857634d4c6f5032664d464d4b48504a345a655a58595655684c7631564d726a5043375057365665636861696e6362746366646572697665a2666368616e676563312f2a677265636569766563302f2a6673637269707466703277706b686b6163636f756e74506174686b6d2f3834272f30272f3027a56478707562786f787075623642674267736573705776455246334c48517536436e716476664576744d63516a597263527a783533514a6a537861726a3261665957634c74656f47566b79374433554b4450395179724c70725133564345436f5934397966644445484743744d4d6a393270526555735165636861696e6362746366646572697665a2666368616e676563312f2a677265636569766563302f2a6673637269707464703274726b6163636f756e74506174686b6d2f3836272f30272f3027a56478707562786f787075623643366e51774861576253727a7335745a3171376d35523963504b396559704e4d4665736958735972676331503862764c4c416574394a66486a59584b6a546f443863425273774a5858626246705867777373775650415a7a4b4d61316a5570326b566b475655614a613765636861696e6362746366646572697665a2666368616e676563312f2a677265636569766563302f2a667363726970746b703273682d703277706b686b6163636f756e74506174686b6d2f3439272f30272f3027a56478707562786f7870756236426f7366436e69667a7863467772537a51697175324442565473686b43586163764e735747594a56566868617741376434523557535747464e6269384177365a5263316272784d79574d7a473344535353536f656b6b756468556439794c62367178333954396e4d646a65636861696e6362746366646572697665a2666368616e676563312f2a677265636569766563302f2a66736372697074657032706b686b6163636f756e74506174686b6d2f3434272f30272f3027a46478707562786f787075623644436f43705375515a42326a6177716e474d455053363365504b576b77575048345455343551374c505857754e6438544d7456785272676a744573687571704b336d6468615748504673426e67683547465a614d36736933795a64557354386464594d3350776e41547465636861696e6365766d6664657269766563302f2a6b6163636f756e74506174686c6d2f3434272f3630272f3027a46478707562786f787075623644314161624e4843757065694c4d36355a52395553744d684a3176437079563458625a6479684d5a42694a58414c51746d6e39703432565451636b6f48566e38574e71533764716e4a6f6b5a4841486348476f6151676d76384434356f4e554b7836445a4d4e5a42436465636861696e6474726f6e6664657269766563302f2a6b6163636f756e74506174686d6d2f3434272f313935272f3027a365636861696e63736f6c6770726f66696c656c62697034342d736f6c616e61686163636f756e747383a46470617468706d2f3434272f353031272f30272f302765696e646578006761646472657373782c4841676b31344a704d514c6774367256677637634251464a5746746f3544717869343732755433444b70716b6770726f66696c65687374616e64617264a464706174686d6d2f3434272f353031272f302765696e646578016761646472657373782c476a4a79654331723252676b756f43574d7950596b4357534753474c637a3236364561416b4c41323741684c6770726f66696c65657472757374a464706174686a6d2f3434272f3530312765696e646578026761646472657373782c44325050515359466538336e447a6b3936467147756d5655384a41374a38766a3252686a63326f587a4569356770726f66696c65666c65676163796870726f746f636f6c75626974746f6f6c2d7369676e65722d77616c6c6574716d617374657246696e6765727072696e74683733633564613061
c8db82cd92e0cf45cb6329b2429494b04a3b6bad7653c9bed1d2a5ef10f4a5fc
La primera dirección de cada cuenta, que BitTool Signer muestra debajo del código QR:
| Cuenta | Ruta | Dirección |
|---|---|---|
| Bitcoin P2WPKH | m/84'/0'/0'/0/0 | bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu |
| Bitcoin P2TR | m/86'/0'/0'/0/0 | bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr |
| Bitcoin P2SH-P2WPKH | m/49'/0'/0'/0/0 | 37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf |
| Bitcoin P2PKH | m/44'/0'/0'/0/0 | 1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA |
| EVM | m/44'/60'/0'/0/0 | 0x9858EfFD232B4033E47d90003D41EC34EcaEda94 |
| TRON | m/44'/195'/0'/0/0 | TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH |
| Solana standard | m/44'/501'/0'/0' | HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk |
| Solana trust | m/44'/501'/0' | GjJyeC1r2RgkuoCWMyPYkCWSGSGLcz266EaAkLA27AhL |
| Solana legacy | m/44'/501' | D2PPQSYFe83nDzk96FqGumVU8JA7J8vj2Rhjc2oXzEi5 |
Cómo decodificarlo
Este decodificador usa @ngraveio/bc-ur para la capa UR y cborg en modo estricto para CBOR:
import { URDecoder } from '@ngraveio/bc-ur';
import { decode } from 'cborg';
export function decodeWalletPairing(frames) {
const decoder = new URDecoder();
for (const frame of frames) decoder.receivePart(frame); // any order; repeats are ignored
if (!decoder.isComplete()) throw new Error(`Keep scanning: ${Math.round(decoder.estimatedPercentComplete() * 100)}%`);
if (!decoder.isSuccess()) throw new Error(decoder.resultError());
const ur = decoder.resultUR();
if (ur.type !== 'bytes') throw new Error('Not a BitTool Signer wallet QR');
const strict = { strict: true, rejectDuplicateMapKeys: true };
const inner = decode(ur.cbor, strict); // layer 1: a CBOR byte string
if (!(inner instanceof Uint8Array)) throw new Error('ur:bytes must hold a byte string');
const pairing = decode(inner, strict); // layer 2: the text-keyed map
if (pairing.protocol !== 'bittool-signer-wallet') throw new Error('Unknown protocol');
if (pairing.version !== 1) throw new Error('Unsupported version');
if (pairing.mode !== 'full' && pairing.mode !== 'simple') throw new Error('Unsupported mode');
if (pairing.network !== 'mainnet') throw new Error('Unsupported network');
if (!/^[0-9a-f]{8}$/.test(pairing.masterFingerprint)) throw new Error('Bad fingerprint');
return pairing;
}
Luego deriva las direcciones. Bitcoin usa los cuatro tipos de script; EVM y TRON calculan el hash Keccak-256 de la clave pública sin comprimir y conservan los últimos 20 bytes (TRON agrega el prefijo 0x41 y Base58Check); las direcciones de Solana ya vienen en la carga útil:
import { HDKey } from '@scure/bip32';
import { keccak_256 } from '@noble/hashes/sha3.js';
import { sha256 } from '@noble/hashes/sha2.js';
import { secp256k1 } from '@noble/curves/secp256k1.js';
import { base58check } from '@scure/base';
import { p2wpkh, p2tr, p2sh, p2pkh } from '@scure/btc-signer';
import { getAddress } from 'ethers';
const child = (xpub, branch, index) => HDKey.fromExtendedKey(xpub).deriveChild(branch).deriveChild(index);
export function firstAddresses(pairing) {
const out = {};
for (const account of pairing.accounts) {
if (account.chain === 'btc') {
const pub = child(account.xpub, 0, 0).publicKey; // receive branch 0, index 0
out[`btc:${account.script}`] = {
p2wpkh: () => p2wpkh(pub).address,
p2tr: () => p2tr(pub.subarray(1)).address, // BIP86: untweaked x-only key
'p2sh-p2wpkh': () => p2sh(p2wpkh(pub)).address,
p2pkh: () => p2pkh(pub).address,
}[account.script]();
} else if (account.chain === 'evm' || account.chain === 'tron') {
const point = secp256k1.Point.fromBytes(child(account.xpub, 0, 0).publicKey);
const hash = keccak_256(point.toBytes(false).subarray(1)).subarray(-20);
out[account.chain] = account.chain === 'evm'
? getAddress('0x' + Buffer.from(hash).toString('hex'))
: base58check(sha256).encode(Uint8Array.from([0x41, ...hash]));
} else if (account.chain === 'sol') {
for (const sol of account.accounts) out[`sol:${sol.profile}`] = sol.address;
}
}
return out;
}
Ambas funciones se ejecutan contra el vector de prueba anterior mediante examples/test.mjs en el repositorio de la documentación.
Reglas para el receptor
Un receptor robusto debe:
- Exigir
protocol == "bittool-signer-wallet", unversion == 1entero,mode == "full",network == "mainnet"y unmasterFingerprintválido de 8 dígitos hex. Explicar claramente qué verificación falló. - Buscar las cuentas por clave, no por posición, y rechazar una carga útil a la que le falte una cuenta conocida o que tenga una duplicada.
- Ignorar los campos desconocidos: las cargas útiles v1 posteriores pueden agregar algunos. Verifica los bytes tal como los recibiste; no elimines los campos desconocidos para volver a codificar antes de una verificación de codificación canónica, o una carga útil más nueva y válida no la pasará.
- Validar cada xpub: Base58Check, versión
0x0488B21E, profundidad 3, un índice hijo endurecido (hardened), una clave secp256k1 comprimida válida. Rechazarxprv,tpubyypub/zpub. - Comprobar las rutas con la tabla anterior, en lugar de reescribir en silencio una ruta desconocida a un valor predeterminado.
- Validar las claves de Solana: el Base58 se decodifica en exactamente 32 bytes, un punto Ed25519 válido, sin duplicados.
- Tratarla como entrada no confiable: limitar la cantidad de fotogramas y los tamaños, y rechazar claves CBOR duplicadas y bytes sobrantes. El propio decodificador de referencia de BitTool Signer también rechaza CBOR no canónico.
Se puede comprobar la estructura de un xpub, pero no su origen. Nada en la carga útil demuestra que las claves provengan de la huella digital o la ruta que declara, y por eso el usuario debe comparar las direcciones.
Notas de seguridad
- La carga útil contiene solo claves públicas extendidas y direcciones. Se construye a partir del índice público autenticado del firmador; la bóveda nunca se abre.
- Cualquiera que la escanee puede ver todas las direcciones de estas cuentas y todo su historial. Trátala como cualquier xpub: no la registres en logs, no la subas a herramientas de analítica ni la incluyas en reportes de fallos.
- El código QR no está firmado y tampoco autentica a la billetera receptora. La verificación es la comparación de direcciones por parte del usuario.