キー、アドレス、ウォレット

イントロダクション

bitcoinの所有権は、 デジタルキーBitcoinアドレスデジタル署名 で規定されます。デジタルキーはBitcoinネットワークの中に保持されておらず、ユーザによって作成され個々のユーザのファイルの中、または ウォレット という簡単なデータベースの中に保持されています。ユーザのウォレットの中にあるデジタルキーは完全にBitcoinプロトコルとは独立しており、ブロックチェーンへの参照またはインターネットへのアクセスを必要とすることなくユーザのウォレットでデジタルキーの生成および管理ができるようになっています。このキーによってBitcoinの多くの興味深い性質が実現可能になっています。この性質にはde-centralized trustや資産コントロール/所有権の証明、暗号学的証明によるセキュリティモデルが含まれます。

全てのBitcoinトランザクションには有効な署名が必要であり、これによってトランザクションをブロックチェーンに含めることができます。有効な署名は有効なデジタルキーのみによって生成できます。よって、このデジタルキーのコピーを持つ人であればどんな人でも、このデジタルキーに紐づく口座にあるbitcoinをコントロールできます。銀行の口座番号と似た公開鍵と、銀行のPINコードに似た秘密鍵、また小切手への署名について考えてみましょう。これらのデジタルキーをごく稀にユーザが確認することがありますが、ほとんどの場合ウォレットのファイルの内部に格納されウォレットによって管理されるため目にすることはありません。

Bitcoinトランザクションの支払い情報の一部には受取人の公開鍵が表示されており、この公開鍵は Bitcoinアドレス と呼ばれるデジタルフィンガープリントの形で表示されています。このBitcoinアドレスは小切手にある受取人名(米国小切手にある "Pay to the order of")と同じようにも使われます。多くの場合、Bitcoinアドレスは公開鍵から生成され1つの公開鍵に対応しています。しかし、全てのBitcoinアドレスが公開鍵を表現しているわけではありません。この後の章でも見る通り、scriptのような形でも他の受取人を表現できます。この方法では、Bitcoinアドレスは資金の受取人を要約しているだけであり、小切手用紙のように取引の相手方を柔軟に変更できるようにしています。これは、支払い手段として、個人の口座への支払い、企業の口座への支払い、請求書に対する支払い、現金としての支払いなどがあるようなものです。Bitcoinアドレスはユーザが繰り返し見るキーの1つの表現です。なぜなら、BitcoinアドレスはBitcoinネットワークの中で共有される必要があるものだからです。

この章では、暗号学的キーを含むウォレットを紹介します。どのようにキーが生成され、格納され、管理されているのかを見ていき、秘密鍵や公開鍵、Bitcoinアドレス、scriptアドレスを表すいろいろなエンコード形式を説明します。最後に、メッセージに署名したり所有権を証明したり文字列指定のあるBitcoinアドレスやペーパーウォレットを作成したりするためのキーの特別な使用方法を見ていきます。

公開鍵暗号と暗号通貨

公開鍵暗号は1970年代に発明され、計算機および情報セキュリティ分野の数学的基礎になっていました。

公開鍵暗号が発明されてから、いくつかの適した数学的関数が発見されていきました。この数学的関数は、 素数のべき乗や楕円曲線上のスカラー倍算などです。これらの数学的関数は現実な時間で考えると不可逆ものになっています。不可逆とは、一方向への計算は簡単でも逆方向の計算は実行不可能なことを言います。これらの数学的関数に基づく暗号は、デジタルシークレットや偽造不可能なデジタル署名の生成を可能にしています。Bitcoinは公開鍵暗号として楕円曲線上のスカラー倍算を使っています。

Bitcoinでは、bitcoinへのアクセスコントロールをするキーペアを生成するために公開鍵暗号を使っています。このキーペアは秘密鍵とこの秘密鍵から一意に生成される公開鍵で構成されています。この公開鍵はbitcoinを受け取るために使われ、秘密鍵はBitcoinトランザクションに署名するのに使われます。

公開鍵と署名生成に使われる秘密鍵の間には数学的な関係があります。この署名は秘密鍵を公開することなく公開鍵だけで検証できるようになっています。

bitcoinを使うとき、bitcoin所有者は公開鍵と署名(生成するごとに異なるものになりますが、同じ秘密鍵から生成されます)をBitcoinトランザクション内に記載します。この公開鍵と署名を通して、Bitcoinネットワークの全ての人はこのトランザクションが有効なものであると検証でき、bitcoinを送った人が送る時点でこのbitcoinを所有しているということを確認できるのです。

Tip

ほとんどのウォレット実装では、利便性のため秘密鍵と公開鍵が キーペア として一緒に保存されています。しかし、この公開鍵は秘密鍵から計算できるため、秘密鍵だけを保存しておくことも可能です。

秘密鍵と公開鍵

Bitcoinウォレットにはキーペアリストを持っており、それぞれのキーペアは秘密鍵と公開鍵で構成されています。秘密鍵(k)は数値で、通常ランダムに選ばれます。秘密鍵から不可逆関数である楕円曲線上のスカラー倍算を用いて公開鍵(K)を生成します。そして、この公開鍵(K)から不可逆ハッシュ関数を用いてBitcoinアドレス(A)を生成します。この節では、秘密鍵の生成から始めて、公開鍵の生成に使われる楕円曲線の数学を見ていき、最後にBitcoinアドレスを公開鍵から生成します。秘密鍵と公開鍵、Bitcoinアドレスの関係は[k_to_K_to_A]図に書かれています。

秘密鍵k -> 公開鍵K -> BitcoinアドレスA
Figure 1. 公開鍵、秘密鍵、Bitcoinアドレス

秘密鍵

秘密鍵は意図的に指定できるものではなく無作為に選ばれる数値です。秘密鍵による所有権管理はBitcoinアドレスに結びついた全ての資金の根幹をなします。秘密鍵は署名を生成するときに使われ、この署名は資金を使うときに所有権の主張に必要となります。秘密鍵はいつでも極秘に保っておかなければいけません。もし他者に秘密鍵が漏れると、秘密鍵に対応したBitcoinアドレスの資金のコントロールを他者に与えることになってしまうためです。秘密鍵をバックアップをしておくだけでなく、秘密鍵の偶発的な紛失からも保護しておかなければいけません。というのは、もし秘密鍵をなくしてしまうと、秘密鍵を復元することはできず、秘密鍵によってセキュリティを担保していた資金も永遠に失ってしまうためです。

Tip

Bitcoin秘密鍵は単なる数値で、コインや鉛筆、紙だけを使ってランダムに秘密鍵を選ぶことができます。例えば、コインを256回投げてBitcoinウォレットで使うランダムな秘密鍵の二進数を作り出すことができます。公開鍵はこの秘密鍵から生成することができます。

ランダムな数値からの秘密鍵の生成

キーを生成する上で重要かつ一番最初にしなればいけないことは、キー生成の安定的なエントロピー源、つまり十分なランダムさを確保することです。Bitcoinキーを作ることは、"1から 2256 までの間の数字を選ぶ"ということと本質的に同じです。数字を選ぶ厳格な方法は、予測可能であったり再現可能性があったりしない方法です。Bitcoinソフトウェアは、256bitのエントロピー(ランダムさ)を作り出すOSのランダム値生成器を使っています。通常OSのランダム値生成器は人間を元にしたランダムさを使って初期化されます。数秒間だけマウスを小刻みに動かしてくださいとOSからお願いされたことがあるかもしれないですが、それはこのランダム値生成が理由です。

さらに正確に言うと、秘密鍵は 1n - 1 の間の任意の整数で、nはBitcoinで使われている楕円曲線の位数として定義されている定数です(n = 1.158 * 1077で2256 よりわずかに小さい)([elliptic_curve]図参照)。このようなキーを作るために、256bitの数値からランダムに数値を選び、選んだ数値が n - 1 より小さいかをチェックしています。プログラミング用語で言うとこれは通常、暗号学的に安定なランダムさ源から集められた任意の長さを持ったより大きい文字列を取得し、256bitの数値を作る便利なSHA256ハッシュアルゴリズムに放り込むことで達成できます。もし結果が n - 1 よりも小さかった場合、それが適切な秘密鍵です。もしそうでなければ、単にもう一度別のランダムな数値を取得してうまくいくまで同じことを繰り返します。

Tip

自身でランダムな数値を作り出すコードを書いたり、使っているプログラミング言語が提供している"簡単な"ランダム数値生成器を使ったりしないでください。十分なエントロピー源からのシードを伴った暗号学的に安全な擬似乱数生成器(CSPRNG)を使ってください。選んだ乱数生成器ライブラリのドキュメントを読んで暗号学的に安全かどうかを確認してください。CSPRNGの正しい実装はキーのセキュリティにとって決定的に重要な部分になります。

以下はランダムに生成された秘密鍵(k)で、16進数形式(それぞれ4bitずつ64個の16進数整数で表されている256bit整数)になっています。

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD
Tip

Bitcoinの秘密鍵スペースのサイズは 2256 であり、途方もなく大きな数字です。10進数で約 1077 になります。この数字の分かりやすい比較として原子の数を考えてみましょう。観測可能な宇宙には 1080 個の原子があると見積もられており、秘密鍵が取り得るパターン数はこの原子の数くらいの数になります。

Bitcoin Coreクライアントで新しい鍵を生成するために前に説明した getnewaddress コマンドを使います([ch03_bitcoin_client]図参照)。セキュリティのため、公開鍵だけを表示しており秘密鍵は表示していません。 bitcoindに秘密鍵を表示させるには、 dumpprivkey コマンドを使ってください。 dumpprivkey コマンドは Wallet Import Format (WIF) と呼ばれるBase58エンコード形式で秘密鍵を表示します。この詳細については[priv_formats]図で説明します。 getnewaddress コマンドと dumpprivkey コマンドを使った秘密鍵の生成例と表示例を以下に示します。

$ bitcoind getnewaddress
1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
$ bitcoind dumpprivkey 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

dumpprivkey コマンドはウォレットを開き、さきほどの getnewaddress コマンドで生成された秘密鍵を抽出します。このウォレットに秘密鍵と公開鍵の両方が格納されていなければ、bitcoindが公開鍵を秘密鍵から知ることはできません。

Tip

+dumpprivkey+コマンドは公開鍵から秘密鍵を生成しているわけではありません。これは不可能だからです。このコマンドは単にウォレットがすでに知っている秘密鍵を表示しているだけで、この秘密鍵はgetnewaddressコマンドで生成されたものです。

また、秘密鍵を生成および表示するためにBitcoin Explorerコマンドラインツール([libbitcoin]図参照)の seed コマンド、 ec-new コマンド、 ec-to-wif コマンドを使うこともできます。

$ bx seed | bx ec-new | bx ec-to-wif
5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

公開鍵

公開鍵は楕円曲線上のスカラー倍算を使って秘密鍵から計算されるもので、この処理は不可逆な処理になっています。\(K = k * G\) 但し k は秘密鍵、 G_ は _ベースポイント と呼ばれる定点、 K は結果として出てくる公開鍵です。逆操作は、離散対数問題 - K を知っていたときに k を導出する問題 - として知られ、この難しさは k の全ての可能な値を総当たりで調べるのと同じくらい時間がかかる問題です。秘密鍵から公開鍵を生成する方法を説明する前に、楕円曲線暗号をもうちょっと細かく見てみましょう。

