【HDウォレット】1つの文字列から無限に秘密鍵を復元する仕組み

NFT, Web3.0, ブロックチェーン

この記事は最終更新日から1年以上が経過しています。

はじめに

次世代ビジネス推進部FY21新卒の荻原です。

今回は「HDウォレット」について紹介します。先日、暗号資産の財布?〜ウォレットについて〜では暗号資産のウォレットについて、ブロックチェーンに接続する際に使用する秘密鍵というデータを保管するためのものだという紹介をしました。今回はその秘密鍵をどのように生成するかという部分に焦点を当てたお話です。

NFTを発行したり保有したりするためにも秘密鍵が必要不可欠となるので、自分自身NFTに関して勉強や開発をする中でHDウォレットに関する知識は特に重要だと感じています。

目次

  1. 秘密鍵とアドレスについて
  2. 秘密鍵の生成方法
  3. HDウォレットの仕組み
  4. 強化鍵
  5. ニーモニック
  6. パスフレーズ
  7. PythonでHDウォレットを作成してみる

1. 秘密鍵とアドレスについて

暗号資産の財布?〜ウォレットについて〜でも説明した秘密鍵とアドレスについて、復習と補足の内容です。

自分が保有している暗号資産の情報は分散型ネットワーク上のブロックチェーンに記録されています。そのため、取引をする際はそのネットワークに接続する必要があります。

接続には秘密鍵というキーが必要になり、それがパスワードのような役割をしてそのアカウントが保有する暗号資産の証明になります。

また、取引の際にはアドレスという文字列も用います。これは送金相手を指定するための、銀行の口座番号のようなものです。

秘密鍵とアドレスは次のような流れで生成されます。
52da14c5bf7e5ca4f5642fb956e8129d.png
シードは乱数です。秘密鍵、公開鍵、アドレスはシードを基にHash関数によって作られた文字列のデータです。Hash関数とは、ある値を別の値に不可逆的に変換する関数です。不可逆なので、アドレスから公開鍵、公開鍵から秘密鍵、秘密鍵からシードを求めることは実質不可能です。

このように、公開してアカウントの指定に用いるアドレスと、本人しか知り得ない秘密鍵を生成します。

アカウントはリスク分散や用途によって使い分けるために複数作成することがあります。今回はどのように秘密鍵やアドレスをたくさん作るかについて説明します。

2. 秘密鍵の生成方法

秘密鍵の生成方法には2種類あります。
4bc405a7c5987866284cd2c77b52f465.png
1つ目のランダムウォレットはその名の通りランダムに生成された複数のシードからそれぞれ秘密鍵を生成します。仕組みは単純ですが、保有するアカウントの数だけ秘密鍵を保管しておかなければいけません。

2つ目のHDウォレットが今回の本題です。HDウォレットは「Hierarchical Deterministic ウォレット」の略で、日本語で「階層的決定性ウォレット」です。一言で言うと、シードから生成された1つのマスターキーから階層(木)構造を取ってたくさんの秘密鍵を決定(生成)していきます。つまり、保管する必要があるのは大元の1つのシードのみです。
HDウォレットは「BIP32」というビットコインに関するルールで定められています。

3. HDウォレットの仕組み

HDウォレットの仕組みをもう少し詳しく説明していきます。

まずは、シードからマスターキーとなる秘密鍵を生成する方法です。
28b5bdf8cdc1bafa2c426cbb46b78441.png
シードをHMAC-SHA512というHash関数に通し、得た文字列を半分に分けます。そしてその前半をマスターキーとなる秘密鍵、後半をチェーンコードとします。チェーンコードはマスターキーの子となる秘密鍵を生成するために使用します。

では、シードから次々に秘密鍵を生成していく流れを詳しく見ていきます。
24979060b7223b11f007eb030b6804ef.jpg
シードから親秘密鍵、親チェーンコードを得るところまでは上記で説明した流れです。そしてそれらに0,1,2,3…というインデックスを合わせてHash関数(HMAC-SHA512)に通すことで子秘密鍵、子チェーンコードが得られます。インデックスを用いることによって子秘密鍵を複数作ることができるのです。
そしてさらに子秘密鍵と子チェーンコード、インデックスをHash関数に通すことで孫秘密鍵、孫チェーンコードが得られます。

では、これらの計算がどのように行われているのかを見ていきます。

f3e67e8fa7937206da781b76f5527c71.png
まず前提として、楕円曲線暗号の一種である楕円曲線DSAを用いて、秘密鍵 kに対して公開鍵は楕円上の点 K=kG=(x, y)です。ここで、 Gとは楕円上の点 G=(x', y')です。 kGGから kを逆算することは実質不可能です。つまり、秘密鍵から公開鍵は求められますが、公開鍵を知っていても秘密鍵を求めることはできないということです。

