Base32
テキストや 16 進数を Base32 か base32hex にエンコードし、ブラウザ内で元に戻します。パディングの有無も大文字・小文字も選べ、場違いな文字はその行と列を示します。
4OAZHY4CSPRYDK7DQGQ6HANP
32 種類の文字で書くバイトと、外された数字
Base32 はバイトをテキストとして書きます。使うのは 1 文字が 5 ビットを表す 32 種類の文字で、そのため 5 バイト — 40 ビット — ごとに 8 文字になります。Hello は 5 バイトで、JBSWY3DP と書かれます。「デコード」を選んで Base32 を貼り付けると、ページはその Base32 が表すバイトを返します。テキストとして、または「バイトの表記」で「16 進数」を選べば 16 進数として返します。
これを定める RFC 4648 は、26 個の大文字と、2 から 7 までの数字を使います。RFC は 0 と 1 がない理由を挙げています。テキストを扱う人は 0 を O と、1 を l や I と取り違えやすいので、英数字表は英字のほうを残し、この 2 つの数字を外しているのです。英字のほかに数字が 6 個要り、その 6 個が 2 から 7 なので、8 と 9 も入っていません。この英数字表でデコードするとき、ページは 0 を O と、1 を I と読むことはせず、0、1、8、9 があればその位置で拒否します。RFC は、デコーダーがそう読むことを認めつつ、既定ではそうすべきでないと述べています。
大文字か小文字かは、値の一部ではありません。RFC 4648 は Base32 を、大文字小文字を区別せずに読まれなければならないテキストのために設計したので、JBSWY3DP と同じく jbswy3dp も Hello です。RFC は英数字表を大文字で書き、このページも「小文字」をオンにしない限り大文字で書きます。また、読み取りは改行も含めてどこにある空白も読み飛ばすので、グループに分けた Base32 も複数行にわたる Base32 も、ひと続きに書かれたものとして読まれます。
パディングと、Base32 の取りうる長さ
バイトの数がちょうど 5 の倍数になることは、めったにありません。末尾に残った 1 から 4 バイトはそれぞれ 2、4、5、7 文字になり、RFC 4648 はその最後のグループを = で 8 文字まで埋めます。= の数は 6 個、4 個、3 個、1 個です。ですから f は MY======、fo は MZXQ====、foo は MZXW6===、foob は MZXW6YQ= で、5 バイトの fooba は 8 文字をちょうど満たして MZXW6YTB になります。
つまり、= があればその前までの文字を 8 個ずつ数えると、余りはいつも 0、2、4、5、7 のどれかで、1、3、6 になることはありません。どんな数のバイトも、そうした余りを残さないからです。ページはそのようなテキストを、より短い何かとして読むことはせず、その最後の文字で拒否します。パディングは、長さが伝えていないことを何も伝えません。ですからページは、パディングを省いた Base32 も読み — JBUQ==== と同じく JBUQ も Hi です — パディングを拒否するのは、それがあって、しかも誤っている場合だけです。つまり、後ろにまだテキストが続く途中の = か、末尾にある数の誤った = です。後者の場合は、拒否の文がそこに入るべき数を伝えます。
エンコードするときは、「パディング」スイッチが = を書くかどうかを決めます。最初はオンです。RFC 4648 が、それを使う側の仕様が別に定めない限りパディングを求めているからです。そして、別に定めている仕様もいくつかあります。2FA の QR コードに入る otpauth:// URI を記述する Key Uri Format は、シークレットのパディングは省くべきだとしており、NSEC3 レコードと IPFS はパディングをまったく書きません。その横の「小文字」スイッチは英字を小文字で書きます。数字と = には、変えられる大文字小文字がありません。どちらのスイッチもエンコード中にしか表示されません。読み取りはその組み合わせをすべて受け付けるからです。
ほかのツールは、このページほど多くを読みません。GNU coreutils は Base32 を base32 で、2 番目の英数字表である base32hex を basenc --base32hex で書き、Python の base64 モジュールにはそれぞれに対応する関数があります。どれも、このページが開いたときに書くのと同じもの、つまりパディング付きの大文字を書きます。ところが base32 -d は、パディングが欠けている Base32 や英字が小文字の Base32 を拒否し、Python の b32decode もその両方を拒否します。b32decode が小文字を読むのは、casefold=True を渡されたときだけです:
# 書く: 既定の英数字表、次にもう一方の英数字表
printf 'Hi' | base32 # JBUQ====
printf 'Hi' | basenc --base32hex # 91KG====
# 読み戻す: パディングあり、次にパディングなし、次に小文字
printf 'JBUQ====' | base32 -d # Hi
printf 'JBUQ' | base32 -d # base32: invalid input
printf 'jbuq====' | base32 -d # base32: invalid input
# 同じことをスクリプトで
import base64
base64.b32encode(b'Hi') # b'JBUQ===='
base64.b32hexencode(b'Hi') # b'91KG===='
base64.b32decode('JBUQ') # binascii.Error: Incorrect padding
base64.b32decode('jbuq====', casefold=True) # b'Hi'2 つの英数字表: base32 と base32hex
RFC 4648 は 2 番目の英数字表として base32hex を定めています。数字の 0 から 9、続いて英字の A から V で、文字は表す値の順に、ゼロを表す 0 から 31 を表す V まで並びます。そのため、1 番目の英数字表にない性質が 1 つあり、RFC もそれを指摘しています。1 文字ずつ比べて並べ替えると、テキストが、表すバイトの順に並ぶのです。バイト 00 は base32 では AA======、base32hex では 00====== で、バイト FF は 74====== と VS====== です。テキストとして並べ替えると、数字は英字より前に来るので base32 では FF が先になりますが、base32hex ではバイトの順序が保たれます。
DNSSEC の NSEC3 レコードはこれを使います。レコードの名前はドメイン名のハッシュで始まり、そのハッシュはパディングなしの base32hex で書かれます。RFC 5155 は、そう書くと、ハッシュ化した名前がハッシュの値と同じ順に並ぶと述べています。RFC 5155 自身の例のゾーンでは、example のハッシュは 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom です。base32hex を選んでいれば、ページはこの 32 文字をハッシュの 20 バイトとして読みます。base32 を選んでいれば 0 で拒否し — そのとき、テキストは base32hex なら最後まで読めるという知らせが、切り替えるボタンとともに出ます。
同じバイトでも英数字表ごとに別のテキストになるので — Hi は base32 では JBUQ====、base32hex では 91KG==== です — 選んだ英数字表はどちらの方向にも適用され、ページがあなたの代わりにそれを変えることはありません。デコードするテキストに、選んだ英数字表にない文字があるか、テキストが 0 でない余りビット — 下の質問で説明します — で終わっていて、しかももう一方の英数字表ならエンコーダーが書くとおりに全体を読めるとき、知らせがそう伝え、横に切り替えるボタンが出ます。英数字表が変わるのは、そのボタンを押すか、自分で選んだときだけです。ABCDEFGH のように、どちらの英数字表でも問題なく読めるテキストは、選んでいるほうで読まれます。どちらが意図されたのかを示すものが、テキストの中に何もないからです。
Base32 に出会う場所: 2FA シークレット、onion アドレス、IPFS
認証アプリのコードのもとになるシークレットは、Key Uri Format では Base32 で書かれます。Key Uri Format は設定時に読み取る QR コードに入れられる otpauth:// URI を記述したもので、その secret パラメーターは Base32 で、パディングは省くべきだとされています。そうしたシークレットは、大文字でも小文字でも、スペースで区切ったグループでも複数行に分けたものでも、= があってもなくても貼り付けられます — グループの間のハイフンは、その位置で拒否されます。また、URI 全体を貼り付ければ、ページはそこからシークレットを読み取り、そう伝えます。URI のほかのパラメーターが何を意味するか、そしてシークレットがどのようにアプリの表示するコードになるかは、「TOTP / 2FA コードデバッガー」の領分です。そのガイドが URI とそのパラメーターを説明し、そのページがコードを計算します。URI の中のラベルと発行者は、Key Uri Format が求めるとおりパーセントエンコードされており、「URL エンコード / デコード」がそれを読みます。
Tor のバージョン 3 の onion アドレスも Base32 です。.onion の前の 56 文字は 35 バイトの Base32 で、その 35 バイトはサービスの 32 バイトの公開鍵、2 バイトのチェックサム、そしてバージョンのバイト 03 です。Tor の仕様は例のアドレスを小文字で書いており、35 バイトはグループをちょうど満たすので、省くパディングはありません。.onion を除いた 56 文字を貼り付け、「バイトの表記」で「16 進数」を選ぶと、最後のバイトは 03 と読めます。
IPFS はコンテンツ識別子、つまり CID を、バージョン 1 以降は既定で Base32 で書きます。小文字でパディングなし、そして先頭に 1 文字の b が付きます。b は Base32 の一部ではなく、それが Base32 であることを示すもの — multibase の接頭辞で、残りをデコードする前に取り除かれます。ですから IPFS 自身のドキュメントにある例、bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi は、ここではそのままだと、どのバイト列からも生じない長さとして拒否されます。b を除くと、36 バイト、つまりバイナリの CID として読め、その先頭はバージョンを表す 01 です。
2FA シークレットはテキストではなくバイト
Key Uri Format の例のシークレットは JBSWY3DPEHPK3PXP で、この文書はその値を 10 バイトとして書き出しています。Hello! の文字、続いて DE AD BE EF です。最初の 8 文字 JBSWY3DP は、それだけで Hello です。ここで「バイトの表記」をページの既定の「テキスト」にしてデコードすると、Hello!、続いて U+07AD — DE AD はたまたま 1 つの文字、ターナ文字の記号になります — そして U+FFFD が 2 つ出てきます。U+FFFD はそれぞれ、テキストではないバイト列を表しています。結果の下の知らせは、置き換えたバイト列を数え、最初のものが 9 バイト目から始まり、そのビットが 1 行目、13 列目の文字、つまり 3 から始まると伝え、さらに、バイトがそもそもテキストではない可能性があると伝えます。
本物のシークレットはランダムなバイトで、ランダムなバイトがテキストになることはめったにありません。ですからテキストとしてデコードすると、ばらばらの文字と U+FFFD の寄せ集めになり、それはシークレットが正しいかどうかについて何も教えてくれません。そのためにあるのが、「バイトの表記」の「16 進数」です。それを選ぶか、知らせの横のボタンを押すと、同じシークレットが 48 65 6C 6C 6F 21 DE AD BE EF として丸ごと表示されます。バイトそのものであり、サーバーが保持し、すべてのコードの計算のもとになるものです。ページが自分から 16 進数に切り替えることはないので、画面に出ているのはいつも、あなたが選んだものです。
逆向きも同じ選択で、エンコードするときに行います。サーバーが 16 進数で保管している鍵はバイトです。「エンコード」と、「バイトの表記」の「16 進数」を選び、その鍵を貼り付けると — スペース区切りでも続けて書いたものでも、バイトの前に 0x が付いていても、数値の配列でも、hexdump -C や xxd の形の 16 進ダンプでも — ページは認証アプリが受け付ける Base32 を書きます。48656C6C6F21DEADBEEF は、上のシークレット JBSWY3DPEHPK3PXP になります。10 バイトはグループをちょうど満たしますが、満たさない鍵、たとえば 16 バイトの鍵は、Key Uri Format が求めるとおり「パディング」をオフにしない限り、後ろに = が付いて出てきます。デコード中に誤って貼り付けた 16 進数 — 空白文字を除けば偶数個の 16 進数の数字だけで、エンコードするときにバイトとして読める形のもの — には、16 進数に見えるという知らせが、それをエンコードする側に切り替えるボタンとともに出ます。そしてデコード中は、「ダウンロード」がバイトそのものを bytes.bin という名前のファイルとして保存します。
Base32 か Base64 か、そして Crockford の英数字表を使わない理由
Base64 は同じ仕事を 64 種類の文字で、3 バイトごとに 4 文字で行います。そのため Base64 のテキストはバイトより 3 分の 1 長くなります。対して Base32 のテキストは 5 分の 3 長くなります。その代わりに Base64 が手放すのは、大文字小文字を問わずに読めることです。Base64 の英数字表は大文字と小文字を別々の値として持ち、そのうえ + と / もあるので、SGk= は Hi で、sgk= は別の 2 バイトです。Base32 が向いているのは、大文字小文字を区別しない何かを通るテキストや、人がある画面から読み取って別の画面に打ち込むテキストです。ここに貼り付けた Base64 に、どの Base32 も書かない + か / があり、全体が Base64 の形をしているときは、Base64 に見えるという知らせと、Base64 をテキストとして読む「Base64」ツールへのリンクとともに拒否されます。
32 種類の文字でできた英数字表はほかにもありますが、このページが提供するのは RFC 4648 の 2 つで、ほかにはありません。その 1 つが Douglas Crockford のもので、10 個の数字をすべて残して I、L、O、U を外しており、ULID 形式が使っています。Crockford はこれを、数を書くために定めました。バイトを書くのに使うと、答えが 2 つあります。00 から 0F までの 16 バイトは、RFC 4648 がバイトを詰めるように 5 ビットずつ詰めると 000G40R40M30E209185GR38E1W になり、ULID の 26 文字を読むように 1 つの 128 ビットの数として読むと 00041061050R3GG28A1C60T3GF になります。これでバイトを書くページは、2 つのうちの 1 つを、見えないところであなたの代わりに選ぶことになります。
よくある質問
- デコードした私のシークレットに U+FFFD が表示されるのはなぜですか?
- 2FA シークレットはランダムなバイトで、ランダムなバイトが UTF-8 のテキストになることはめったにないからです。「バイトの表記」が「テキスト」のとき、ページはテキストではないバイト列をそれぞれ置換文字の U+FFFD として書き、知らせが、そうしたバイト列がいくつあったかと、最初のものがどこから始まるかを、何バイト目かとして、またそのビットが始まる文字の行と列として伝えます。バイトそのものは手つかずのままです。知らせの横のボタンはそれを 16 進数で表示し、「ダウンロード」はそれを保存します。テキストに本当に入っている U+FFFD、つまり EF BF BD というバイトは、テキストとして読まれ、数えられません。
- 「どのバイト列からも生じない長さ」とはどういう意味ですか?
- Base32 の文字を、= を除いて 8 個ずつ数えると、バイトが何であっても、余りは 0、2、4、5、7 のどれかです。ですから 1、3、6 が余るテキストはどんなバイトも表すことができず、ページはその最後の文字で拒否します。そのようなテキストは、エンコーダーがそう書いたものではありません。あなたのところに届くまでのどこかで、何かが失われたか、加えられたのです。Key Uri Format のシークレットから 2 文字足りない JBSWY3DPEHPK3P は、その最後の文字で拒否されます。切れた位置が、バイト列から生じる長さに当たることもあり、その場合はより少ないバイトとして読まれます — JBSWY3DPEHPK3PX なら 9 バイトとして。ページがそれに気づけるのは、最後の文字の余りビットが 0 でないときだけで、この X の余りビットがそうです。8 文字のグループのちょうど切れ目で切れた場合は、気づく手がかりが何も残りません。
- 余りビットとは何で、ページが私の余りビットは 0 ではないと言うのはなぜですか?
- 1 文字に 5 ビット、1 バイトに 8 ビットでは、割り切れることはめったにありません。そのため、バイトが 5 の倍数で来ない限り、最後の文字はどのバイトにも入らない 1 から 4 ビットを持ち、エンコーダーはそれを 0 で書きます。MZXW6YQ= と MZXW6YR= はどちらも foob です。Q の最後の 3 ビットは 000、R の最後の 3 ビットは 001 で、エンコーダーが書くのは前者だけです。ページはどちらも読み、後者については知らせが、その R と、それがある位置と、エンコーダーがそこに書く Q を示します。余りビットが 0 でないということは、そのテキストがエンコーダーの書いたものではないということです。途中で切れたのかもしれず、手で編集されたのかもしれず、もう一方の英数字表で書かれたのかもしれません。最後のものは、ページがあなたの代わりに確かめます。
- Base32 は暗号化ですか?
- いいえ。Base32 が変えるのはバイトの書き方だけで、それ以外は何も変えません。誰でもデコードでき、RFC 4648 に従うデコーダーなら、どれも同じ英数字表で同じバイトを取り戻します。Base32 で書かれた 2FA シークレットはシークレットそのものなので、セットアップキーを見た人は誰でも、あなたの第二要素を手にすることになります。
- 私が貼り付けたものはサーバーに送られますか?
- いいえ。どちらの方向も、このページの中で、あなた自身の端末上で動きます。貼り付けたもの — シークレットでも、テキストでも、16 進数でも — は何もアップロードされず、「ダウンロード」が保存するファイルは、ブラウザにすでにあるバイトから、そのブラウザの中で作られます。
関連するツール
- Base64
Base64 は 3 バイトごとに 4 文字、Base32 は 5 バイトごとに 8 文字で書き、Base64 では大文字か小文字かも文字の値の一部です。あちらでは abc は YWJj で、YWJJ は abI と読まれます。
- TOTP / 2FA コードデバッガー
2FA コードを生成し、完全な導出とともにデバッグします。
- URL エンコード / デコード
値や URL 全体をパーセントエンコード、その逆も。
- JSON 文字列エスケープ
テキストを JSON 文字列用にエスケープ、または元に戻す。