Z85 のエンコードとデコードの方法
- バイト列を16進数で 入力 · Hex(16進数) に貼り付けます。スペース、コロン、
0xプレフィックスも受け付けるので、16進ダンプをそのまま貼り付けられます。 - 出力 · Z85(ZeroMQ) で Z85 を確認します。
- バイト数が 4 の倍数でない場合は、そのバイト数を示すエラーが出ます。最後の不完全なブロックを許可(非標準) にチェックを入れるのは、双方がその拡張に合意している場合だけにしてください。
- デコードするには、方向の切り替えで Z85(ZeroMQ) → Hex(16進数) をクリックし、Z85 文字列を貼り付けます。別の表示にするには 変換先 を Base64 や テキスト にし、バイナリファイルにするには バイトを保存 を使います。
仕様のテストベクター
ZeroMQ の Z85 仕様(ZeroMQ RFC シリーズの RFC 32)には 2 つのテスト値があります。このツールはどちらも再現します。
| 16進数 | Z85 |
|---|---|
86 4F D2 6F B5 59 F7 5B | HelloWorld |
8E 0B DD 69 76 28 B9 1D 8F 24 55 87 EE 95 C5 B0 4D 48 96 3F 79 25 98 77 B4 9C D9 06 3A EA D3 B7 | JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6 |
1 つ目はブロックの構造を示しています。8 バイト、2 ブロック、10 文字です。2 つ目は 32 バイトの鍵とその 40 文字のエンコードで、CurveZMQ の設定で目にするサイズです。
いくつかの境界値を挙げます。ゼロのバイト 4 つは 00000、ff のバイト 4 つは %nSc0 です。Z85 にはゼロのブロックを省略する仕組みがないので、ゼロが続いても 1 ブロックにつき 5 文字のままです。
ZeroMQ が Z85 を使う理由
CurveZMQ の鍵は 32 バイトのランダムなバイト列です。ZeroMQ には、それを設定ファイル、コマンドライン、ソースコードに普通の文字列として書く方法が必要でした。Z85 はそのために設計されています。
- 生のバイト列より 25% 大きいだけで、Base64 の 33% より小さく済みます。32 バイトの鍵は 44 文字ではなく 40 文字です。
- アルファベットに
"、'、\、バッククォートがないので、C、Python、シェルの文字列リテラルにエスケープなしで入れられます。,と;も使いません。 - 数字と英字が先頭に並び(
0-9 a-z A-Z)、その後に記号 23 文字.-:+=^!/*?&<>()[]{}@%$#が続きます。
ただし <、>、& は含まれるので、XML や HTML の中に Z85 を入れる場合はエスケープが必要です。JSON なら不要です。
Python での Z85
Z85 と Python の b85encode は計算方法が共通で、違いはアルファベットだけなので、文字の置き換えで相互に変換できます。
import base64
B85 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
Z85 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"
TO_Z85 = str.maketrans(B85, Z85)
FROM_Z85 = str.maketrans(Z85, B85)
def z85encode(data: bytes) -> str:
if len(data) % 4:
raise ValueError("Z85 needs a multiple of 4 bytes")
return base64.b85encode(data).decode().translate(TO_Z85)
def z85decode(text: str) -> bytes:
return base64.b85decode(text.translate(FROM_Z85))
z85encode(bytes.fromhex("864FD26FB559F75B")) # 'HelloWorld'
すでに PyZMQ を使っているなら、zmq.utils.z85.encode と decode で同じことができます。C では libzmq に zmq_z85_encode と zmq_z85_decode があります。Python でのバイト列の扱いについては Python のページ で詳しく解説しています。
よくある問題
- 「Z85 は 4 バイト単位でエンコードしますが、データは N バイトです」。 入力がブロック単位に揃っていません。鍵全体をコピーしたか確認してください。32 バイトのはずの鍵が 31 バイトと表示されるなら、16進数 2 桁が失われています。
- 「Z85 は 5 文字単位のグループが必要ですが、N 文字余っています」。 Z85 文字列が途中で切れているか、余分な文字が含まれています。CurveZMQ の鍵はちょうど 40 文字でなければなりません。
- 文字列はデコードできるのに鍵として使えない。 Ascii85 か Git の Base85 かもしれません。長さのルールは同じでもアルファベットが違うので、同じ文字列が別のバイト列にデコードされます。Base85 のページ で 変換元 を別の変種にして試してください。
よくある質問
Z85 が 4 バイトの倍数を要求するのはなぜですか?
仕様が Z85 を 4 バイトの完全なブロックに対してのみ定義しているので、デコーダーは 5 文字が必ず 4 バイトになると想定できます。それ以外の長さのデータは、アプリケーション側でパディングするか枠付けする必要があります。それでも末尾を Ascii85 の方式でエンコードするには、最後の不完全なブロックを許可(非標準) にチェックを入れてください。
Z85 の CurveZMQ 鍵は何文字ですか?
40 文字です。CurveZMQ の公開鍵と秘密鍵は 32 バイトで、32 / 4 × 5 = 40 になります。これは zmq_curve_keypair() が返す長さで、C ではこれに終端のヌル文字が加わります。
Z85 は Ascii85 と同じですか?
計算方法は同じで、アルファベットが違います。どちらも 4 バイトを 85 進数 5 桁で書きますが、Z85 は数字と英字を先に並べ、引用符とバックスラッシュを使わず、z による省略も <~ ~> の区切りもありません。
Python で Z85 をエンコードするには?
標準ライブラリに Z85 はありませんが、base64.b85encode が同じ計算方法を使うので、出力のアルファベットを置き換えれば Z85 になります。このページのコード例がそれを行っています。PyZMQ にも zmq.utils.z85 があります。