図の左下から見ていきます。
親秘密鍵 k_pから親公開鍵 k_pGを得ています。そして親公開鍵、親チェーンコード、インデックスを組み合わせてHMAC-SHA512に通すことで得た文字列を半分に分け、 I_RI_Lを得ます。 I_Rはチェーンコード c_iです。 I_Lと親秘密鍵 k_pから子秘密鍵 k_p + I_L = k_iを得ます。子公開鍵は子秘密鍵から楕円上の点の計算か、親公開鍵と I_Lからのどちらかで得ることができます。

親公開鍵のみ知っていれば子公開鍵を求めることができるということになりますが、これはHDウォレット特有だそうです。それによって秘密鍵を安全に保管したまま公開鍵を作成し、送金に必要なアドレスを次々に作成していくことができます。
343a417f64d6b16e070557bd8c7fc2e1.png

4. 強化鍵

前章で親公開鍵から子公開鍵を計算できる流れがHDウォレット特有だという説明をしましたが、このことによってセキュリティ的に少し問題が出てきます。

HDウォレット秘密鍵の計算の図からわかるのですが、親公開鍵と親チェーンコードは公開されていているものなので、子秘密鍵を知っている人は I_Lも得ることができ、親秘密鍵を求めることができてしまいます。下の階層の秘密鍵から上の階層の秘密鍵の逆算や、そこから兄弟鍵の計算ができてしまうということです。

これを防ぐために強化鍵という仕組みがあります。これは親公開鍵から子公開鍵を求められない代わりに子秘密鍵から親秘密鍵の計算もできないようにしたものです。詳細の説明は今回は割愛しますが、強化鍵を用いたHDウォレットの全体像はこのようになります。
8bd0996c5b09ffbd7c9139ef66dc8453.png

5. ニーモニック

シードは乱数なので人間が覚えるのは難しく、PCなどに保管しておかないと管理が難しいです。しかし、より安全に保管するにはオフラインで紙に書くなどの方法が良いとされています。

そこで、人間が管理しやすいようにシードは「ニーモニック」という12個や24個などの単語の羅列に変換されます。ニーモニックは「BIP39」で定義されています。

HDウォレットのハードウェアウォレットやウェブウォレットなどの初期設定の際も、ニーモニックを入力し、ウォレットにアカウントを復元します。MetaMaskでは「シークレットリカバリーフレーズ」と呼ばれています。
6bf883cd9ac2a64be3361b356504c10f-1.png
ニーモニックは日本語、英語、フランス語などの言語ごとに2048個のワードリストがあり、そこから組み合わせて作成されるそうです。

6. パスフレーズ

HDウォレットは1つのシードから複数のアカウントが作成できますが、アカウントを特定するにはシード以外にどの階層の何番目のアカウントかという情報も必要になります。

BIP32ではそのようにアドレスを特定するためのパスフレーズという形式があります。そしてこれを基に、種類の違う暗号資産でも1つのシードで管理できるようにしたものがBIP44で規定されています。
2a1e8296bffe29b7c7436271deae2664.png

次の図はこちらからの引用ですが、パスフレーズの意味がわかりやすいと思います。
derivation.png
マスターキーからアカウントが複数作られ、それぞれ入金用とお釣り用に分かれ、そこからアドレスが多数生成されています。

7. PythonでHDウォレットを作成してみる

PythonのHDWallet(公式ドキュメント)というライブラリを使ってHD ウォレットを作成してみます。

まずはニーモニックを生成します。英語と日本語でそれぞれ作成しました。

文字列がニーモニックかどうかを判定する関数があったので、適当な英単語を12個並べてみたり上記で作成した正しいニーモニックの語順のみ変えてみたりしましたが、当然ですが Falseとなりました。

続いて、作成したニーモニックからウォレットを復元します。下記ではパスの指定がないため、マスターキーが求められている状態です。以下のコードの最後は、省略していますがニーモニックや秘密鍵など、アカウントの様々な情報を出力しています。

子以降の秘密鍵の復元の計算は複雑なのですが、親の部分に関して3. HDウォレットの仕組みの内容が正しいのかを確認してみます。

digestはシードをHMAC-SHA512に通して得たハッシュ値です。 digest_hexはその結果のハッシュ値を16進数で表したものです。これを前半と後半に分けたものが ilirですが、それぞれ上記の hd_wallet_account_infoprivate_keychain_codeと一致しています。

最後にパスを指定してMATICの1番目の入金用アカウントの1番目のアドレスの情報を求めてみます。ニーモニックやシードはマスターキーと変わらないことがわかります。

まとめ

  • HDウォレットはシードという1つの乱数から無限にアドレスを作成する仕組みである。
  • 特定のアドレスはシードとパスフレーズによって復元される。
  • シードはニーモニックという単語の羅列で管理しやすいように表される。

最後まで読んでいただきありがとうございました。

参考