楕円曲線暗号

楕円曲線暗号は、楕円曲線上の点に対する加法とスカラー倍算で表現される離散対数問題をベースに作られた非対称型暗号方式または公開鍵暗号方式です。

[ecc-curve]図はBitcoinで使われているものと似ている楕円曲線の例です。

楕円曲線
Figure 2. 楕円曲線

Bitcoinは特別な楕円曲線を使っており、 National Institute of Standards and Technology (NIST)で標準化された secp256k1 と呼ばれる集合を使っています。 secp256k1 曲線は次のような関数で定義されており、この関数は楕円曲線になっています。

\begin{equation} {y^2 = (x^3 + 7)}~\text{over}~(\mathbb{F}_p) \end{equation}

または

\begin{equation} {y^2 \mod p = (x^3 + 7) \mod p} \end{equation}

mod p (素数pを法とした剰余)とは、この曲線が、位数が素数_p_の有限体をなしていることを示しており、\(\mathbb{F}_p\) 但し p = 2256 – 232 – 29 – 28 – 27 – 26 – 24 – 1(とても大きい素数) のようにも書かれます。

この曲線は実数ではなく素数位数の有限体をなしているため、二次元に散りばめられたドットパターンのように見えます。しかし、この数学は実数上に定義された楕円曲線の数学と同等です。例として、[ecc-over-F17-math]図はより小さい位数17の有限体をなす楕円曲線を示していて、グリッド上のドットパターンになっています。 secp256k1 Bitcoin楕円曲線は、底が知れないほど大きなグリッド上にもっと複雑に描かれたドットパターンと考えることができます。

有限体F(17)をなしている楕円曲線
Figure 3. 楕円曲線暗号: p=17の有限体F(p)をなしている楕円曲線を可視化したもの

例えば、以下は secp256k1 曲線上の点Pです。これが secp256k1 曲線上にあることは以下のPythonコードを使って自身で確かめることができます。

P = (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)

楕円曲線では、 "無限遠点"と呼ばれる点があります。この点は簡単に言って足し算での0に対応しています。コンピュータ上では、ときどき x = y = 0 と表現されます(これは楕円曲線方程式を満たしていませんが、特別な場合として簡単に確かめることができる解です)。

+ 演算子は加法と呼ばれ、小学校で習う実数に対する加法と似たいくつかの性質を持ちます。楕円曲線上にある2つの点 P1 と P2 が与えられたとき、3つ目の点 P3 = P1 + P2 があり楕円曲線上にあります。

幾何学的には、3つ目の点 P3 は P1 と P2 を通る直線を描くことによって計算されます。この直線は必ず楕円曲線と別のところで交差します。この交差した点を P3' = (x, y) とすると、 P3 はX軸に対して反転したところにある点 P3 = (x, –y) になります。

無限遠点に関して2つ説明しなければいけない特別な場合があります。

もし P1 と P2 が同じ点だったとすると、 P1 と P2 を"通る"直線は P1 で曲線に接する接線となるはずです。この接線は曲線と新しい点と必ず交わります。接線の傾きを決めるのに微積分のテクニックを使うことができます。奇妙にも、実数ではなく整数座標で構成される曲線にしたとしても微積分のテクニックはうまくいくのです!

いくつかの場合( P1 と P2 が同じX座標を持ちY座標が違う場合など)、接線は厳密に垂直となり P3 は無限遠点となります。

もし P1 が無限遠点である場合、和は P1 + P2 = P2 となります。同様に、もし P2 が無限遠点である場合、P1 + P2 = P1 となります。これが示していることは、無限遠点が0の役割をしているということです。

+ が (A + B) + C = A + (B + C) という結合則を満たすことがわかります。これは括弧をなくても何のあいまいさもなく A + B + C と書けることを意味します。

加法が定義されたので、加法を拡張する標準的な方法に沿ってスカラー倍算を定義できます。楕円曲線上の点Pに対して、もしkが整数だとすると、kP = P + P + P + … + P (k回)。紛らわしいことにこのkがときどき"べき指数"と呼ばれるのです。

公開鍵の生成

秘密鍵をランダムに生成された数値 k とすると、あらかじめ決められた ベースポイント Gk に掛けることで楕円曲線上のもう1つの点を得ます。このもう1つの点は公開鍵 K に対応するものです。ベースポイントは secp256k1 標準で決められており、Bitcoinでの全ての公開鍵に対して常に同じです。

\begin{equation} {K = k * G} \end{equation}

但し、kは秘密鍵、Gはベースポイント、Kは結果として出てくる公開鍵で楕円曲線上にある点です。ベースポイントはどのBitcoinユーザでも同じであるため、秘密鍵kが同じであれば公開鍵は同じになります。kとKの関係は固定されていますが、kからKという一方向でのみ計算ができます。これは、(Kから得られる)Bitcoinアドレスがどの人にも共有され得るからで、Bitcoinアドレスから秘密鍵kを知ることはできません。

Tip

秘密鍵は公開鍵に変換できますが、公開鍵は秘密鍵に戻すことはできません。これは秘密鍵から公開鍵への変換プロセスが一方向だけにしか使えないからです。

楕円曲線上のスカラー倍算を実装すると、前に生成した秘密鍵kをベースポイントGに掛けることで公開鍵Kが得られます。

K = 1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD * G

Public Key K is defined as a point K = (x,y):

K = (x, y)

但し

x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

点への整数のスカラー倍算を可視化するために、数理的な構造が同じ実数に対する簡単な楕円曲線を使います。ゴールは、ベースポイントGから決められるkGを見つけることです。これは、G自身をk回足すことと同じです。楕円曲線で自分自身に足すとは、点に接する接線をを描きその接線がもう一度楕円曲線に交わる点、X軸に対して対称な点、を見つけることです。

[ecc_illustrated]図は、楕円曲線上で幾何学的な操作を繰り返すことでG, 2G, 4Gを導きだすプロセスを表しています。

Tip

ほとんどのBitcoin実装では、 OpenSSL 暗号学的ライブラリを使って楕円曲線での数学の計算をしています。例えば、公開鍵を導出するために関数 EC_POINT_mul() が使われています。

楕円曲線暗号
Figure 4. 楕円曲線暗号: 楕円曲線上での整数 k によるベースポイント G へのスカラー倍算を可視化したもの

Bitcoinアドレス

Bitcoinアドレスは、あなたにお金を送りたい人誰にでも共有されえる、数値と文字で構成された文字列です。公開鍵から作られるBitcoinアドレスは数値と文字による文字列になっており、1からはじまるものです。以下はBitcoinアドレスの例です。

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

Bitcoinアドレスは、資金の受取人としてトランザクションに共通に現れるものです。小切手(英語)とBitcoinトランザクションを比べてみましょう。Bitcoinアドレスは米国小切手の"Pay to the order of"のあとに書かれる受益者名です。小切手上では、この受益者は銀行口座を持っている人の名前になりますが、企業名や機関名、現金になることもあります。小切手では口座を特定する必要はないので、むしろ資金の受取人として口座を指す抽象名を使います。この抽象名を使うことで小切手での支払いがしやすくなります。BitcoinトランザクションではBitcoinアドレスという小切手に似た抽象名を使い、Bitcoinトランザクションを使いやすいものにしています。Bitcoinアドレスは秘密鍵/公開鍵の所有者、または[p2sh]にある支払いscriptのような他の何かを表すものです。とりあえずBitcoinアドレスが公開鍵から生成される簡単な場合から説明していきましょう。

Bitcoinアドレスは一方向暗号学的ハッシュ化を使うことで公開鍵から生成されます。"ハッシュ化アルゴリズム"または簡単に"ハッシュアルゴリズム"は、フィンガープリントまたは任意のサイズの"ハッシュ"を作り出す一方向関数です。暗号学的ハッシュ関数は、Bitcoin、Bitcoinアドレス、scriptアドレス、proof-of-workマイニングアルゴリズムの中で使われます。公開鍵からBitcoinアドレスを作るときに使うアルゴリズムは、 Secure Hash Algorithm (SHA) と RACE Integrity Primitives Evaluation Message Digest (RIPEMD) で、中でも SHA256 と RIPEMD160 が使われます。

公開鍵KのSHA256ハッシュを計算し、さらにこの結果のRIPEMD160ハッシュを計算することで、160bit(20byte)の数字を作り出します。

\begin{equation} {A = RIPEMD160(SHA256(K))} \end{equation}

但し、Kは公開鍵、Aは結果として出てきたBitcoinアドレスです。

Tip

Bitcoinアドレスは公開鍵と同じでは ありません 。Bitcoinアドレスは公開鍵から一方向関数を使って導出されるものです。

Bitcoinアドレスは、 "Base58Check" と呼ばれる形にエンコードされた状態で通常使われます([base58]参照)。"Base58Check"では、58個の文字(Base58)とチェックサムを使いますが、これは人間にとって読みやすくしたり、曖昧さを避けたり、転写時のエラーを防いだりするためです。Base58Checkはまた、Bitcoinで他の用途でも使われます。Bitcoinアドレス、秘密鍵、暗号化された鍵、scriptハッシュなど、ユーザが読む必要があったり、数字を正しく転写しなければいけなかったりするときはいつでもです。次の節では、Base58Checkのエンコード、デコードの仕組みと結果として出てくる表現について説明します。[pubkey_to_address]図は公開鍵からBitcoinアドレスへの変換を説明しています。

公開鍵からアドレスへ
Figure 5. 公開鍵からBitcoinアドレスへ: 公開鍵をBitcoinアドレスに変換するプロセス

Base58とBase58Checkエンコード

大きな数字をコンパクトに表すために、多くのコンピュータではいくつかの記号を使うことで10以上を基数とするアルファベットと数字を混ぜた表現を使っています。例えば、伝統的な10進数では0から9までの10個の数字を使う一方、16進数ではAからFの文字を使うことで16個の数字を使います。16進数で表される数字は10進数で表すよりも短くなります。 Base-64 では、26個の小文字、26個の大文字、10個の数字、バイナリデータをemailのようなテキストベースの通信で送るための"+" や "/" のような2種類の文字を使います。Base-64 はemailにバイナリデータを添付するのによく使われます。Base58 はBitcoinで使うために開発されたテキストベースのエンコード形式で、他の暗号通貨でも使われています。これはコンパクトな表現、可読性、エラー発見および防止のためです。Base58はBase64の部分集合でアルファベットの大文字小文字、数字が使われます。しかし、あるフォントで表示したときに同じように見えて、よく間違えてしまういくつかの文字は省かれています。Base58はBase64から0(数字の0)、O(大文字o)、l(小文字L)、I(大文字i)、記号"\+"や"/"を除いたもの、つまり、(0, O, l, I)を除いたアルファベットの大文字小文字、数字の集合になっています。

Example 1. BitcoinにおけるBase58のアルファベット
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

