FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

「Mastering Bitcoin」を読む (第4章 鍵、アドレス、ウォレット)

ビットコインとブロックチェーン:暗号通貨を支える技術

ビットコインとブロックチェーン:暗号通貨を支える技術

関連記事

これは著者が読んだ内容をまとめているだけなので、誤訳、理解不足により誤っている可能性があります!鵜呑みにしないようにお願いします。 文章の途中に挿入している画像は、 https://www.bitcoinbook.info/translations/ja/book.pdf に含まれている画像を使用させていただいています。

第03章 ビットコインクライアント

クライアントの使い方がメインなので省略。

第04章 鍵、アドレス、ウォレット

イントロダクション

ビットコインの所有権の基礎 : 「デジタル鍵」「ビットコインアドレス」「デジタル署名」

公開鍵暗号と暗号通貨

公開鍵、秘密鍵ビットコインアドレスの関係性

  • 秘密鍵はランダムに生成される値である。このアドレスをもとに公開鍵とビットコインアドレスが生成される。
  • 秘密鍵は所有権そのものを意味する。秘密鍵の値が漏れてしまうと、所有権が移されてしまうのと同義である。
    • また、秘密鍵を紛失しても、その秘密鍵に結びついている個人の資産に永久にアクセスできなくなってしまうことを意味する。

秘密鍵の生成

正確には、秘密鍵は1から n-1 までの任意の整数である。 n楕円曲線の異数であり、 2^{256} よりもわずかに小さな値である。

公開鍵

楕円曲線上のスカラー倍算を使って秘密鍵から生成する。これは不可逆の計算である。  K=k * G k秘密鍵 G は生成元と呼ばれる定点、 K は公開鍵となっている。

この公開鍵は一方向でのみ導出することができ、公開鍵から秘密鍵を導出することができない。 また、公開鍵から生成されるビットコインアドレスからも、秘密鍵が計算することができないため、ビットコインアドレスは誰と共有しても問題とならない。

楕円曲線暗号

ビットコイン楕円曲線暗号は、米国国立標準技術研究所(NIST)のsecp256k1という標準で定義された楕円曲線と定数を使っている。

$$y^{2} = \left(x^{3}+7\right) \text{over} (F_p)$$

または、

$$y^{2} \mod p = (x^{3}+7) \mod p$$

として定義される。ここで p 2^{256}-2^{32}-2^{9}-2^{8}-2^{7}-2^{6}-2^{4}-1 として定義される非常に大きな素数である。

例えば、以下のP楕円曲線上の点である。Pythonを使って確かめる。

P = (55066263022277343669578718895168534326250603453777594175500187360389116729240,
32670510020758816978083085130507043184471273380659243275938904335757337482424)
>>> P = [55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424]
>>> p = 2**256-2**32-2**9-2**8-2**7-2**6-2**4-1
>>> (P[0]**3+7 - P[1]**2)%p
0

※ 注記 : 楕円曲線暗号での計算の方法は、以下のサイトが分かりやすかった。

https://btcnews.jp/elliptc-curve-diy/

ビットコインアドレス

ビットコインアドレスとは、資金の受取人としてトランザクションに登場する。 数字と文字で構成された文字列。

  • ビットコインアドレスは、一方向のハッシュ化アルゴリズムを使って生成される。
    • 公開鍵からビットコインアドレスを生成するためには、SHA(Secure Hash Algorithm)とRIPEMD(RACE Integrity Primitives Evaluation Message Digest)が使われる。
    • その中で、SHA256とRIPEMD160が使用される。
    •  A= \text{RIPEMD160}(\text{SHA256}(K)) ただし、Kは公開鍵で、Aビットコインアドレス

さらに、ビットコインアドレスはBase58Checkと呼ばれる形にエンコードされる。 これは、可読性を上げ、転写時のエラーを防ぐ目的がある。

Base58 と Base58Checkエンコード

