メインコンテンツまでスキップ

UR と CBOR の入門

BitTool Signer が読み書きする QR コードは、プレーンなアドレスと PSBT テキストを除き、すべて Uniform Resource(BCR-2020-005)です。UR を自前で実装する必要はありませんが(ライブラリを使ってください)、各層を理解しておくとデバッグがずっと楽になります。

CBOR から 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

たとえば、短い EVM 署名のレスポンスは次のとおりです。

ur:eth-signature(1 パート)
UR:ETH-SIGNATURE/OEADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTADAOHDFPJNBYWYMUYLLTIMKSWDCFWKYTSPVWGYVEGMDNJNWMLSGAOLHGDPATJEGLIOTOQZFPJNQDYNMHQDESRSIHLEHHLGDRTDYLHHUREYGMWTLBMWBKLFCTIHRKJZRHCKMSLFOYADCWJNLUAY

テキストは大文字小文字を区別しません。QR コードにする前に大文字に変換してください。そうすると文字が QR の 英数字 モードに収まり、バイトモードよりはるかに高密度になります。

マルチパート(アニメーション)UR​

ペイロードが 1 つの QR コードに収まらない場合、エンコーダーはペイロードを等しい長さのフラグメントに分割し、複数のパートを生成します。

ur:<type>/<seq>-<count>/<bytewords of CBOR [seq, count, message-length, checksum, fragment]>
  • パート 1…count は、フラグメントを順番どおりに運びます(「純粋な」パート)。
  • count より後のパートは ファウンテン パートです。seq とチェックサムから導出したシードに基づいて擬似ランダムに選んだフラグメントの XOR になっています。デコーダーはこれらから欠けたフラグメントを復元できます。
  • count、message-length、checksum(メッセージ全体の CRC-32)は、1 つのセッション内のすべてのパートで同じです。BitTool Signer は、2 つのセッションが混在したスキャンを拒否します。

実務上の注意点:

  • パートは順不同でスキャンし、重複は無視します。
  • 表示側:最初の count 個のパートを繰り返し再生するのではなく、ループ内で nextPart() を使ってパートを生成し続けます。
  • フラグメントサイズは、QR の密度とフレーム数のトレードオフです。BitTool Signer は、ウォレットレベルのペアリングに 400 バイト、署名レスポンスに 180 バイトを使っています。

ウォレットレベルのペアリング QR コードのフレーム 1:

ur:bytes、4 パート中の 1
UR:BYTES/1-4/LPADAACFAMCECYJYHYKIWKHKADLTHKAMCFOLIEJNJLIEIHIEIYKPJZJZIOJTIHJYKTJLJPJEIOJNHSINJTJTIHJYIOKOIHJPJKINJLJTADISHSIAIAJLKPJTJYJKLTONIEKSJOKPIDKSJLKSJOKPIDENFXHSJYHGIEINHTINJLIEJNGOIHGHFYJOETGSGHECJLJPETJTJNIDGRGLIAKPKKKOKNEMHGKKJEJKHFFGJEGRFWEEGMFDKTFXFYEOHDKKKPKOGDFEIDKOJSFPGYHKEOJPFPGDJKISHGIAGTGSJLGDEYIYGTFGGTGRFDGDGEEEHTIHHTHDHKHFGOISGSKOEHHFGTJPIMGDFXEMGDHGENHFIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIYJOEYKTJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLETEEDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFWIOFWIOJKIHJKJOHGKOFEGMFGEOGSFDGYKPENFXJTJSIEKOIYFEKOJYGTIAGYIMHKJPIAGMKNKSECEOGYGEIMGUKSHSJPIMEYHSIYHKHGIAGSJYIHJLFLHFJEKKEMFYEOGOGRFYGDESGYKKJPGSJOJPGYEOHFFXFEFXJLHKEEESKKIYIEFYFEFDFLFXJYGTGTIMESEYJOGMIHGOJKGYIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHUYSWLYTA

これらのペイロードで使われる CBOR​

使われるのは CBOR のごく一部だけです。

メジャータイプ用途
0 unsigned intマップのキー、データタイプ、チェーン ID、フィンガープリント、パスのインデックス
2 byte string署名データ、鍵、チェーンコード、署名、PSBT
3 text stringOrigin、名前、ペアリングマップのキーと値
4 arrayパスの要素、ディスクリプタのリスト、ペアリングのアカウント
5 mapすべてのリクエスト、レスポンス、鍵の構造
6 tag型付きアイテム(後述)
7 simpletrue / false(is-private、強化フラグ)

リクエストは 確定長 を使い、マップのキーに重複がなく、末尾に余分なバイトがあってはなりません。浮動小数点数、負の整数、不定長アイテムは、これらの構造のどこにも登場しません。

タグ​

タグ名前包む対象
37uuidリクエスト ID のバイト列
303crypto-hdkeyHD キーのマップ
304crypto-keypathパスのマップ {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(…)

バイト文字列とテキストの区別は重要です。リクエスト ID は、UUID のテキストではなく、タグ 37 内のバイト列です。キー 6 の EVM アドレスは、"0x…" ではなく 20 バイトの生データです。

リクエストを手作業で読む​

EIP-1559 の例 の CBOR に注釈を付けたものです。

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
同じバイト列の全体
a701d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0102583102ef010c843b9aca008506fc23ac0082520894000000000000000000000000000000000000dead872386f26fc1000080c00304040105d90130a2018a182cf5183cf500f500f400f4021a73c5da0a06549858effd232b4033e47d90003d41ec34ecaeda94076e426974546f6f6c2057616c6c6574

ライブラリ​

言語URCBOR
JavaScript / TypeScript@ngraveio/bc-ur。型付きクラスには Keystone の @keystonehq/bc-ur-registry*cborg
SwiftURKitURKit の DCBOR、SwiftCBOR
Kotlin / JavaHummingbirdHummingbird、com.upokecenter:cbor
Rusturminicbor、ciborium