書き間違いや転写間違いをさらに防ぐため、Base58Checkはチェックサムを加えたBase58エンコード形式になっていてBitcoinで頻繁に使われています。チェックサムは、エンコードされようとしているデータの最後に追加される4byteです。このチェックサムはエンコードされたデータのハッシュから作られ、転写間違いやタイピング間違いを検出したり防いだりするのに使われます。Base58Checkでエンコードされたデータが与えられた場合、デコードソフトウェアはエンコードされようとしているデータのチェックサムを計算し、含まれているチェックサムと比較します。もし2つが一致しなかった場合、これはエラーが混入してしまったかBase58Checkデータが無効だということを示しています。これによって、例えば、ウォレットが有効な送り先だと判断して受け付けてしまった打ち間違いBitcoinアドレスを無効と判断し、資金を失ってしまうということを防ぐことができます。

数値データをBase58Check形式に変換するために、まずデータの先頭に"version byte"と呼ばれている文字を追加します。このversion byteは、簡単にエンコードされたデータの種類を特定できるように付加されています。例えば、Bitcoinアドレスの場合先頭はゼロ(16進数で0x00)で、一方秘密鍵をエンコードするときは先頭は128(16進数で0x80)です。共通に使われているversion byteのリストは[base58check_versions]を参照してください。

次に、"double-SHA"チェックサムを計算してみましょう。これは、SHA256ハッシュアルゴリズムを前の結果(プレフィックスとデータ)に2回適用するという意味です。

checksum = SHA256(SHA256(prefix+data))

結果として出てくる32byteハッシュ(ハッシュのハッシュ)から最初の4byteだけを取り出します。これら4byteをエラーチェックコードとして、または チェックサムとして使用されます。このチェックサムは最後に付加されます。

結果は3つの部分、プレフィックス、データ、チェックサムによって構成されていて、前に書いたBase58のアルファベットを使ってエンコードされています。[base58check_encoding]図はBase58Checkエンコードのプロセスを説明しています。

Base58Checkエンコーディング
Figure 6. Base58Checkエンコーディング: 曖昧さなくBitcoinデータをエンコードするために、version byte、チェックサム付加しBase58変換をしたフォーマット

Bitcoinでは、データをコンパクトにするためや読みやすくするため、エラーを検知しやすくするために、ユーザに渡されるほとんどのデータをBase58Checkエンコードにしています。Base58Checkでのversion prefixは、簡単に形式を区別するために使われており、Base58でエンコードされるときにBase58Checkエンコードpayloadの最初に特定の文字を付けられています。これらの文字は、どんな種類のデータをエンコードしたのか、データをどう使うのかを人間が分かるようにしています。1から始まるものはBase58CheckエンコードされたBitcoinアドレス、5から始まるものはBase58Checkエンコードされた秘密鍵WIF形式です。

Table 1. Base58Checkのversion prefixとエンコードされた結果の例
種類 Version prefix (16進数) Base58出力文字列の先頭に付けられるプレフィックス

Bitcoinアドレス

0x00

1

Pay-to-Script-Hashアドレス

0x05

3

Bitcoin Testnet アドレス

0x6F

m or n

秘密鍵 WIF形式

0x80

5, K or L

BIP38 暗号化秘密鍵

0x0142

6P

BIP32 拡張公開鍵

0x0488B21E

xpub

Bitcoinアドレスを生成する完全な手順を見てみましょう。秘密鍵から始まって、公開鍵(楕円曲線上の点)を作り、二重ハッシュ化アドレスを作り、最後にBase58Checkエンコードします。[addr_example]にあるC++コードは、秘密鍵からBase58Checkエンコード済みBitcoinアドレスまでの手順を完全な形で逐一示しています。このサンプルコードは、いくつかの補助関数を使うために[alt_libraries]で紹介したlibbitcoinライブラリを使っています。

Example 2. 秘密鍵からのBase58CheckエンコードされたBitcoinアドレスの作成

このコードは事前に決められた秘密鍵を使っており、動作させるたびに毎回同じBitcoinアドレスが生成されるようになっています。具体的な動かし方は[addr_example_run]に示されている通りです。

Example 3. このaddrコードのコンパイルと実行

キーフォーマット

秘密鍵も公開鍵も多くの違った形式で表現されています。見た目が違っていたとしても、これらの表現は全て同じ数値にエンコードされます。これらの形式は基本的に人々が読みやすくなる、間違うことなく転写できるようになるために使われます。

秘密鍵フォーマット

秘密鍵は多くの違った形式で表現されていて、これらは全て同じ256bitの数値に対応しています。[table_4-2]に秘密鍵を表現するために使われる3つの形式を示します。

