QR a nivel de cartera (bittool-signer-wallet v1)
El código QR a nivel de cartera exporta todas las cuentas de una cartera 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 predefinidas (preajustes) de Solana. En la app se encuentra 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 vinculación está dentro de ella. Decodifica la capa exterior para obtener los bytes y, después, 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 más abajo son los mismos datos.
- Codificación canónica. Longitudes definidas, formas enteras más cortas y claves del mapa ordenadas por los bytes de su codificación (para claves de texto: primero las más cortas y, después, byte a byte). Solo aparecen textos, enteros sin signo, arrays 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 4096 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 vinculación
Envoltorio
| Campo | Tipo | Valor |
|---|---|---|
protocol | text | "bittool-signer-wallet" |
version | uint | 1. Rechaza cualquier otra versión; nunca leas una versión superior como v1. |
mode | text | "full". BitTool Signer solo produce full. |
network | text | "mainnet" |
masterFingerprint | text | Huella de la clave maestra BIP32, 8 dígitos hex en minúsculas |
accounts | array | 7 entradas, descritas a continuación. 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 sola 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' |
Todos los xpub usan 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. No se incluyen las claves de cofirmante multifirma (BIP48); una cartera multifirma 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 interior (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 el 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;
}
Después, deriva las direcciones. Bitcoin usa los cuatro tipos de script; EVM y TRON aplican Keccak-256 a la clave pública sin comprimir y se quedan con los últimos 20 bytes (TRON añade 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;
}
examples/test.mjs, en el repositorio de la documentación, ejecuta ambas funciones contra el vector de prueba anterior.
Reglas para el receptor
Un receptor robusto debería:
- Exigir
protocol == "bittool-signer-wallet", un enteroversion == 1,mode == "full",network == "mainnet"y unmasterFingerprintválido de 8 dígitos hex. Explica con claridad qué comprobación ha fallado. - Buscar las cuentas por clave, no por posición, y rechazar una carga útil en la que falte una cuenta conocida o esté duplicada.
- Ignorar los campos desconocidos: las cargas v1 posteriores pueden añadir algunos. Comprueba los bytes tal como los has recibido; no elimines los campos desconocidos ni vuelvas a codificar antes de una comprobación de codificación canónica, o una carga útil más reciente y válida no la superará.
- Validar cada xpub: Base58Check, versión
0x0488B21E, profundidad 3, un índice hijo hardened y una clave secp256k1 comprimida válida. Rechazaxprv,tpubyypub/zpub. - Comprobar las rutas con la tabla anterior, en lugar de reescribir sin avisar una ruta desconocida a un valor por defecto.
- Validar las claves de Solana: que Base58 decodifique exactamente a 32 bytes, que sean un punto Ed25519 válido y que no haya duplicados.
- Tratarla como entrada no fiable: limitar el número de fotogramas y los tamaños, y rechazar claves CBOR duplicadas y bytes sobrantes al final. El decodificador de referencia del propio BitTool Signer también rechaza el 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 procedan de la huella o de la ruta que indica, 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 firmante; 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 servicios de analítica ni la incluyas en informes de fallos.
- El código QR no está firmado y tampoco autentica la cartera receptora. La comprobación es la comparación de direcciones que hace el usuario.