Magicode logo
Magicode
8

Pythonでイーサリアムで使われている暗号技術を学ぶ ① - 秘密鍵・楕円曲線・公開鍵・アドレス -

7 min read

イーサリアムにおける暗号技術

イーサリアムでは資金(イーサ)の所有権や取引の管理をするために暗号技術が用いられている。一方、イーサリアムプロトコルには用いられていないため、イーサリアムネットワーク上の全てのコミュニケーションや状態の遷移は誰でも読むことができ、透明性の高い状態で合意形成することができる。

イーサリアムでは2種類のアカウントがある。

  • トランザクション外部所有アカウント(EOA):イーサリアムネットワークのユーザーによって、ユーザーのために作成されるアカウント。取引の主体。
  • コントラクトアカウント:EOA・他のコントラクトアカウントから受け取ったイーサを用いて何らかの処理をするコードが入っているアカウント。これ自身が取引を行うわけではない。

秘密鍵はEOAは一対一で結びついていて、個人が確実にプライベートで安全な方法で管理しなければならず、外部に漏れてはならないパスワードみたいなものである。この秘密鍵を知られずに、イーサの所有権・イーサの取引の許可などを証明するために、暗号技術が用いられている。

登場人物は主に3種類で、

  • 秘密鍵
  • イーサリアムアドレス
  • デジタル署名

である。

今回の記事では最後のデジタル署名以外の部分について見ていこうと思う。

秘密鍵

11~22562^{256}から無作為に選べられた数字と同義の文字列である。ちなみに、宇宙の原子数は108010^{80}で、22562^{256}107710^{77}の規模なので、宇宙に存在する全ての原子にイーサリアムアカウントを与えるのにほぼ十分な秘密鍵が存在するらしい。

秘密鍵は確実に安全な方法で管理しなければならず、外部に漏れてはならない。デジタルではなく、アナログで管理する方法がよく薦められている。

この秘密鍵を用いて、イーサリアムアドレスやデジタル署名が生成される。そして、秘密鍵が知られることのないようなシステムで、それらの検証がなされる。そのシステムに暗号技術が用いられている。

Pythonではecdsaというライブラリが暗号周辺のさまざまな関数を提供しています。