Base64エンコードは、もともとE-mailなどでバイナリを送信するために利用されているエンコードだが、Base58はよりコンパクトで、可読性、エラー防止などの機能をバランスよく提供している。 - 0(ゼロ)、O(大文字のオー)、l(小文字のエル)、I(大文字のアイ)、記号「+」や「/」を除去したものである。

さらに、書き間違いや転写間違いを防ぐために、Base58にチェックサムが組み込まれている。これがBase58Check。

checksum = SHA256(SHA256(prefix+data))

生成される32バイトのハッシュ値から最初の4バイトだけを取り出す。

鍵フォーマット

人々が鍵を間違えずに容易に読み転写できるようになることを第一の目的として用いられている。

秘密鍵フォーマット

秘密鍵を表現するために使われる3つの形式として、「16進数」「WIF」「圧縮WIF」が用いられる。

公開鍵フォーマット

公開鍵は楕円曲線上の点であり、 (x, y) の形式で表現されている。さらに、プレフィックス04が付加されている。

以下の秘密鍵から生成した公開鍵は、プレフィックス04が付加されて以下のように公開鍵となる。

x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

圧縮された公開鍵

公開鍵のサイズを圧縮するために、 (x, y) のうち x のみを保持するという方法がある。 これは、楕円曲線の上に載っている場合、 x が分かれば y も計算可能であるという原理に基づいている。 具体的には、 y^{2}\mod p = (x^{3}+7)\mod p を解くことで、 y を計算することができる。 これにより公開鍵のサイズを50%圧縮することができる。

しかし、 x から y を導こうとすると平方根の関係上正負の値が生成されてしまうため、 y 座標の符号は省略できず、保存する必要がある。 y 座標は偶数または奇数になり、これは正負に対応している。 したがって、y が偶数ならば02を、奇数ならば03を公開鍵の先頭に付与する。

以下が圧縮された公開鍵である。

K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

圧縮された公開鍵は一般的に使用されるようになっているが、圧縮された公開鍵に対応していないクライアントから転送されたトランザクションに対応するために、そのトランザクションを解釈する必要がある。

秘密鍵をウォレットからエクスポートする場合、これまでとは異なるWallet Import Formatを使用する。 圧縮された公開鍵が秘密鍵から作られていることを示しており、ビットコインアドレスが圧縮されているということをが示されている。

圧縮された秘密鍵

秘密鍵自体は圧縮されておらず、圧縮することができない。 このため、圧縮された秘密鍵というのは、実際には秘密鍵の最後に01を付加したものになっており、秘密鍵よりも1バイト長いものになっている。

圧縮された公開鍵を実装した新しいウォレットでは、秘密鍵はWIF圧縮形式(先頭がKまたはL)でエクスポートされるが、そうでなければWIF形式(先頭が5)でエクスポートされる。

鍵とビットコインアドレスのPythonでの実装

ウォレット

非決定性ウォレット

  • ランダムに生成された鍵を使用する。
  • この場合の欠点は、鍵がバックアップされておらずその鍵を紛失した場合には、すべての資産を紛失することになるというものである。
  • 非決定性ウォレットを使ってしまうと、トランザクションの度に別の鍵を作成し、そのすべてを管理しなければならなくなり、複雑になってしまう。
f:id:msyksphinz:20180203194231p:plain
Type-0非決定性(ランダム)ウォレット : ランダムに生成された鍵のコレクション

決定性ウォレット

  • 一つのシードから秘密鍵を生成し、同じシードから何度も別の秘密鍵を生成することができる。
  • このシードをバックアップしておけば、同じ秘密鍵を何度も再生成することができるという利点がある。

Mnemonic Code Words

  • 決定性ウォレットを生成する際にシードとして使った乱数を表現するための、英単語の列。

階層的決定性ウォレット (Hierarchical Deterministicウォレット : HDウォレット)

  • 親鍵が子鍵群を生成する。
  • ツリー構造であることにより、特定の機能ごと、口座ごとに「ブランチ」を割り当てることができる。
  • 秘密鍵にアクセスすることなく、ユーザが公開鍵を生成することができる。