Table 2. 秘密鍵の表現一覧(エンコーディングフォーマット
種類 プレフィックス 説明

16進数

None

64個の16進数

WIF

5

Base58Checkエンコーディング: 128のversion prefixと、32bitチェックサムを伴ったBase58

圧縮WIF

K または L

上にあるように、エンコード前にサフィックス 0x01 が追加されています

[table_4-3]は、これら3つの形式で生成された秘密鍵を示しています。

Table 3. 例: 同じキーの違ったフォーマット
フォーマット 秘密鍵

16進数

1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd

WIF

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

WIF圧縮形式

KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

これら全ての表現は、同じ数値、同じ秘密鍵を表す違った形式になっています。ぱっと見は違っていますが、どれも他の形式に簡単に変換できます。

Bitcoin Explorer([libbitcoin]参照)の wif-to-ec コマンドをを使うことで、さきほどの両方のWIFキーが同じ秘密鍵を表すことを示すことができます。

$ bx wif-to-ec 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd

$ bx wif-to-ec KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
Base58Checkからのデコード

Bitcoin Explorerコマンド([libbitcoin]参照)は、Bitcoinキーやアドレス、トランザクションを操作するシェルスクリプトやコマンドラインの "パイプライン" を簡単に書けるようにするツールです。Bitcoin Explorerを使うとコマンドラインでBase58Checkをデコードできます。

base58check-decode コマンドを使うことで圧縮されていないキーをデコードできます。

$ bx base58check-decode 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
wrapper
{
    checksum 4286807748
    payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
    version 128
}

出力された結果には、payloadとしてのキーとWallet Import Format (WIF)のversion prefix 128、チェックサムが含まれています。

圧縮されたキーの"payload"にはサフィックス 01 が追加されており、出力された公開鍵が圧縮されたものであることを表しています。

$ bx base58check-decode KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
wrapper
{
    checksum 2339607926
    payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01
    version 128
}
16進数からBase58Checkエンコード

Base58Checkにエンコード(前のコマンドの逆)するために、Bitcoin Explorer の base58check-encode コマンド([libbitcoin]参照)に16進数秘密鍵を与えると、 Wallet Import Format (WIF)のversion prefix 128が返ってきます。

bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd --version 128
5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
16進数(圧縮されたキー)からBase58Checkへのエンコード

"圧縮された"秘密鍵としてBase58Checkにエンコードする([comp_priv]参照)ためには、16進数鍵の末尾に 01 を追加し上記のようにエンコードします。

$ bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01 --version 128
KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

生成されたWIF圧縮形式は"K"から始まります。これは、元の秘密鍵の最後に"01"が付いていることを意味し、圧縮された公開鍵のみを生成するために使われます([comp_pub]参照)。

公開鍵フォーマット

公開鍵もまた違った形で表現され、 圧縮された 公開鍵または 圧縮されていない 公開鍵があります。

前に見たように、公開鍵は楕円曲線上の点であり、(x,y) というペアの形で構成されます。これは普通プレフィックスに 04 が伴って表されます。この 04 のあとに256bitの2つの数字、1つは x 座標、もう1つは y 座標、が続きます。プレフィックス 04 は圧縮されていない公開鍵を圧縮された公開鍵と区別するために使われます。圧縮された公開鍵は 0203 から始まります。

ここで、さきほど作った秘密鍵から公開鍵を生成してみましょう。以下に x 座標と y 座標を示します。

x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

以下は、520bitの数値(130桁の16進数整数)として表したさきほどと同じ公開鍵です。これは 04 のプレフィックスが付いており、そのあとに 04 x y のように x 座標と y 座標が続きます。

K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A<?pdf-cr?>07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
圧縮された公開鍵

圧縮された公開鍵は、トランザクションのサイズの削減やブロックチェーンを保持しているBitcoinノード上のディスクスペースの保護のためにBitcoinに導入されました。ほとんどのトランザクションは、所有者の証明書を検証したりbitcoinを使うために公開鍵を含んでいるのです。それぞれの圧縮されていない公開鍵は520bit(プレフィックス \+ x \+ y)を必要とするため、ブロックごとに数百トランザクション、1日に数万トランザクションというトランザクションを重ねると巨大なデータがブロックチェーンに追加されていくことになってしまいます。

[pubkey]で見てきたように、公開鍵は楕円曲線上の点 (x,y) です。楕円曲線は数学的な関数として表現されるため、楕円曲線上の点は[pubkey]にある方程式の解であり、もし x 座標が分かるとすると y 座標は y2 mod p = (x3 + 7) mod p を解くことで計算できます。このため、公開鍵の点として単に x 座標だけを保持すればよく、 y 座標を省略して鍵のサイズと256bitを保持するのに必要なスペースを削減することができます。これによりトランザクションのデータサイズにして50%弱の削減ができます。

圧縮されていない公開鍵が 04 から始まるのに対して、圧縮されている公開鍵は 02 または 03 から始まります。なぜ2つのプレフィックスがあるのかというと、方程式の左側にはy2があるので、x座標からy座標を導こうとするとyの解は正負それぞれの符号を持った平方根になってしまい1つに決めることができません。イメージ的には、 y 座標がx軸の上側と下側の両方の値を取り得ることを意味します。[ecc-curve]図にある楕円曲線から分かるように、楕円曲線はx軸に対して鏡のように折り返す操作に対して対称です。このため、 y 座標の絶対値は省略できてもy座標の 符号 は省略できず保存しておく必要があります。別のいい方をすると、これら符号はそれぞれ違った点、違った公開鍵を表すので、点がx軸の上にあったか下にあったかを覚えておかなければいけません。素数位数pの有限体上で二進数演算を用いて楕円曲線を計算すると、 y 座標は偶数または奇数になり、これらはさきほど説明した正/負に対応しています。よって、yの2つの可能な値を区別するには、 y が偶数なら 02 を圧縮された公開鍵の先頭に付与、 y が奇数なら+03+を先頭に付与するようにします。これによって、ソフトウェアは y 座標を x 座標から正しく導くことができ、公開鍵を圧縮することができるのです。公開鍵の圧縮は[pubkey_compression]図で説明されています。

公開鍵の圧縮
Figure 7. 公開鍵の圧縮

前に生成した公開鍵と同じものを以下に示します。以下は、 y 座標が奇数であることを示している+03+をプレフィックスとして持つ264bit(66桁の16進数)の圧縮された公開鍵です。

K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

圧縮されていない公開鍵と違うように見えますが、この圧縮された公開鍵は同じ秘密鍵から生成されたものです。重要なこととして、もし圧縮された公開鍵を二重ハッシュ化関数 (RIPEMD160(SHA256(K))) を使ってBitcoinアドレスに変換したとすると、違った Bitcoinアドレスが生成されるでしょう。これは1つの秘密鍵が2つの形式(圧縮と非圧縮)の公開鍵を生成し、それぞれの公開鍵を用いて別々のBitcoinアドレスを生成してしまうためですが、これは混乱させる元になってしまいます。

圧縮された公開鍵は次第にBitcoinクライアントでデフォルトになりつつあります。圧縮された公開鍵に対応することで、トランザクションのサイズおよびブロックチェーンのサイズを削減することに十分なインパクトがあるのです。しかし、全てのクライアントが圧縮された公開鍵に対応している訳ではありません。圧縮された公開鍵に対応している最近のクライアントは圧縮された公開鍵に対応していない古いクライアントから来たトランザクションを解釈しなければいけません。これはウォレットがもう1つのウォレットから秘密鍵をインポートするときに特に重要です。というのは、新しいウォレットがインポートされた秘密鍵に対応したトランザクションを見つけるためにブロックチェーンをスキャンしなければいけないためです。ウォレットはどのBitcoinアドレスをスキャンするべきでしょうか?圧縮されていない公開鍵から生成されたBitcoinアドレスでしょうか?それとも、圧縮された公開鍵から生成されたBitcoinアドレスでしょうか?両方とも有効なアドレスですが、これらは異なったBitcoinアドレスです!

この問題を解決するために、秘密鍵をウォレットからエクスポートする場合、今までと異なり最近のウォレットではWallet Import Formatで出力されます。Wallet Import Formatは、 圧縮された 公開鍵が秘密鍵から作られたということを示しており、Bitcoinアドレスは 圧縮された ものということが分かるのです。これによって、インポートされる側のウォレットは古いウォレットから来た秘密鍵か新しいウォレットから来た秘密鍵かを区別でき、公開鍵が圧縮されていてもいなくても公開鍵に対応したBitcoinアドレスが含まれているトランザクションをブロックチェーンから探し出すことができます。次の節で、どのようにこれが動いているのか詳細を見てみましょう。

圧縮された秘密鍵

皮肉にも、"圧縮された秘密鍵"という言葉は誤解を与えてしまいます。というのは、秘密鍵がWIF圧縮形式でエクスポートされた場合、実際には"圧縮されていない"秘密鍵より1byte 長い からです。これは、01を秘密鍵の最後に付加しているためで、01は秘密鍵が新しいウォレットから来たこと、秘密鍵から圧縮された公開鍵を生成すべきであることを意味しています。秘密鍵自体は圧縮されておらず、また圧縮することはできません。"圧縮された秘密鍵"という言葉は、本当は"圧縮された公開鍵を生成するために使うべき秘密鍵"という意味です。WIF圧縮形式やWIF形式をエクスポートをするときの形式という意味でのみ使うべきで、混乱を避けるために、"圧縮された"という言葉を秘密鍵に対して使うべきではないのです。

WIF圧縮形式とWIF形式は片方しか使えないということを覚えておいてください。圧縮された公開鍵を実装した新しいウォレットでは、秘密鍵はWIF圧縮形式(先頭がKまたはL)としてのみエクスポートされます。もしウォレットが古い実装のもので圧縮された公開鍵が使えないものであれば、秘密鍵はWIF形式(先頭が5)としてのみエクスポートされます。ここでのゴールは、圧縮された公開鍵またはBitcoinアドレスでブロックチェーンを探索しなければいけないか、圧縮されていないもので探索しなければいけないかをウォレットに教えることです。

もしウォレットが圧縮された公開鍵を実装していれば、全てのトランザクションで圧縮された方の方法を使うでしょう。秘密鍵は楕円曲線上の公開鍵点を導出し、この公開鍵は圧縮されます。圧縮された公開鍵はBitcoinアドレスを生成することに使われ、Bitcoinアドレスはトランザクションの中で使われます。圧縮された公開鍵を実装した新しいウォレットから秘密鍵をエクスポートするとき、Wallet Import Formatは秘密鍵の最後に1byteの 01 が付加される形に修正されます。結果として出力されるBase58Checkエンコード秘密鍵は"圧縮されたWIF形式"と呼ばれ、古いウォレットでのWIF形式(非圧縮)の場合の"5"の代わりにKまたはLから始まります。

[table_4-4]は同じキーを表しており、WIF形式とWIF圧縮形式でエンコードされています。

Table 4. 例: 同じキーの違ったフォーマット
フォーマット 秘密鍵

16進数

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD

WIF

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

16進数圧縮形式

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD_01_

WIF圧縮形式

KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

Tip

"圧縮された秘密鍵"は誤った名称です!これらは圧縮されていません。むしろ、WIF圧縮形式は圧縮された公開鍵やこれに対応したBitcoinアドレスを導出するためのみ使われるべきということを表しています。皮肉にも、"WIF圧縮形式"にエンコードされた秘密鍵は1byteだけ長いのです。というのは、"圧縮されていない"秘密鍵と区別するためにサフィックス 01 が追加されているためです。

PythonでのキーとBitcoinアドレスの実装

Pythonで書かれた最も総合的なBitcoinライブラリは Vitalik Buterin によって書かれた pybitcointools です。[key-to-address_script]の中で、pybitcointoolsライブラリ("bitcoin"としてimportされています)を使ってキーとBitcoinアドレスをいろいろな形式で生成しています。

Example 4. pybitcointoolsライブラリを使った、キーとアドレスの生成と各フォーマット生成

[key-to-address_script_run]はこのコードを実行した結果を示しています。

Example 5. key-to-address-ecc-example.pyの実行
$ python key-to-address-ecc-example.py Private Key (hex) is: 3aba4162c7251c891207b747840551a71939b0de081f85c4e44cf7c13e41daa6 Private Key (decimal) is: 26563230048437957592232553826663696440606756685920117476832299673293013768870 Private Key (WIF) is: 5JG9hT3beGTJuUAmCQEmNaxAuMacCTfXuw1R3FCXig23RQHMr4K Private Key Compressed (hex) is: 3aba4162c7251c891207b747840551a71939b0de081f85c4e44cf7c13e41daa601 Private Key (WIF-Compressed) is: KyBsPXxTuVD82av65KZkrGrWi5qLMah5SdNq6uftawDbgKa2wv6S Public Key (x,y) coordinates is: (41637322786646325214887832269588396900663353932545912953362782457239403430124L, 16388935128781238405526710466724741593761085120864331449066658622400339362166L) Public Key (hex) is: 045c0de3b9c8ab18dd04e3511243ec2952002dbfadc864b9628910169d9b9b00ec243bcefdd4347074d44bd7356d6a53c495737dd96295e2a9374bf5f02ebfc176 Compressed Public Key (hex) is: 025c0de3b9c8ab18dd04e3511243ec2952002dbfadc864b9628910169d9b9b00ec Bitcoin Address (b58check) is: 1thMirt546nngXqyPEz532S8fLwbozud8 Compressed Bitcoin Address (b58check) is: 14cxpo3MBCYYWCgF74SWTdcmxipnGUsPw3

[ec_math] はもう1つのコード例です。このコードでは、楕円曲線での計算にPython ECDSAライブラリを使い、いかなる特別なBitcoinライブラリも使っていません。

Example 6. Bitcoinのキー生成に使われる楕円関数数学のデモスクリプト

[ec_math_run]はこのコードを実行した結果を示しています。

Note

上記の例コードでは os.urandom を使用しており、これは裏で動作しているオペレーティングシステムによって提供されている暗号学的に安全な乱数生成器(CSRNG)の値を反映しています。LinuxのようなUNIX-likeなオペレーティングシステムの場合これは /dev/urandom から乱数を取得し、Windowsの場合 CryptGenRandom() を呼び出すことで乱数を取得します。もし適した乱数発生源がない場合、NotImplementedError が発生します。ここで使われている乱数生成器はデモ目的のものであり、十分なセキュリティを持ったように実装されていないので商用レベルのクオリティを持ったBitcoinキーを生成するには適切では ありません

Example 7. Python ECDSAライブラリのインストールとec_math.pyスクリプトの実行
$ # Install Python PIP package manager
$ sudo apt-get install python-pip
$ # Install the Python ECDSA library
$ sudo pip install ecdsa
$ # Run the script
$ python ec-math.py
Secret:  38090835015954358862481132628887443905906204995912378278060168703580660294000
EC point: (70048853531867179489857750497606966272382583471322935454624595540007269312627, 105262206478686743191060800263479589329920209527285803935736021686045542353380)
BTC public key: 029ade3effb0a67d5c8609850d797366af428f4a0d5194cb221d807770a1522873

ウォレット

ウォレットは秘密鍵のためのコンテナであり、通常構造化されたファイルまたは簡単なデータベースとして実装されています。 キーを作るもう1つの方法は、 決定性鍵生成 です。ここでは、一方向ハッシュ関数を使って前に出てきた秘密鍵から順々に新しい秘密鍵を作ってみます。秘密鍵を再生成するために必要なのは最初の鍵( シード または マスター キーとして知られているもの)だけです。この節では、キー生成の方法と決定性キーによって構築されたウォレットの構造を説明します。

Tip

Bitcoinウォレットはキーを保持していますが、bitcoinは保持していません。それぞれのユーザはbitcoinではなくキーを含むウォレットを持つことになります。ウォレット自体は本当に秘密鍵/公開鍵のペアを含む単なるキーホルダーなのです([private_public_keys]参照)。ユーザはトランザクションにキーを使って署名をし、トランザクションアウトプット(ユーザのbitcoin)を所有していることを証明します。このbitcoinはトランザクションアウトプット(よくvoutまたはtxoutと書かれます)の形でブロックチェーン上に保存されています。

非決定性(ランダム)ウォレット

最初のBitcoinクライアントでは、ウォレットは単にランダムに生成された秘密鍵の集まりでした。このタイプのウォレットを Type-0 非決定性ウォレット と呼びます。例えば、Bitcoin Coreクライアントは、初回起動のときにあらかじめ100個のランダムな秘密鍵を作成し、個々のキーは一度しか使われないためその後必要に応じてさらにキーを作ります。このタイプのウォレットは"Just a Bunch Of Keys"またはJBOKというニックネームがついています。ランダムなキーの不利な点は、もし多く生成したとするとそれら全てのコピーも保持しなければいけなくなる点です。つまり、ウォレットは頻繁にバックアップされなければならないということです。もしバックアップされていなければウォレットがアクセス不可能になって管理資金が永久に失われてしまうのです。このため、トランザクションごとに1回だけBitcoinアドレスを使うというBitcoinアドレス再利用回避方法は使えなくなってしまうのです。Bitcoinアドレスの再利用は、Bitcoinアドレスが多くのトランザクションに結びつくことでプライバシーの低下に繋がります。特にBitcoinアドレスの再利用を避けたいのであれば、Type-0 非決定性ウォレットを選ぶことはやめたほうがよいでしょう。多くのキーを管理することを意味し、頻繁にバックアップを作る必要が生じてしまうからです。Bitcoin CoreクライアントはType-0 ウォレットを含んでいますが、このウォレットの使用はBitcoin Coreの開発者たちから反対されています。[Type0_wallet]は非決定性ウォレットを示しており、ランダムなキーの緩い集まりを表現しています。

決定性(Deterministic , Seeded)ウォレット

決定性(Deterministic)または"Seeded"ウォレットは、全て共通のシードから生成される秘密鍵を持っているウォレットです。共通のシードからの生成は一方向ハッシュ関数を使います。このシードはランダムに生成される数値で、この数値は指数または"チェーンコード"([hd_wallets]参照)のような秘密鍵を生成するためのその他のデータと結びついています。決定性ウォレットでは、このシードを使えば生成された全てのキーを復活できるため、このシードを生成した時点で一回バックアップを取っておけば十分です。このシードはまたウォレットのエクスポートやインポートでも重要で、異なったウォレットの間でのキーの移行を簡単にしてくれるのです。

non-deterministic wallet
Figure 8. Type-0非決定性(ランダム)ウォレット: ランダムに生成されたキーのコレクション

Mnemonic Code Words

mnemonic codeは、決定性ウォレットのシードとして使われるランダムな数値を表す(エンコードする)英単語の配列です。この英単語配列は、シードを再生成するために使われ、このシードからウォレットと全てのキーを再生成します。mnemonic codeを伴った決定性ウォレットは、ウォレットの初期設定時にユーザに12から24個の英単語の配列を示します。この英単語配列はウォレットのバックアップであり、全てのキーを復活させるまたは再生成するのに使用できます。mnemonic codeによってユーザはウォレットのバックアップを取りやすくなります。なぜなら、ランダムな数字と比べてこの英単語配列は読みやすく、また正確に転写できるからです。

mnemonic codeは Bitcoin Improvement Proposal 39 ([bip0039]参照)で定義されており、現在草案段階にあります。BIP0039は草案段階提案であり、まだ標準ではないことに注意してください。特にどの英単語群を使うかについて異なる標準があり、 ElectrumウォレットはBIP0039を元にして別の英単語群を使っています。BIP0039は Trezorウォレットやいくつかの他のウォレットでも使われていますが、Electrumの実装とは相容れないものになっています。

BIP0039は以下のようにmnemoni codeとシードの生成を定義しています。

  1. 128bitから256bitのランダムな配列(entropy)を生成

  2. ランダムな配列のSHA256ハッシュの先頭4bitを取得し、ランダムな配列のチェックサムを生成

  3. このチェックサムをランダムな配列の最後に付加

  4. 2048個のあらかじめ決められた英単語の辞書のインデックスとして使うために、この配列を11bitずつの部分に分割

  5. mnemonic codeを表す12から24個の英単語を生成

[table_4-5]はエントロピーデータサイズとmnemoni codeの単語数の関係を示しています。

Table 5. Mnemonic codes: エントロピーと単語長
エントロピー (bits) チェックサム (bits) エントロピー+チェックサム 単語数

128

4

132

12

160

5

165

15

192

6

198

18

224

7

231

21

256

8

264

24

mnemonic codeは128から256bitになっており、PBKDF2というキー拡張関数を使うことでmnemonic codeからより長いシード(512bit)が生成されます。結果として出てくるシードは決定性ウォレットと全ての鍵を生成することに使われます。

に、mnemonic codeとそれらが作り出したシードのいくつかの例を示します。

Table 6. 128bitエントロピーから得たmnemonic codeと出力されたシード

エントロピーインプット (128 bits)

0c1e24e5917779d297e14d45f14e1a1a

Mnemonic (12 単語)

army van defense carry jealous true garbage claim echo media make crunch

シード (512 bits)

3338a6d2ee71c7f28eb5b882159634cd46a898463e9d2d0980f8e80dfbba5b0fa0291e5fb88 8a599b44b93187be6ee3ab5fd3ead7dd646341b2cdb8d08d13bf7

Table 7. 256bitエントロピーから得たmnemonic codeと出力されたシード

エントロピーインプット (256 bits)

2041546864449caff939d32d574753fe684d3c947c3346713dd8423e74abcf8c

Mnemonic (24 単語)

cake apple borrow silk endorse fitness top denial coil riot stay wolf luggage oxygen faint major edit measure invite love trap field dilemma oblige

シード (512 bits)

3972e432e99040f75ebe13a660110c3e29d131a2c808c7ee5f1631d0a977fcf473bee22 fce540af281bf7cdeade0dd2c1c795bd02f1e4049e205a0158906c343

階層的決定性ウォレット(BIP0032/BIP0044)

決定性ウォレットは1つの"シード"から多くのキーを生成しやすくするために開発されました。決定性ウォレットの最も進んだ形は、BIP0032で定義されている 階層的決定性ウォレット または HDウォレット です。階層的決定性ウォレットはツリー構造をなしているキーを含んでいて、このツリー構造は親キーが子キー群を作り、それぞれの子キーが孫キー群を作り出すような感じに無限に続いていきます。[Type2_wallet]図でツリー構造を説明しています。((("hierarchical deterministic wallets (HD wallets)","tree structure for"))

HD ウォレット
Figure 9. Type-2階層的決定性ウォレット: 1つのシードから生成されたキーツリー
Tip

もしBitcoinウォレットを実装するのであれば、BIP0032とBIP0044標準に従ったHDウォレットとして構築するべきです。

HDウォレットは、ランダムな(非決定性)キーに比べて2つの主な利点があります。1つ目としては、ツリー構造は付加的な組織的意味を表すのに使うことができる点です。例えば、サブキーの特定のブランチを支払いを受け取ることに使う場合や、異なるブランチをおつりを受け取ることに使う場合です。キーのブランチはまた企業内の状況に合わせて使われます。例えば、部署ごと、課ごと、特定の機能集団ごと、口座種類ごとにブランチを割り当てるような場合です。

2つ目の利点としては、ユーザが秘密鍵に触れることなく公開鍵を生成できる点です。それぞれのトランザクションごとに異なる公開鍵を発行することで、安全でないサーバや受信用にしかしていないサーバも使うことができるのです。公開鍵をあらかじめこれらのサーバに置いておいたり先に生成しておいたりする必要はありませんが、これらのサーバは資金を使うときに必要な秘密鍵を持つことはできません。

シードからのHDウォレット作成

HDウォレットは単一の ルートシード から作られ、ルートシードは128bit、256bit、512bitのランダムな数値です。HDウォレットにある他の全ては、決定性的にルートシードから導出され、このルートシードからHDウォレット全体を再生成できます。ルートシードだけを単にコピーすれば数千個、または数百万個のキーがあってもHDウォレットをバックアップ、リストア、エクスポートしやすくなるということです。ルートシードは前の節 [mnemonic_code_words] で書いたように_mnemonic word sequence_によってしばしば表現され、ルートシードを転写、保存をしやすくしています。

HDウォレットでマスターキーとマスターチェーンコードを生成するプロセスを[HDWalletFromSeed]を示します。

ルートシードからのHDウォレット
Figure 10. ルートシードからのマスターキーとチェーンコードの生成

ルートシードはHMAC-SHA512アルゴリズムに対するインプットであり、結果として出力されたハッシュは マスター秘密鍵 (m)と マスターチェーンコード を生成するために使われます。マスター秘密鍵(m)は対応したマスター公開鍵(M)を生成し、このときこの章の最初に見た標準的な楕円曲線上のスカラー倍算プロセス m * G が使われています。次の章で見るように、このチェーンコードは親キーから子キーを生成するプロセスの中でエントロピー(乱雑さ)を導入するために使われます。

子秘密鍵の生成(private child key derivation)

階層的決定性ウォレットは子キー生成(child key derivation) (CKD)関数を使って親キーから子キーを生成します。

child key derivation関数は一方向ハッシュ関数をベースにしており、以下の組み合わせによって成っています。

  • 子秘密鍵または子公開鍵(ECDSA非圧縮鍵)

  • チェーンコード(256bit)と呼ばれるシード

  • インデックス(32bit)

表面的には、このチェーンコードによって生成される子秘密鍵に十分なランダムさが含まれることになります。そして、チェーンコードなくインデックスだけではその他の子キーを生成することはできません。子キーを保持していたとしても、もしチェーンコードも持っていなければ他の子キーを見つけることはできないのです。最初のチェーンコードシード(ツリー構造のルート)はランダムなデータから作られ、次のチェーンコードはそれぞれの親チェーンコードから作られます。

以下のように、これらの3つの要素は組み合わされ子キーを生成するためにハッシュ化されます。

親公開鍵、チェーンコード、インデックスは組み合わされ、512bitハッシュを生成するためにHMAC-SHA512アルゴリズムでハッシュ化されます。出力されたハッシュは2つの部分に分けられます。右半分の256bitは子チェーンコードになり、左半分の256bitとインデックスは親秘密鍵となります。[CKDpriv]図は、インデックスが0のときの0番目の子を作り出す状況を表しています。

ChildPrivateDerivation
Figure 11. 子秘密鍵を生成するための親秘密鍵拡張

インデックスを変えることで、子0、子1、子2…と子を作り出していくことができ、それぞれの親キーは20億個の子キーを持つことができます。

このプロセスを繰り返すことで、ツリー構造を無限に下っていくことができ、それぞれの子が次々に親となり自身の子供を作っていきます。

生成された子キーの使用

それぞれ子秘密鍵たちは互いにどういう関係にあるかを知ることができません。HMAC-SHA512は一方向関数であるため、子秘密鍵は親秘密鍵を探すことができずまた、子秘密鍵は自分の兄弟を探すこともできません。もしn 番目 の子があったとしても、n-1番目の子やn+1番目の子、それ以外のいかなる子も探すことができません。ただ唯一親秘密鍵とチェーンコードだけが全ての子を作り出すことができます。もし子チェーンコードがないと、子秘密鍵は孫秘密鍵を作ることもできません。子秘密鍵と子チェーンコードの両方があって初めて孫秘密鍵を作り出すことができるのです。

子秘密鍵は何のために使われるのでしょうか?子秘密鍵は公開鍵とBitcoinアドレスを作ることに使われます。また、支払いに使われるトランザクションに署名をするときにも使われます。

Tip

子秘密鍵と、これに対応した子公開鍵およびBitcoinアドレスは、HDウォレットではない全てランダムに生成されたキーやアドレスと見分けがつきません。これらが生成列の一部であるということは自分自身からは分からないのです。これらが一度生成されると、"普通"のキーと全く同じように使えます。

拡張鍵

前に見たように、キー導出関数は子供を作ることに使われ、キー、チェーンコード、インデックスという3つのインプットに基づいています。本質的に必要なインプットはキーとチェーンコードで、これらを組み合わせたものは 拡張鍵 と呼ばれています。拡張鍵という言葉はまた、"拡張可能鍵"と呼ばれることもあります。なぜなら、このキーで子供を生成できるからです。

拡張鍵は単に256bitのキーと256bitのチェーンコードを単にくっつけて512bitにしたものです。拡張鍵には2つの種類があります。拡張秘密鍵は秘密鍵とチェーンコードの組み合わせで、子秘密鍵(そして、これから子公開鍵も)を生成することに使われます。拡張公開鍵は公開鍵とチェーンコードの組み合わせで、子公開鍵を生成することに使われます。詳細については[public_key_derivation]に記載しています。

拡張鍵をツリー構造のブランチのルートと考えてみましょう。ブランチのルートを使って、ブランチの残りを生成することができます。拡張秘密鍵は完全なブランチを作ることができますが、一方拡張公開鍵は公開鍵のブランチしか作ることができません。

Tip

拡張鍵は秘密鍵または公開鍵、チェーンコードで構成されています。拡張鍵は子供を生成し、ツリー構造の中に子供自身のブランチを作り出していくことができます。拡張鍵を共有することで、ブランチ全体を参照することができます。

拡張鍵はBase58Checkでエンコードされ、BIP0032互換ウォレットの間でエクスポートとインポートを簡単に行うことができます。拡張鍵のBase58Checkはプレフィックスとして"xprv"と"xpub"という特別なversion byteを使います。拡張鍵は512または513bitなので、前に見たBase58Checkエンコード文字列よりも長くなっています。

ここにBase58Checkでエンコードされた拡張秘密鍵の例を示します。

xprv9tyUQV64JT5qs3RSTJkXCWKMyUgoQp7F3hA1xzG6ZGu6u6Q9VMNjGr67Lctvy5P8oyaYAL9CAWrUE9i6GoNMKUga5biW6Hx4tws2six3b9c

以下はこの拡張秘密鍵に対応した拡張公開鍵です。これもBase58Checkでエンコードされています。

xpub67xpozcx8pe95XVuZLHXZeG6XWXHpGq6Qv5cmNfi7cS5mtjJ2tgypeQbBs2UAR6KECeeMVKZBPLrtJunSDMstweyLXhRgPxdp14sk9tJPW9
子公開鍵の生成(public child key derivation)

前に書いたように、階層的決定性ウォレットには秘密鍵を使うこと なく 親公開鍵から子公開鍵を作り出せるというとても有用な特徴を持っています。この性質から、子公開鍵を作る方法には2種類あることが分かります。子公開鍵を子秘密鍵から作るか、親公開鍵から作るか、です。

このため、拡張公開鍵は全ての 公開 鍵(そして公開鍵のみ)を生成することができます。

これにより、拡張公開鍵のコピーだけを持ち全く秘密鍵を持たないサーバやアプリケーションで、とても安全な公開鍵のみの生成ができるようになります。この仕組みを使うと、制限なく公開鍵とBitcoinアドレスを作り出すことができますが、これらのBitcoinアドレスに送られるお金を使うことができません。一方、より安全なサーバでは拡張秘密鍵を使ってさきほどのBitcoinに対応した秘密鍵を生成でき、トランザクションに署名することでお金を使うことができます。

この応用として、Eコマースを提供するwebサーバに拡張公開鍵を置く場合が考えられます。webサーバは公開鍵のderivation関数を使ってBitcoinアドレスをトランザクションごと(例えば顧客のショッピングカートごと)に新しいBitcoinアドレスを作ることができます。しかし、このwebサーバは盗難の攻撃を受けやすい秘密鍵を持っていません。HDウォレットを使わない場合、これを実行する唯一の方法は、切り離された安全なサーバで数千のBitcoinアドレスを生成し、あらかじめEコマースサーバ上にBitcoinアドレスを読み込んでおく方法です。ただしこの方法は、webサーバがBitcoinアドレスを"払い出せ"ないため、Bitcoinアドレスが枯渇しないように定期的なメンテナンスが必要となります。

別の応用としては、コールドストレージまたはハードウェアウォレットへの応用です。この応用では、拡張秘密鍵はペーパーウォレットまたはハードウェアデバイス(例えば、 Trezor hardware wallet)に保存されます。一方、拡張公開鍵はオンライン上に保持されます。ユーザは"受け取り用"のBitcoinアドレスを自由に作ることができますが、秘密鍵は安全にオフラインに保存されます。資金を使うには、ユーザはオフラインの署名用Bitcoinクライアント上で秘密鍵を使って行うか、ハードウェアウォレットデバイス上でトランザクションに署名するかをしなければいけません。[CKDpub]図は親公開鍵から子公開鍵を生成するメカニズムを説明しています。

子公開鍵生成
Figure 12. 子公開鍵を生成するための親公開鍵拡張
強化子公開鍵生成(hardened child key derivation)

拡張公開鍵から公開鍵のブランチを生成する方法はとても有用ですが、潜在的リスクも持っています。拡張公開鍵にさわれても子秘密鍵にはさわれません。しかし、拡張公開鍵はチェーンコードを含んでいるため、もし子秘密鍵が知られているまたは漏洩してしまった場合、このチェーンコードを使ってその他全ての子秘密鍵を導けてしまうのです。親チェーンコードを伴った1つの子秘密鍵の漏洩により、全ての子供の秘密鍵が明らかになってしまいます。悪いことに、親チェーンコードを伴った子秘密鍵は親秘密鍵を推測することに使うことができるのです。

このリスクへの解決策として、HDウォレットは hardened derivation(強化生成) と呼ばれるもう1つのderivation関数を使っています。この関数は、親公開鍵と子チェーンコードの間の関係を"壊す"ものです。hardened derivation関数は親公開鍵の代わりに親秘密鍵を使って子チェーンコードを生成します。これは親秘密鍵または兄弟秘密鍵が漏洩しないようなチェーンコードを使って親と子の間に"ファイヤーウォール"を作ります。hardened derivation関数は通常のchild key derivationとほとんど同じように見えますが、[CKDprime]図に示すように親公開鍵の代わりに親秘密鍵がハッシュ関数のインプットとして使われる点が異なります。

強化子秘密鍵生成
Figure 13. 子キーのhardened derivation: 親公開鍵の省略

子秘密鍵hardened derivation関数で出力される子秘密鍵とチェーンコードは、通常のderivation関数から得られる結果とは完全に異なります。結果として出てくる鍵の"ブランチ"は脆弱ではない拡張公開鍵を生成します。なぜなら、この拡張公開鍵に含まれているチェーンコードは、いかなる秘密鍵も攻撃できないようになっているからです。よって、hardened derivationは拡張公開鍵が使われる階層よりも上のツリーに行けないようにする"ギャップ"を作り出すのです。

もし拡張公開鍵の利便性を使い、しかもチェーンコードの漏洩リスクを回避したいのであれば、通常の親(親公開鍵)ではなく強化された親(親秘密鍵)から拡張公開鍵を生成すべきです。ベストプラクティスとしては、マスターキーの1階層目の子供を常にhardened derivationを通して生成されるようにしておくことがよいでしょう。

通常およびhardened derivationのインデックス

derivation関数で使われているインデックスは32bitの整数です。通常のderivation関数を通して得られたキーと強化されたderivation関数を通して得られたキーを簡単に区別するために、このインデックスを2つの範囲に分けておきます。0から231-1(0x0 から 0x7FFFFFFF)までのインデックスは通常のderivation関数に のみ に使われます。231から232-1(0x80000000 から 0xFFFFFFFF)はhardened derivation関数に のみ に使われます。よって、もしインデックスが231 より小さければ、子は通常のderivation関数から生成されたもの、一方もしインデックスが231と等しいかそれより上であれば、子は強化されたderivation関数から生成されたものです。

読んだり表示したりしやすいように、強化された子供に対するインデックスは0から始まるダッシュ付きの数字で表されます。最初の通常derivation関数による子キーは0と表示され、一方最初のhardened derivation関数による子キー(インデックス 0x80000000)は0'と表示されます。次に、二番目のhardenedキーはインデックス 0x80000001 を持ち 1' を表示されます。HDウォレットのインデックス i' を見たときは、これは231+iを表していると考えてください。

HDウォレットキー識別子(path)

HDウォレットにあるキーは、"path"命名規則を使って一意に指定されます。このpath命名規則は、ツリーの階層をスラッシュ(/)で区切って表します([table_4-8]参照)。マスター秘密鍵から得られた秘密鍵は"m"から始まります。マスター公開鍵から得られた公開鍵は"M"から始まります。このため、マスター秘密鍵の最初の子秘密鍵はm/0で、最初の子公開鍵はM/0、最初の子の二番目の孫はm/0/1などとなります。

キーの"先祖"をたどるには右から左に読み、それが得られたマスターキーに到達するまで読んでいきます。例えば、識別子 m/x/y/z はz番目の m/x/y の子キーを表し、また m/x/y は m/x のy番目の子キー、m/x は m のx番目の子キーを表します。

Table 8. HDウォレットのpath例
HD path 説明

m/0

マスター秘密鍵(m)から生成された最初(0)の子秘密鍵

m/0/0

最初の子供(m/0)の最初の孫秘密鍵

m/0'/0

最初の 強化された 子供(m/0')の最初のノーマルな孫秘密鍵

m/1/0

二番目の子供(m/1)の最初の孫秘密鍵

M/23/17/0/0

24番目の子供の、18番目の孫の、最初の曽孫の、最初の玄孫公開鍵

HDウォレットツリー構造をたどってみよう

HDウォレットツリー構造は非常に大きな柔軟性を持っています。それぞれの親拡張鍵は40億個の子供を持つことができます(20億個の通常の子供と20億個の強化された子供)。それぞれの子供は、もう1つ40億個の子供を持つことができ、これがどんどん続きます。ツリーは好きなだけ深くすることができ、制限なく世代を作っていくことができます。制限なく作っていくことはできますが、無限のツリーをたどろうとするととても時間がかかってしまします。特にHDウォレットを他のウォレットに移そうとするときとても大変です。

この複雑さを解決する2つのBitcoin Improvement Proposal (BIP)が提案されています。2つ目のBIP0043は、ツリー構造の"目的"が明確化された特別な識別子を最初の拡張された子インデックスとして使うということを提案しています。BIP0043に基づけば、HDウォレットはツリーの1階層目に1つだけブランチを持ち、その他の階層は目的が定義された構造や名前空間を持つインデックスを伴うはずです。例えば、1階層目にm/i'/ のブランチだけを持っているHDウォレットは特定の目的に沿ったウォレットとして使われることを想定されており、この目的はインデックス "i" で指定される目的になります。

このBIPを拡張することで、BIP0044はBIP0043の元での"目的"を表す数字 44' を定義しており、これは複数の口座を保持する目的を表すものです。ウォレットがBIP0044の構造に従っているかどうかは、1階層目が m/44'/だけになっていることから確認できます。

BIP0044では、以下のように5つの事前に定義された階層構造を提案しています。

m / purpose' / coin_type' / account' / change / address_index

最初の階層"purpose"は常に 44' になります。第2階層"coin_type"は暗号通貨コインの種類を表し、個々の通貨が第2階層以下に独自のサブツリーを持つような複数の通貨を扱えるHDウォレットを作ることができるようになっています。現在は3つの通貨が定義されており、Bitcoinは m/44'/0'、Bitcoin Testnetは m/44'/1'、Litecoinはm/44'/2'になっています。

第3階層"account"は、ユーザが複数の口座を使えるようにし、会計や組織的な目的で使えるようにしています。例えば、HDウォレットは2つのBitcoin "口座" m/44'/0'/0'm/44'/0'/1' を持つかもしれません。それぞれの口座はそれぞれ自身のサブツリーのルートになっています。

第4階層"charge"では、HDウォレットは2つのサブツリーを持つことができ、1つは受取用アドレスで1つはおつり用アドレスです。前の階層ではhardened derivationが使われたのですが、この階層では通常の生成が使われています。これは、拡張公開鍵を安全ではない環境で使うようにするためです。使うことのできないアドレスが第4階層の子供としてHDウォレットから生成され、第5階層"address_index"を作ります。例えば、最初の口座の三番目のbitcoin受け取り用アドレスはM/44'/0'/0'/0/2になります。[table_4-9]にいくつかの例を示します。

Table 9. BIP0044のHDウォレット構造例
HD path 説明

M/44'/0'/0'/0/2

最初のBitcoin口座に対する3番目の受信公開鍵

M/44'/0'/3'/1/14

4番目のBitcoin口座に対する15番目のおつり用公開鍵

m/44'/2'/0'/0/1

トランザクションに署名するための、Litecoinメイン口座の2番目の秘密鍵

Bitcoin Explorerを使ったHDウォレットの実験

[ch03_bitcoin_client]で紹介したBitcoin Explorerコマンドラインツールを使うと、異なった形式での表現と同時にBIP0032決定性キーを生成したり拡張したりする実験をしてみることができます。:

最近のキーとアドレスの進展

次の節では、暗号化秘密鍵、scriptとマルチシグネチャーアドレス、文字列指定のあるBitcoinアドレス(vanity address)、ペーパーウォレットなどキーとアドレスの最近の進展を見ていきます。

暗号化秘密鍵(BIP0038)

秘密鍵は極秘にしておかなければいけません。ただ秘密鍵の 機密性 は、実用上達成することが大変難しいことはよく知られていることです。というのは、機密性といつでも安全に使えることの両立が難しいためです。秘密鍵を秘密に保つことは、秘密鍵のバックアップを保持し紛失を避けるようにするとさらに難しくなります。ウォレットにあるパスワードで暗号化された秘密鍵は安全かもしれませんが、ウォレットはバックアップしておかなければいけないのです。例えばウォレットをアップグレードする、または別のウォレットに変えるといったときに、ユーザは鍵を1つのウォレットから別のウォレットに移動する必要が出てきます。秘密鍵のバックアップもまた紙([paper_wallets]参照)、またはUSBフラッシュメモリのような外部ストレージに保存されるかもしれません。しかし、バックアップそのものが盗まれたり紛失してしまったらどうなるでしょうか?これらの両立が難しいセキュリティ問題を解決するために、持ち出し可能で便利な暗号化秘密鍵が考案されました。この暗号化秘密鍵は、多くのウォレットやBitcoinクライアントに実装されており、Bitcoin Improvement Proposal 38またはBIP0038([bip0038]参照)で標準化されています。

BIP0038は、安全なバックアップメディアへの保存およびウォレット間の転送ができるように、またキーが晒される可能性のある状況にも対応できるように、パスフレーズで秘密鍵を暗号化しさらにBase58Checkでエンコードする標準規格を提案しています。この暗号化基準は、 Advanced Encryption Standard (AES)を採用しています。AESはNational Institute of Standards and Technology (NIST)によって開発され、商用または軍用アプリケーションの暗号化実装に広く使われているものです。

BIP0038暗号化スキームでは、インプットとしてBitcoin秘密鍵を取り、通常プレフィックス "5" を伴ったBase58Checkを使いWallet Import Format (WIF)にエンコードされます。さらにBIP0038暗号化スキームでは、アルファベットと数字が混ざった複雑な文字列で構成されるパスコードという長いパスワードを使います。BIP0038暗号化スキームの結果として、 6P から始まるBase58Checkでエンコードされた暗号化秘密鍵が得られます。もしキーが 6P から始まっていれば、それは暗号化されており、どのウォレットでも使えるWIF形式秘密鍵( 5 から始まる)に戻すためにはパスフレーズが必要だということが分かります。現在多くのウォレットはBIP0038暗号化秘密鍵を実装しており、キーをインポートし復号化するときにパスフレーズを求められるでしょう。非常に使いやすいブラウザベースの Bit Address (Wallet Details tab) などのサードパーティーのアプリケーションでもBIP0038キーを復号化することができます。

BIP0038暗号化キーの最も多い利用用途は、秘密鍵を紙にバックアップするときです。ユーザが協力なパスフレーズを選んでいる限りBIP0038暗号化秘密鍵を伴ったペーパーウォレットは非常に安全で、オフラインBitcoinストレージ("コールドウォレット"と呼ばれてもいます)を作るための最高の方法です。

パスフレーズを入れることでどのように復号化されたキーを得るかを理解するために bitaddress.org を使って[table_4-10]にある暗号化されたキーをテストしてみてください。

Table 10. BIP0038暗号化秘密鍵の例

秘密鍵 (WIF)

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

パスフレーズ

MyTestPassphrase

暗号化鍵 (BIP0038)

6PRTHL6mWa48xSopbU1cKrVjpKbBZxcLRRCdctLJ3z5yxE87MobKoXdTsJ

Pay-to-Script Hash (P2SH) とマルチシグネチャアドレス

知っての通り、初期から使われているBitcoinアドレスは“1”から始まり、秘密鍵から得られた公開鍵から得られます。誰もがbitcoinを“1”から始まるBitcoinアドレスに送れますが、このbitcoinは対応する秘密鍵署名と公開鍵ハッシュを使うことでのみ使用できます。

“3”から始まるBitcoinアドレスはpay-to-script hash (P2SH)アドレスであり、ときどき間違ってマルチシグネチャアドレスまたはマルチシグアドレスと呼ばれます。これらではBitcoinトランザクションの受取人を、公開鍵の所有者の代わりにscriptのハッシュを使って指定します。この特徴は、2012年にBitcoin Improvement Proposal 16または BIP0016 ([bip0016]参照)で導入され、幅広く採用されています。というのは、この特徴により、アドレス自体に機能を追加することができるようになったためです。“1”から始まるBitcoinアドレスに資金を“送る”トランザクション(((("BIP0016"))) pay-to-public-key-hash (P2PKH)とも呼ばれる)と違って、“3”で始まるBitcoinアドレスに送った資金のトランザクションでは、公開鍵ハッシュと秘密鍵署名以外の別のものも要求されます。要求されるものはBitcoinアドレスを作ったときにscriptの中で指定され、このBitcoinアドレスへの全てのインプットでそれらが要求されます。

pay-to-scriptハッシュアドレスはトランザクションscriptから生成され、トランザクションアウトプットを誰が使えるかということを定義しています(詳細については[p2sh]参照)。pay-to-scriptハッシュアドレスのエンコードはBitcoinアドレスを生成するときに使われる二重ハッシュ関数と同じ関数を使って行われ、公開鍵の代わりにscriptにのみ適用されます。

script hash = RIPEMD160(SHA256(script))

出力された"script ハッシュ"はversion prefix 5でBase58Checkエンコーディングされ、これは 3 から始まるものになっています。P2SHアドレスの例は、32M8ednmuyZ2zVbes4puqe44NZumgG92sM で、これはBitcoin Explorerコマンド script-encodesha256ripemd160base58check-encode ([libbitcoin]参照) を使って生成することができます。

$ echo dup hash160 [ 89abcdefabbaabbaabbaabbaabbaabbaabbaabba ] equalverify checksig > script
$ bx script-encode < script | bx sha256 | bx ripemd160 | bx base58check-encode --version 5
3F6i6kwkevjR7AsAd4te2YB2zZyASEm1HM
Tip

P2SHはマルチシグネチャの標準トランザクションというわけでありません。P2SHアドレスは ほぼ常に マルチシグネチャscriptを表しますが、他のトランザクションタイプのscriptを表す可能性もあるのです。

マルチシグネチャアドレスとP2SH

現在、P2SHの最も一般的な実装は、マルチシグネチャアドレスscriptです。この名前が示しているように、scriptは所有権を証明し資金を利用するために1つ以上の署名を要求します。Bitcoinマルチシグネチャは、N個のキーから作られるM個の署名(“threshold”とも呼ばれます)を要求し、これはM-of-N multi-sigと呼ばれています。ここで、MはNと等しいか小さい数です。例えば、[ch01_intro_what_is_bitcoin]に出てきたコーヒーショップのオーナーのボブは、彼のキーと彼の奥さんのキーから作られる1-of-2 シグネチャーを要求するマルチシグネチャーアドレスを使うことができ、このアドレスに紐づいたトランザクションアウトプットを使うには彼または奥さんのどちらか一方のキーで署名すればよいのです。Gopesh(ボブのウェブサイトを作ったウェブデザイナ)がビジネス用に2-of-3マルチシグネチャアドレスを持っていたとすると、少なくともビジネスパートナーの2人がトランザクションに署名しなければこのアドレスに紐づく資金を使うことができません。

[transactions]で、P2SH(とマルチシグネチャ)でのトランザクションの作成方法について説明します。

Vanity Addresses

文字列指定のあるBitcoinアドレス(Vanity Address)は人間が読むことができるメッセージを含んだBitcoinアドレスです。例えば、1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33 には、最初の四文字に"Love"という単語が含まれています。Vanity addressでは、Bitcoinアドレスに望んだパターンが出るまで何回も秘密鍵を生成しチェックしなければいけません。vanity addressを生成するいくつかの効率的な方法があるものの、基本的に秘密鍵をランダムに生成して公開鍵、Bitcoinアドレスを導出、vanityパターンに合っているかチェックするということを数十億回繰り返す必要があります。

一度望んだパターンを含むvanity addressが見つかると、それを導出した秘密鍵はvanity address以外のときと全く同じ方法でbitcoinの使用に使われます。vanity addressの安全性は他のアドレスと全く変わりません。vanity address以外のときと同じように、安全性はElliptic Curve Cryptography (ECC)とSecure Hash Algorithm (SHA)に依っています。ただvanityパターンに合っているかBitcoinアドレスを繰り返し生成してチェックしなければいけないため、vanity addressは作ることが難しいのです。

[ch01_intro_what_is_bitcoin]でフィリピンで子供たちのチャリティー活動を行っているEugeniaを紹介しました。Eugeniaがbitcoinによる寄付を募るためにvanity addressを作りたいと考えているとしましょう。Eugeniaは"1Kids"から始まるvanity addressを作ることにしました。どのようにvanity addressが作られ、Eugeniaのチャリティー活動のセキュリティにどう影響するか見ていきましょう。

vanity addressの生成

BitcoinアドレスはBase58文字で表されている数字であるため、"1Kids"のようなパターンの探索は、 1Kids11111111111111111111111111111 から 1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz の範囲に入るアドレスを探すことになります。近似的に5829(約1.4 * 1051)個のアドレスがこの範囲にあり、全て"1Kids"から始まっています。[table_4-11]は1Kidsから始まるアドレスの範囲を示しています。

Table 11. "1Kids"から始まるvanityアドレスの範囲

From

1Kids11111111111111111111111111111

1Kids11111111111111111111111111112

1Kids11111111111111111111111111113

To

1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz

特別なハードウェアではなく一般的なデスクトップパソコンでだいたい秒間100,000個のキーを探すことができると想定すると、どれくらいの時間で望んだパターンを含むBitcoinアドレスが現れるでしょうか([table_4-12]参照)。

Table 12. vanityパターン(1KidsCharity)の出現頻度と、デスクトップPCでの平均探索時間
長さ パターン 頻出度 探索時間

1

1K

58個に1個

< 1ミリ秒

2

1Ki

3,364個に1個

50ミリ秒

3

1Kid

195,000個に1個

< 2秒

4

1Kids

1100万個に1個

1分

5

1KidsC

6億5600万個に1個

1時間

6

1KidsCh

380億個に1個

2日

7

1KidsCha

2.2兆個に1個

3〜4ヶ月

8

1KidsChar

128兆個に1個

13〜18年

9

1KidsChari

7000兆個に1個

800年

10

1KidsCharit

40京個に1個

46,000年

11

1KidsCharity

2300京個に1個

250万年

このように、たとえ数千台のコンピュータが使えたとしてもEugeniaは"1KidsCharity"を含むvanity addressをすぐに作ることはできないでしょう。望むパターンの文字数が1個増えると見つける難しさは58倍になります。7文字より多いパターンだと通常特別に設計されたハードウェアが必要になり、複数の graphical processing units (GPUs)を積んだカスタムデスクトップが必要になります。特別に設計されたハードウェアとして、Bitcoinマイニングではもう利益を生まなくなったBitcoinマイニング"マシン"をvanity address生成に転用することがよくあります。GPUで組まれたものを使うと、汎用CPUに比べてはるかに速く計算できるのです。

vanity addressを見つけるもう1つの方法は、vanityマイナープールに依頼することです。例えば、Vanityプール です。これはGPUハードウェアを使ってvanity addressを見つけ出すことで儲けを出そうとしている集団です。少ない費用(0.01bitcoin、この執筆段階では約$5)でEugeniaは7文字のパターンを持つvanity addressの探索を外注に出すことができ、数時間後に結果を得ることができるようになります。

vanity addressの生成は、1つずつを確認していく総当たり方式です。1個1個ランダムにキーを作ってみて、望んだパターンに合っているか確認します。[vanity_miner_code] は"vanityマイナー"の例で、C++で書かれたvanity addressを探すプログラムです。例では[alt_libraries]で紹介した libbitcoinライブラリを使っています。

Example 8. vanityアドレスマイナー
Note

上記の例コードでは std::random_device を使用しており、これは裏で動作しているオペレーティングシステムによって提供されている暗号学的に安全な乱数生成器(CSRNG)の値を反映しています。LinuxのようなUNIX-likeなオペレーティングシステムの場合だとこれは /dev/urandom から乱数を取得しています。ここで使われている乱数生成器はデモ目的のものであり、十分なセキュリティを持ったように実装されていないので商用レベルのクオリティを持ったBitcoinキーを生成するには適切では ありません

この例コードでは、Cコンパイラとlibbitcoinライブラリを使ってコンパイルする必要があります。例コードを動作させるには、 vanity-miner++ 実行ファイルを引数なしで実行([vanity_miner_run]参照)し、実行すると"1kid"から始まるvanity addressを見つけ始めます。

Example 9. vanity-minerコード例のコンパイルと実行

例コードは3文字のパターン"kid"と合うBitcoinアドレスを探すのに数秒かかります。これはUnixコマンド time で計測したものです。ソースコードの中にある search パターンを変えて、4文字または5文字パターンにするとどれくらい処理に時間がかかるようになるかやってみてください!

Vanity addressのセキュリティ

vanity addressはセキュリティを増す方向に 下げる方向にも働きます。まさに諸刃の剣なのです。セキュリティを改善する方向に使われるとすると、特色があり見て分かりやすいアドレスであるため、悪意ある者があなたのお客さんをだまし自身のアドレスに支払いをさせることが難しくなります。他方セキュリティを下げる方向に使われるとすると、vanity addressでは誰でも似たアドレスを作ることができるため、似たアドレスを使ってお客さんをだますこともできてしまいます。

Eugeniaは、ランダムに生成されたBitcoinアドレス(例えば、1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy)を使って寄付を募ることもできます。また、1Kidsから始まるvanity addressを作って、区別しやすいアドレスにすることもできます。

どちらの場合でも、1つだけのアドレス(寄付者ごとに区分けされたアドレスではなく)を使うことのリスクの1つは、侵入者があなたのウェブサイトに侵入しBitcoinアドレスを侵入者のBitcoinアドレスに置き換えてしまうことです。もし寄付用のBitcoinアドレスをすでに多くの場所に貼り出しているとしたら、寄付者は寄付をする前に、前にウェブサイトやメール、チラシで見たBitcoinアドレスと同じであるかを確認するかもしれません。+1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy+のようなランダムなBitcoinアドレスを使っている場合、大方の人はおそらく最初の数文字、"1J7mdg" だけを見てBitcoinアドレスが合っているか判断するでしょう。vanity addressを使っていると、見た目が似ているBitcoinアドレスですりかえようとしている誰かが、最初の数文字だけ合っているBitcoinアドレスをすばやく作ることができてしまいます。

Table 13. ランダムなアドレスに先頭が一致するvanityアドレスの生成

オリジナルのランダムなアドレス

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

Vanity (4文字が一致)

1J7md1QqU4LpctBetHS2ZoyLV5d6dShhEy

Vanity (5文字が一致)

1J7mdgYqyNd4ya3UEcq31Q7sqRMXw2XZ6n

Vanity (6文字が一致)

1J7mdg5WxGENmwyJP9xuGhG5KRzu99BBCX

vanity addressはセキュリティを向上させるのでしょうか?Eugeniaがvanity address 1Kids33q44erFfpeXrmDSz7zEqG2FesZEN を作ったとすると、人々はvanityパターンの単語 と次の数文字 だけを見て正しいかどうかを見ます。例えば、"1Kids33"だけです。悪意ある者は最初の6文字か8文字だけ合っているvanity addressを作りますが、2文字多く一致しているvanity addressを生成する労力はEugeniaが4文字のvanity addressを作るために使った労力の3,364倍(58 × 58)です。本質的に、Eugeniaがつぎ込んだ(またはvanityプールにアドレス生成を頼んだ)労力が多ければ、この悪意ある者はさらに長いvanityパターンの作成を"強いられる"ことになります。もしEugeniaが8文字のvanity addressの生成を頼んでいたとすると、この悪意ある者は10文字のvanity addressを作らなければ いけません。これはパーソナルコンピュータ上では実行できずカスタマイズされたvanity-mining rigやvanityプールでさえ高価になってしまいます。特にもし詐欺を行うことで悪意ある者が得られる報酬がvanity addressの生成コストをカバーするほど高くないなら、Eugeniaには入手可能なアドレスでもこの攻撃者には入手不可能になります。

ペーパーウォレット

ペーパーウォレットは秘密鍵を紙の上に印刷したものです。よくペーパーウォレットは利便性のため秘密鍵に対応するBitcoinアドレスも含んでいます。しかし、Bitcoinアドレスは秘密鍵から導出できるためこれは必須ではありません。ペーパーウォレットはバックアップやオフラインBitcoinストレージを作るとても効率的な方法で、"コールドストレージ"とも呼ばれています。ペーパーウォレットはハードドライブの破損、盗難、また間違ってデータを削除してしまった場合などによるキーの紛失に対するバックアップとして機能します。もしペーパーウォレットのキーがオフラインで生成されてコンピュータ上に保存されていないとすると、ペーパーウォレットはハッカーやキーロガー、その他のオンライン上の脅威などに対する安全性が増す"コールドストレージ"として機能します。

ペーパーウォレットにはいろいろな形、大きさ、デザインがありますが、基本的に秘密鍵とBitcoinアドレスが紙に印刷されているだけのものです。[table_4-14]は最も簡単なペーパーウォレットを示しています。

Table 14. ペーパーウォレットの最もシンプルな形-Bitcoinアドレスと秘密鍵の印刷
公開アドレス 秘密鍵 (WIF)

1424C2F4bC9JidNjjTUZCbUxv6Sa1Mt62x

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

ペーパーウォレットは bitaddress.org でのクライアント側JavaScriptツールなどを使って簡単に作ることができます。このページには、インターネットに接続していなくてもペーパーウォレットが作れるコードが含まれています。それを使うために、HTMLページをローカルドライブ、または外部USBフラッシュドライブに保存してください。インターネットから切り離された状態で、ブラウザで保存したHTMLページを開いてみてください。より最適な状況を作り出すためには、CD-ROMで起動できるLinux OSのようなもっと簡素なオペレーティングシステムを使って起動し直してください。ツールを使って生成したどんなキーでもUSBケーブル(無線ではなく)で繋がれたローカルプリンタで印刷することができます。これにより、オンラインから切り離されたペーパーウォレットを作ることができます。これらのペーパーウォレットを耐火金庫に入れ、bitcoinをペーパーウォレット上のBitcoinアドレスに"送り"ます。ペーパーウォレットはとてもシンプルですが、極めて効果的な"コールドストレージ"です。

images/msbt_0414.png
Figure 14. bitaddress.orgから持ってきたシンプルなペーパーウォレットの例

簡単なペーパーウォレットの不利な点は、盗難される可能性があることです。盗難者は紙を盗む、またはペーパーウォレットの写真を撮ることでこれらのキーに紐づいているbitcoinをコントロールできるようになります。より洗練されたペーパーウォレットストレージはBIP0038暗号化秘密鍵を使う方法です。ペーパーウォレットに印刷されたキーは、所有者が記憶しているパスフレーズによって守られています。パスフレーズなしでは、暗号化されたキーを使うことはできません。暗号化秘密鍵を使ったペーパーウォレットは単なるパスフレーズで保護されたウォレットよりも安全です。というのは、オンラインに晒されることがないということと、金庫またはその他の安全なストレージから物理的に取り出さなければいけないということがあるためです。[paper_wallet_encrypted]図はbitaddress.org上で作られた暗号化秘密鍵(BIP0038)によるペーパーウォレットを示しています。

images/msbt_0415.png
Figure 15. bitaddress.org.から持ってきた暗号化されたペーパーウォレットの例。パスフレーズは"test"。
Warning

何回かペーパーウォレットに資金を預けることはできますが、ペーパーウォレットから引き出すときは全ての資金を一度に引き出すべきです。これは、資金のロックを解除して使用するプロセスでいくつかのウォレットはおつり用のアドレスを生成するかもしれないからです。さらに、もしトランザクションに署名するために使ったコンピュータに脆弱性があった場合、秘密鍵が漏洩してしまうかもしれません。ペーパーウォレットの残高全てを一度に使うことによってキーが漏洩してしまうリスクを減らすことになります。もし小さい額だけ必要なのであれば、同じトランザクション内で新しいペーパーウォレットに残りの資金を送ってください。

ペーパーウォレットは多くのデザイン、大きさがあり、また多くの異なる特徴をそれぞれ持っています。いくつかはギフトとして使われることを想定したものであり、クリスマスや新年など季節ごとのテーマを持ったデザインが施されています。また、他のいくつかは銀行の格納庫、または金庫に置くことを想定されたもの、削るスクラッチがついたものなどがあります。図 から 図 はいろいろな種類のペーパーウォレットを紹介しています。

images/msbt_0416.png
Figure 16. 折りたたみ部分に秘密鍵が置かれた bitcoinpaperwallet.com ペーパーウォレット例
images/msbt_0417.png
Figure 17. 秘密鍵部分が覆われて見えないようになっている bitcoinpaperwallet.com ペーパーウォレット

他のデザインでは、チケットのように切り離し可能になっていて、火事や洪水など自然災害があってもいいようにキーとBitcoinアドレスの複数のコピーが持てるようになっているものもあります。

images/msbt_0418.png
Figure 18. バックアップ用の"切り離し部分"にキーのコピーがあるペーパーウォレットの例