Collecting ecdsa
Downloading ecdsa-0.17.0-py2.py3-none-any.whl (119 kB) [?25l |██▊ | 10 kB 32.8 MB/s eta 0:00:01 |█████▌ | 20 kB 19.0 MB/s eta 0:00:01 |████████▎ | 30 kB 10.3 MB/s eta 0:00:01
|███████████ | 40 kB 4.5 MB/s eta 0:00:01 |█████████████▊ | 51 kB 4.0 MB/s eta 0:00:01 |████████████████▌ | 61 kB 4.7 MB/s eta 0:00:01 |███████████████████▏ | 71 kB 4.8 MB/s eta 0:00:01 |██████████████████████ | 81 kB 3.9 MB/s eta 0:00:01 |████████████████████████▊ | 92 kB 4.4 MB/s eta 0:00:01 |███████████████████████████▌ | 102 kB 4.8 MB/s eta 0:00:01 |██████████████████████████████▏ | 112 kB 4.8 MB/s eta 0:00:01 |████████████████████████████████| 119 kB 4.8 MB/s [?25hRequirement already satisfied: six>=1.9.0 in /srv/conda/envs/notebook/lib/python3.7/site-packages (from ecdsa) (1.16.0)
Installing collected packages: ecdsa
Successfully installed ecdsa-0.17.0

Private key (hex): cabf51d9a6456500717471f18a3e7b6707bcaa5cad168dc4a35fc487ed6e7b39

256bitは4bitごとに16進数にエンコードされ、64桁で表示されます。


64

公開鍵・イーサリアムアドレス

イーサリアムアドレスは公に公開できるアカウント名で、公開鍵から生成される。

公開鍵

公開鍵は公にできるが、秘密鍵は公にしてはならない。ということは公開鍵から秘密鍵を推測できてはならない。一方、秘密鍵から公開鍵は生成されるため、生成関数として現代のコンピュータシステムでよく用いられている楕円曲線暗号(Elliptic curve cryptography)が採用されている。イーサリアムではsecp256k1が採用されている。

https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Secp256k1.png

関数の詳細は、xxを秘密鍵、yyを公開鍵、p=225623229282726241p = 2^{256}-2^{32}-2^9-2^8-2^7-2^6-2^4-1(巨大な素数ということ)としたときに、

y2modp=(x3+7)modpy^2\bmod p = (x^3+7) \bmod p

となる。

例えば、ppがデカすぎるので、p=17p=17としたときに、これを満たす点は(x,y)=(1,5)(x,y)=(1,5)である。


0

楕円曲線上では点同士の加算が可能、つまり乗算も可能である。 例えば、p1+p2=p3p_1+p_2=p_3において、p3p_3p1p_1p2p_2を通る直線と楕円曲線の交点p3(x,y)p'_3(x,y)に対してp3(x,y)p_3(x,-y)と定義される。さまざまな都合の良い性質が重なり、点同士の加算が可能となり、従って乗算も可能となっている。

ある生成点GGが与えられた時に、それを秘密鍵kkの値分だけかければ公開鍵KKが計算される。

K=kGK = k * G

つまり、秘密鍵kkから公開鍵KKを生成することはGG同士の加算をkk回繰り返すということである。

この順方向の計算は簡単だが、KKからkkを導き出すのはほぼ不可能であると言われている。


Public key (hex): 376ad4b80d4e6ae0390b4af3bd548a86f3cd6581d5d7220ec185ebff63115817873b071783065c2a8e44528f2e707fbbc041ef6ee10afea92e97c30bf709c131

x of Public key: 376ad4b80d4e6ae0390b4af3bd548a86f3cd6581d5d7220ec185ebff63115817 y of Public key: 873b071783065c2a8e44528f2e707fbbc041ef6ee10afea92e97c30bf709c131

イーサリアムでは圧縮されていない公開鍵のみを使用する。

Public keyのx,y座標はそれぞれ16進数で64桁の数字で表され、非圧縮点を意味する"04" prefixを前につけて、以下のようにシリアライズされる。

04+x+y04 + x + y

Public key (serialized): 04376ad4b80d4e6ae0390b4af3bd548a86f3cd6581d5d7220ec185ebff63115817873b071783065c2a8e44528f2e707fbbc041ef6ee10afea92e97c30bf709c131

イーサリアムアドレス

イーサリアムアドレスはKeccak-256というハッシュ関数を用いて公開鍵から生成される。

そもそもハッシュ関数とは「任意のサイズのデータを固定のサイズのデータにマップさせるために使用することができる任意の関数」であり、安全性が重視されるようなプラットフォームに有用な特性を持っているハッシュ関数は特に暗号ハッシュ関数と言われる。Keccak-256はその一つである。

アドレスを生成する際には公開鍵にprefixをつける必要はなく、そのままの16進数を入力すれば良い。


Collecting pysha3
Downloading pysha3-1.0.2.tar.gz (829 kB) [?25l |▍ | 10 kB 25.8 MB/s eta 0:00:01 |▉ | 20 kB 17.4 MB/s eta 0:00:01 |█▏ | 30 kB 10.3 MB/s eta 0:00:01 |█▋ | 40 kB 4.5 MB/s eta 0:00:01 |██ | 51 kB 4.0 MB/s eta 0:00:01 |██▍ | 61 kB 4.7 MB/s eta 0:00:01 |██▊ | 71 kB 4.9 MB/s eta 0:00:01 |███▏ | 81 kB 3.9 MB/s eta 0:00:01 |███▋ | 92 kB 4.4 MB/s eta 0:00:01 |████ | 102 kB 4.8 MB/s eta 0:00:01 |████▍ | 112 kB 4.8 MB/s eta 0:00:01 |████▊ | 122 kB 4.8 MB/s eta 0:00:01 |█████▏ | 133 kB 4.8 MB/s eta 0:00:01 |█████▌ | 143 kB 4.8 MB/s eta 0:00:01 |██████ | 153 kB 4.8 MB/s eta 0:00:01 |██████▎ | 163 kB 4.8 MB/s eta 0:00:01 |██████▊ | 174 kB 4.8 MB/s eta 0:00:01 |███████▏ | 184 kB 4.8 MB/s eta 0:00:01 |███████▌ | 194 kB 4.8 MB/s eta 0:00:01 |████████ | 204 kB 4.8 MB/s eta 0:00:01 |████████▎ | 215 kB 4.8 MB/s eta 0:00:01 |████████▊ | 225 kB 4.8 MB/s eta 0:00:01 |█████████ | 235 kB 4.8 MB/s eta 0:00:01 |█████████▌ | 245 kB 4.8 MB/s eta 0:00:01 |█████████▉ | 256 kB 4.8 MB/s eta 0:00:01 |██████████▎ | 266 kB 4.8 MB/s eta 0:00:01 |██████████▊ | 276 kB 4.8 MB/s eta 0:00:01 |███████████ | 286 kB 4.8 MB/s eta 0:00:01 |███████████▌ | 296 kB 4.8 MB/s eta 0:00:01 |███████████▉ | 307 kB 4.8 MB/s eta 0:00:01 |████████████▎ | 317 kB 4.8 MB/s eta 0:00:01 |████████████▋ | 327 kB 4.8 MB/s eta 0:00:01 |█████████████ | 337 kB 4.8 MB/s eta 0:00:01 |█████████████▍ | 348 kB 4.8 MB/s eta 0:00:01 |█████████████▉ | 358 kB 4.8 MB/s eta 0:00:01 |██████████████▎ | 368 kB 4.8 MB/s eta 0:00:01 |██████████████▋ | 378 kB 4.8 MB/s eta 0:00:01 |███████████████ | 389 kB 4.8 MB/s eta 0:00:01 |███████████████▍ | 399 kB 4.8 MB/s eta 0:00:01 |███████████████▉ | 409 kB 4.8 MB/s eta 0:00:01 |████████████████▏ | 419 kB 4.8 MB/s eta 0:00:01 |████████████████▋ | 430 kB 4.8 MB/s eta 0:00:01 |█████████████████ | 440 kB 4.8 MB/s eta 0:00:01 |█████████████████▍ | 450 kB 4.8 MB/s eta 0:00:01 |█████████████████▉ | 460 kB 4.8 MB/s eta 0:00:01 |██████████████████▏ | 471 kB 4.8 MB/s eta 0:00:01 |██████████████████▋ | 481 kB 4.8 MB/s eta 0:00:01 |███████████████████ | 491 kB 4.8 MB/s eta 0:00:01 |███████████████████▍ | 501 kB 4.8 MB/s eta 0:00:01 |███████████████████▊ | 512 kB 4.8 MB/s eta 0:00:01 |████████████████████▏ | 522 kB 4.8 MB/s eta 0:00:01 |████████████████████▌ | 532 kB 4.8 MB/s eta 0:00:01 |█████████████████████ | 542 kB 4.8 MB/s eta 0:00:01 |█████████████████████▍ | 552 kB 4.8 MB/s eta 0:00:01 |█████████████████████▊ | 563 kB 4.8 MB/s eta 0:00:01 |██████████████████████▏ | 573 kB 4.8 MB/s eta 0:00:01 |██████████████████████▌ | 583 kB 4.8 MB/s eta 0:00:01 |███████████████████████ | 593 kB 4.8 MB/s eta 0:00:01 |███████████████████████▎ | 604 kB 4.8 MB/s eta 0:00:01 |███████████████████████▊ | 614 kB 4.8 MB/s eta 0:00:01 |████████████████████████ | 624 kB 4.8 MB/s eta 0:00:01 |████████████████████████▌ | 634 kB 4.8 MB/s eta 0:00:01 |█████████████████████████ | 645 kB 4.8 MB/s eta 0:00:01 |█████████████████████████▎ | 655 kB 4.8 MB/s eta 0:00:01 |█████████████████████████▊ | 665 kB 4.8 MB/s eta 0:00:01 |██████████████████████████ | 675 kB 4.8 MB/s eta 0:00:01
|██████████████████████████▌ | 686 kB 4.8 MB/s eta 0:00:01 |██████████████████████████▉ | 696 kB 4.8 MB/s eta 0:00:01 |███████████████████████████▎ | 706 kB 4.8 MB/s eta 0:00:01 |███████████████████████████▋ | 716 kB 4.8 MB/s eta 0:00:01 |████████████████████████████ | 727 kB 4.8 MB/s eta 0:00:01 |████████████████████████████▌ | 737 kB 4.8 MB/s eta 0:00:01 |████████████████████████████▉ | 747 kB 4.8 MB/s eta 0:00:01 |█████████████████████████████▎ | 757 kB 4.8 MB/s eta 0:00:01 |█████████████████████████████▋ | 768 kB 4.8 MB/s eta 0:00:01 |██████████████████████████████ | 778 kB 4.8 MB/s eta 0:00:01 |██████████████████████████████▍ | 788 kB 4.8 MB/s eta 0:00:01 |██████████████████████████████▉ | 798 kB 4.8 MB/s eta 0:00:01 |███████████████████████████████▏| 808 kB 4.8 MB/s eta 0:00:01 |███████████████████████████████▋| 819 kB 4.8 MB/s eta 0:00:01 |████████████████████████████████| 829 kB 4.8 MB/s [?25h
Preparing metadata (setup.py) ... [?25l
-
 done [?25hBuilding wheels for collected packages: pysha3 Building wheel for pysha3 (setup.py) ... [?25l
-
 \
 |
 done [?25h Created wheel for pysha3: filename=pysha3-1.0.2-cp37-cp37m-linux_x86_64.whl size=135833 sha256=e070c4485b4abeee0e335cd59332c019b1f5d1ff866102ec0d1967bc39fac3d9 Stored in directory: /home/jovyan/.cache/pip/wheels/0d/9e/bc/789fa0986c1fef30cafcc29da4dd07bc17ecba3fab78e27ed6 Successfully built pysha3
Installing collected packages: pysha3
Successfully installed pysha3-1.0.2

'1d151ad434793918260514a465279d2d307afa1d271eeac45f7d29366b6f83fb'

アドレスには最後の20バイト(16進数では最後の40文字)を用いる。


'65279d2d307afa1d271eeac45f7d29366b6f83fb'

0x prefixをつけることもある。


'0x65279d2d307afa1d271eeac45f7d29366b6f83fb'

チェックサム・EIP-55

アドレスの入力ミス・誤読などを防ぐためにチェックサムという機能が必要である。イーサリアムの性質上、ビルトインチェックサム機能はないため、代替手段としてのアドレスのエンコーディング法が用いられている。最近よく用いられているのが、EIP-55(Ethereum Improvement Proposal 55)である。

これはアドレスの一部をある規則に基づいて大文字にするというエンコーディング法である。従来のアドレス識別では使われなかった大文字小文字を検証に用いることで、高精度でエラーを検出することができる。

次にエンコーディングの過程を順に見ていこう。

①アドレスをkeccak256に突っ込む


'1d151ad434793918260514a465279d2d307afa1d271eeac45f7d29366b6f83fb'

②アドレスとハッシュ値を並べて、ハッシュ値に対応する16進数が8以上の場合に、対応するアドレスの文字がアルファベットだったら大文字にする


'65279D2d307AfA1D271eeaC45f7d29366b6F83fB'

以上まとめると、


エラーの検出

それではEIP-55を用いてエラーの検出をしてみよう。試しに正しいEIPエンコーディング後のアドレスの一番最後の文字を1bitズレて入力してしまったと仮定する。


Correct Address: 65279d2d307afA1d271eEAC45F7D29366B6f83Fb Wrong Address: 65279d2d307afA1d271eEAC45F7D29366B6f83F7

間違っているアドレスを小文字にしてハッシュ値を求めると1bitの差が大きなハッシュの変化をもたらすことがわかります。


Correct Hash: 5a5e70d68ba17ef0f6b3f8a9eb5b0c602de4d895cda8a1d65aacac97b022c687 Wrong Hash: bf613466ac413d976fde9054be8c6ee2946757c8743c11481b04733afb868570

さらに間違ったハッシュ値からEIP-55で大文字化してみます。


Wrong Address: 65279d2d307afA1d271eEAC45F7D29366B6f83F7 Checksum: 65279d2d307afA1d271EEac45F7D29366b6f83F7

すると、入力した大文字のアドレスと計算されたチェックサムが違うことがわかります。これは、入力されたアドレスでEIPエンコーディングとの整合性がない変更があったことを意味し、何かがおかしいことを示しています。このようにして、EIP-55エンコーディングを用いて、アドレスが正しいものかどうか検証することができます。

まとめ

今回は秘密鍵・公開鍵・イーサリアムアドレスについてPythonを用いて検証しながら学んでみた。次は今回触れていないデジタル署名について学んでいきたいと思う。

何か違うことなどあればご指摘お願いしますm(_ _)m

参照:https://github.com/ethereumbook/ethereumbook

Discussion

コメントにはログインが必要です。