Saltar al contenido principal

Introducción a UR y CBOR

Todos los códigos QR que BitTool Signer lee o escribe, excepto las direcciones simples y el texto PSBT, son un Uniform Resource (BCR-2020-005). No necesitas implementar UR por tu cuenta (usa una biblioteca), pero conocer las capas facilita mucho la depuración.

De CBOR a texto QR​

CBOR bytes
→ append CRC-32 (4 bytes)
→ Bytewords "minimal": each byte becomes 2 letters (first + last letter of a 4-letter word)
→ "ur:" + type + "/" + bytewords single part

Por ejemplo, una respuesta corta de firma EVM:

ur:eth-signature (una parte)
UR:ETH-SIGNATURE/OEADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTADAOHDFPJNBYWYMUYLLTIMKSWDCFWKYTSPVWGYVEGMDNJNWMLSGAOLHGDPATJEGLIOTOQZFPJNQDYNMHQDESRSIHLEHHLGDRTDYLHHUREYGMWTLBMWBKLFCTIHRKJZRHCKMSLFOYADCWJNLUAY

El texto no distingue entre mayúsculas y minúsculas. Pásalo a mayúsculas antes de generar el código QR: así los caracteres caben en el modo alfanumérico de QR, que es mucho más denso que el modo byte.

UR de varias partes (animados)​

Cuando una carga útil es demasiado grande para un código QR, el codificador la divide en fragmentos de igual longitud y produce partes:

ur:<type>/<seq>-<count>/<bytewords of CBOR [seq, count, message-length, checksum, fragment]>
  • Las partes 1…count llevan los fragmentos en orden (partes "puras").
  • Las partes posteriores a count son partes fountain: el XOR de una selección pseudoaleatoria de fragmentos, elegida a partir de una semilla derivada de seq y del checksum. Con ellas, un decodificador puede reconstruir los fragmentos faltantes.
  • count, message-length y checksum (CRC-32 de todo el mensaje) son iguales en todas las partes de una misma sesión. BitTool Signer rechaza un escaneo que mezcle dos sesiones.

Consecuencias prácticas:

  • Escanea las partes en cualquier orden; ignora las repetidas.
  • Visualización: sigue generando partes con nextPart() en un bucle en lugar de repetir las primeras count partes.
  • El tamaño del fragmento es un equilibrio entre la densidad del QR y la cantidad de fotogramas. BitTool Signer usa 400 bytes para el emparejamiento a nivel de billetera y 180 bytes para sus respuestas de firma.

Fotograma 1 del código QR de emparejamiento a nivel de billetera:

ur:bytes, parte 1 de 4
UR:BYTES/1-4/LPADAACFAMCECYJYHYKIWKHKADLTHKAMCFOLIEJNJLIEIHIEIYKPJZJZIOJTIHJYKTJLJPJEIOJNHSINJTJTIHJYIOKOIHJPJKINJLJTADISHSIAIAJLKPJTJYJKLTONIEKSJOKPIDKSJLKSJOKPIDENFXHSJYHGIEINHTINJLIEJNGOIHGHFYJOETGSGHECJLJPETJTJNIDGRGLIAKPKKKOKNEMHGKKJEJKHFFGJEGRFWEEGMFDKTFXFYEOHDKKKPKOGDFEIDKOJSFPGYHKEOJPFPGDJKISHGIAGTGSJLGDEYIYGTFGGTGRFDGDGEEEHTIHHTHDHKHFGOISGSKOEHHFGTJPIMGDFXEMGDHGENHFIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIYJOEYKTJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLETEEDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFWIOFWIOJKIHJKJOHGKOFEGMFGEOGSFDGYKPENFXJTJSIEKOIYFEKOJYGTIAGYIMHKJPIAGMKNKSECEOGYGEIMGUKSHSJPIMEYHSIYHKHGIAGSJYIHJLFLHFJEKKEMFYEOGOGRFYGDESGYKKJPGSJOJPGYEOHFFXFEFXJLHKEEESKKIYIEFYFEFDFLFXJYGTGTIMESEYJOGMIHGOJKGYIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHUYSWLYTA

CBOR en estas cargas útiles​

Solo aparece un pequeño subconjunto de CBOR:

Tipo mayorSe usa para
0 entero sin signoClaves de mapa, tipos de datos, chain IDs, huellas digitales, índices de ruta
2 cadena de bytesDatos de firma, claves, chain codes, firmas, PSBT
3 cadena de textoOrigen, nombres, las claves y los valores del mapa de emparejamiento
4 arregloComponentes de ruta, listas de descriptores, cuentas de emparejamiento
5 mapaTodas las solicitudes, respuestas y estructuras de claves
6 tagElementos tipados (ver abajo)
7 simpletrue / false (is-private, indicadores de endurecimiento)

Las solicitudes deben usar longitudes definidas, sin claves de mapa duplicadas y sin bytes sobrantes. Los flotantes, los enteros negativos y los elementos de longitud indefinida no tienen cabida en ninguna de estas estructuras.

Tags​

TagNombreEnvuelve
37uuidBytes del ID de solicitud
303crypto-hdkeyMapa de clave HD
304crypto-keypathMapa de ruta {1: components, 2: source-fingerprint, 3: depth}
305crypto-coin-info{1: coin type, 2: network}
400script-hashsh(…)
401witness-script-hashwsh(…)
403public-key-hashpkh(…)
404witness-public-key-hashwpkh(…)
409taproottr(…)

La diferencia entre cadena de bytes y texto importa: un ID de solicitud son bytes dentro del tag 37, no el UUID como texto; una dirección EVM en la clave 6 son 20 bytes sin procesar, no "0x…".

Leer una solicitud a mano​

El CBOR del ejemplo EIP-1559, anotado:

a7 map, 7 entries
01 d8 25 50 5f1e9b2c…5a01 1: tag 37 (uuid), 16 bytes
02 58 31 02ef010c…80c0 2: 49 bytes: 0x02 ‖ rlp(unsigned EIP-1559 tx)
03 04 3: data-type 4
04 01 4: chain-id 1
05 d9 0130 a2 5: tag 304 (crypto-keypath), map 2
01 8a 18 2c f5 18 3c f5 00 f5 00 f4 00 f4 components [44,true,60,true,0,true,0,false,0,false]
02 1a 73c5da0a source-fingerprint 0x73c5da0a
06 54 9858effd…eda94 6: 20-byte address
07 6e "BitTool Wallet" 7: origin
Los mismos bytes completos
a701d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0102583102ef010c843b9aca008506fc23ac0082520894000000000000000000000000000000000000dead872386f26fc1000080c00304040105d90130a2018a182cf5183cf500f500f400f4021a73c5da0a06549858effd232b4033e47d90003d41ec34ecaeda94076e426974546f6f6c2057616c6c6574

Bibliotecas​

LenguajeURCBOR
JavaScript / TypeScript@ngraveio/bc-ur; @keystonehq/bc-ur-registry* de Keystone para clases tipadascborg
SwiftURKitDCBOR de URKit, SwiftCBOR
Kotlin / JavaHummingbirdHummingbird, com.upokecenter:cbor
Rusturminicbor, ciborium