本記事では、Ethereumのメッセージ署名について考えられるユースケース、仕組みと実装方法を記載します。
1.メッセージ署名とは
dAppsを触っていると、以下のように、何らかのメッセージに対して署名を求められることが多くあります。
出典: https://note.com/takatangame/n/nfb0e690d7337
これはメッセージ署名と呼ばれます。
メッセージ署名は、トランザクションと同じく、MetaMask等のウォレットアプリから署名することで作成できます。
ただし、メッセージ署名自体はブロックチェーンに何かしら作用するわけではない(オフチェーンでのやり取り)ので、ガス代が発生したりはしません。
2.ユースケース
メッセージ署名はどのように活用されるのか、具体例を交えつつ考えてみます。
2.1.ユーザー認証
Ethereumアドレスとユーザーを紐づけることで、Ethereumアドレスを用いた認証が行えるようになります。
UIとしては、従来のユーザーID・パスワード入力ではなく、MetaMask等ウォレットでメッセージに署名することでログインできる、といった形です。
例えば、Web3知識の学習やプロジェクト発見のためのプラットフォームであるLayer3では、MetaMaskによるサインイン機能が提供されています。
以下の記事では細かな認証フローも検討されていました。
2.2.特定のアカウントへの認可
アカウントの残高取得などを、事前にメッセージ署名により同意を得てから行うケースも考えられます。
パブリックチェーンでは、Ethereumアドレスさえ分かれば同意を得ることなく各種情報を取得できますが、より信頼性が求められるdAppsであれば、このような認可へのメッセージ署名の活用も意味があるかもしれません。
2.3. 規約への同意
2.2.とも関連しますが、アプリケーションの利用規約等に、メッセージ署名によって同意する、というケースも考えられます。
オフチェーン上の操作であれば、あらゆる操作への同意に利用することができます。
具体例としては、エアドロップ(あるアドレスに、アプリケーションの利用など特定の評価基準に応じてトークンを配布すること)の規約への同意があったりします。
3.メッセージ署名の作成
メッセージ署名は以下の手順で作成されます。
- メッセージを準備し、ハッシュ化する(これをメッセージ1とする)
- メッセージ1に、「\x19Ethereum Signed Message:\n + {メッセージ1のバイト数}」というプレフィックスを追加する(これをメッセージ2とする)
- メッセージ2をさらにハッシュ化する(これをメッセージ3とする)
- メッセージ3に秘密鍵で署名する
実装においては、ethers.js等のライブラリを使えば簡単にできます。
const wallet = ethers.Wallet.createRandom();
const sig = wallet.signMessage("hello");
「\x19Ethereum Signed Message:\n + {メッセージ1のバイト数}」 は、ERC191で定められたフォーマットのひとつです。
このプレフィックスが、署名対象のデータがRLPエンコードされた結果トランザクションとして扱われてしまう可能性を排除し、メッセージとして解釈されることを保証しています。
4.メッセージ署名の検証
通常のトランザクションの署名と同じく、メッセージ署名は検証することができます。
メッセージ署名とメッセージがあれば、そのメッセージに署名者のアドレスを復元することができます。
こちらも、ethers.jsで簡単に記述できます。
const verifiedAddress = ethers.verifyMessage("hello",sig)
また、solidityで検証を行うこともできます。
詳細は下記を参照ください。
5.所感
トランザクションへの署名、メッセージ署名の違いを明確に理解する良い機会になったと思います。また、メッセージ署名のみならず、Ethereumにおける署名と検証を再確認することができました。