f:id:msyksphinz:20180203194632p:plain
Type-2階層的決定性ウォレット : 1つのシードから生成された鍵ツリー

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

  • HDウォレットは、ルートシードから生成する。
  • ルートシードをHMAC-SHA512アルゴリズムに入力される。
    • 出力 : マスター秘密鍵 (m)、マスターchain code
    • さらに、マスター秘密鍵からマスター公開鍵が生成される。
    • マスターchain codeは、親鍵から子鍵を生成するプロセスの中で、エントロピー(乱雑さ)を導入するために使われる。
f:id:msyksphinz:20180203194756p:plain
ルートシードからのマスターカギとマスターchain codeの生成

秘密鍵の導出

  • 子鍵導出関数は、以下を組み合わせた一方向ハッシュ関数となっている。
    • 秘密鍵または親公開鍵 (ECDSA非圧縮鍵)
    • chain code(256ビット)と呼ばれるシード
    • インデックス (32ビット)
  • インデックスを変えることで、いくつもの子鍵を作ることができる。
f:id:msyksphinz:20180203195158p:plain
秘密鍵を生成するための親秘密鍵拡張

導出された子鍵の使用

拡張鍵

  • 子鍵生成に必要な、本質的なインプットは、鍵とchain codeであるため、これを組み合わせて拡張鍵とする。
  • 拡張秘密鍵 : 秘密鍵とchain codeを組み合わせて、子秘密鍵を生成する。さらに、子秘密鍵から子公開鍵を生成する。
  • 拡張公開鍵 : 公開鍵とchain codeを組み合わせて、子公開鍵を生成する。
    • この、秘密鍵を使うことなく、親公開鍵から、子公開鍵を生成できるという特徴を持っている。

子公開鍵の導出

  • 秘密鍵を持たないサーバにおいても、拡張公開鍵を持っていればいくらでも公開鍵を生成することができる。
  • Eコマースなどで、サーバ内で公開鍵が生成できるようになる。
f:id:msyksphinz:20180203195254p:plain
子公開鍵を導出するための親公開鍵拡張

強化子公開鍵

  • 万が一子秘密鍵が漏洩してしまった場合、すべての子秘密鍵を漏洩できる危険性がある。
  • これを防ぐために「強化導出」というものを使っている。
    • 親公開鍵と子chain codeの関係を壊す。
    • 親公開鍵の代わりに、親秘密鍵を使って子chain codeを導出する。
f:id:msyksphinz:20180203195406p:plain
子鍵強化導出 : 親公開鍵の省略

高度な鍵とアドレス

暗号化秘密鍵

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

  • 3から始まるビットコインアドレスはpay-to-script hash(P2SH)アドレス
    • トランザクションは、受取人を公開鍵の所有者の代わりにscriptおハッシュを使って指定する。
    • このアドレスに送った資金は、その所有権を示すために、1つの公開鍵ハッシュと1つの秘密鍵署名以上のものを提示することが必要になる。

Vanity Address

  • ビットコインアドレス内に、人間の読み取ることのできる文字列を挿入する。
    • これにより、慈善団体が寄付を募るためのコードに、任意の文字を入れることができる。
    • ただし、このようなVanity Addressを探し出すためには、一つずつ計算を行っていく必要があり、5~6文字以上は現実的ではない。

Vanity Addressのセキュリティ

  • このVanity Addressを悪意のある人間が誤用させるためには、Vanity Adressの文字列の部分を長さ+2くらいの長さの似たようなビットコインアドレスが必要になる。
    • 8文字のVanity Addressを含むアドレスを生成したらならば、悪意のある人間は+2の10文字を生成する必要がある。これは現実的ではない。
    • より長いVanity Addressを生成すると、悪意のある人間もそれに伴って多くの計算量が必要となる。

ペーパーウォレット

  • 秘密鍵を紙に印刷する。
    • コールドストレージとして機能する。
f:id:msyksphinz:20180203195807p:plain
bitaddress.org から持ってきたシンプルなペーパーウォレットの例