はじめに

初めまして。 『DApps開発入門』という本や色々記事を書いているかるでねです。

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!

今回は「EIP7951」についてまとめていきます。

secp256r1(P-256)楕円曲線のECDSA署名検証を6900ガスのプリコンパイルとしてEthereumに追加し、Apple Secure EnclaveやPasskeyを直接Ethereumに繋げるようにする仕組みを提案している提案です。

概要

EIP7951は、0x100番地にP256VERIFYというプリコンパイルを追加します。 このプリコンパイルは、secp256r1(別名P-256またはprime256v1)楕円曲線上でのECDSA署名検証を6900ガスで実行します。

P256VERIFY プリコンパイル概要

項目 内容
プリコンパイルアドレス 0x100
操作名 P256VERIFY
ガスコスト 6900 gas
入力サイズ 160バイト
出力サイズ 32バイト(成功時) / 0バイト(失敗時)
対象曲線 secp256r1(P-256 / prime256v1)

secp256r1とsecp256k1の違い

Ethereumが既存の署名検証(ecrecover)で使っているのはsecp256k1曲線です。 一方、secp256r1(P-256)はNISTが標準化した別の楕円曲線で、Apple Secure Enclave、Android Keystore、FIDO2/WebAuthnなどのセキュアハードウェアで広く採用されています。 この2つは互換性がなく、これまでEthereumはsecp256r1の署名をオンチェーンで効率的に検証できませんでした。

EIP7951はLayer 2ネットワークで先行実装されたRIP7212と同じインターフェースを持ちながら、RIP7212に含まれていた2つのセキュリティ上の問題を修正したEthereumメインネット向けの仕様です。

動機

セキュアハードウェアとの統合

現代のスマートフォンやセキュリティキーに内蔵されているセキュアハードウェアは、secp256r1を使って秘密鍵を管理しています。 代表的な例を以下に示します。

デバイス / 技術 利用場面
Apple Secure Enclave Face ID / Touch IDでの署名
Android Keystore 指紋・顔認証での署名
HSM(ハードウェアセキュリティモジュール) 企業の鍵管理
TEE(信頼実行環境) セキュアな計算環境
FIDO2 / WebAuthn パスキー認証

これらのデバイスはすべてsecp256r1で署名を生成します。 EthereumがP256VERIFYプリコンパイルを持つことで、ユーザーはiPhoneのFace IDやPasskeyを使ってEthereumのトランザクションを直接承認できるようになります。 専用のウォレットアプリをインストールしなくても、デバイスに組み込まれた認証機能がウォレットの署名に使えるため、一般ユーザーの参入障壁が大幅に下がります。

Layer 2での先行実装とEIP化の経緯

RIP7212 から EIP7951 への改善

secp256r1のプリコンパイルはすでにLayer 2ネットワークでRIP7212として実装・運用されていました。 しかし、RIP7212には2つの重大なセキュリティ上の欠陥が発見されました。 EIP7951はRIP7212と同じインターフェースを維持しながら、これらの脆弱性を修正してEthereumメインネットへの導入を提案するものです。

RIP7212の脆弱性

RIP7212には以下の2つの問題がありました。

1つ目は「無限遠点チェックの欠如」です。 署名検証中に復元した点R'が無限遠点(楕円曲線の加法における単位元)になるケースを確認していませんでした。 この問題は実装によって動作が異なる非決定論的な動作を引き起こし、コンセンサス障害の原因になる可能性がありました。

脆弱性① 無限遠点チェックの欠如

2つ目は「剰余比較の誤り」です。 復元したR'のx座標r'と署名のrを比較する際、r' == rという単純な等値比較を行っていました。 しかし、x座標が曲線の位数nを超えるケースでは、r' ≡ r (mod n)というモジュラー比較が必要です。 この誤りによって、数学的に正しいECDSA検証の結果と異なる結果を返す可能性がありました。

脆弱性② 剰余比較の誤り

無限遠点とは

楕円曲線の加法演算における特別な点で、通常の整数演算における「0」に相当します。 点Pと-Pを加算した結果がこの無限遠点になります。 ECDSA検証では、復元した点R'が無限遠点になった場合に検証失敗として扱う必要がありますが、RIP7212はこのチェックを省略していました。

仕様

プリコンパイルアドレスとガスコスト

P256VERIFYは0x100番地に配置され、ガスコストは6900ガスです。 既存のECRECOVERプリコンパイル(3000ガス)との実測ベンチマークを行った結果、secp256r1の検証処理がより重いことが確認されたため、6900ガスに設定されました。

エラーや検証失敗時も同じ6900ガスを消費します。 プリコンパイルはいかなる状況でもrevertしません。

secp256r1カーブパラメータ

secp256r1はNISTがSP 800-186で標準化した楕円曲線で、以下のパラメータで完全に定義されます。

パラメータ 値
基底体の法 p 0xffffffff00000001...ffffffff
曲線係数 a 0xffffffff00000001...fffffffc
曲線係数 b 0x5ac635d8aa3a93e7...2604b
基点Gのx座標 0x6b17d1f2e12c4247...8c296
基点Gのy座標 0x4fe342e2fe1a7f9b...51f5
部分群の位数 n 0xffffffff00000000...632551
補因子 h 0x1

曲線方程式は y² = x³ + ax + b (mod p) です。

エンコーディング

P256VERIFYの入力に登場するデータは、種類によってエンコード方法が異なります。

スカラーのエンコーディング

スカラー(署名コンポーネントr、sやメッセージハッシュh)は32バイトのビッグエンディアン符号なし整数として表現します。 フィールド要素とは異なり、整数値は部分群の位数n以下である必要はありません。

フィールド要素のエンコーディング

有限体Fpの要素(フィールド要素)は32バイトのビッグエンディアン符号なし整数として表現します。 エンコードする整数は法p未満でなければなりません。p以上の値は不正入力として扱います。

点(Point)のエンコーディング

secp256r1上の点は、x座標とy座標をそれぞれ32バイトでエンコードして連結した64バイトで表現します。

無限遠点のエンコーディング

secp256r1では座標(0, 0)はFp上の正当な点ではないため(曲線方程式を満たさない)、64バイトのゼロ列を無限遠点の表現として使います。

まとめると、hはスカラー、r・sはスカラー、qx・qyはフィールド要素(Fp上の値)として扱われます。 スカラーは位数n未満でなくてよい(バリデーションでチェック)、フィールド要素は法p未満が必須(チェック③)という違いがあります。

P256VERIFYの入出力

入力形式

P256VERIFYは160バイトの入力を受け取ります。入力は以下の順で連結されたバイト列です。

P256VERIFY 入力バイトレイアウト

フィールド サイズ 説明
メッセージハッシュ h 32バイト 検証対象のハッシュ値
署名コンポーネント r 32バイト ECDSA署名のrコンポーネント
署名コンポーネント s 32バイト ECDSA署名のsコンポーネント
公開鍵 x座標 qx 32バイト secp256r1公開鍵のx座標
公開鍵 y座標 qy 32バイト secp256r1公開鍵のy座標
出力形式

署名検証の成否に応じて異なるバイト列を返します。

結果 出力
検証成功 0x0000...0001(32バイト)
検証失敗 / 不正入力 空のバイト列(0バイト)
入力バリデーション

プリコンパイルは以下の5つのチェックを順番に実施します。いずれかが失敗した場合は、それ以降のチェックをスキップして空のバイト列を返します。

P256VERIFY 入力バリデーションフロー

  1. 入力長チェック

    • 条件: 入力が正確に160バイトであること
    • 意味: 5つのフィールド(各32バイト)が揃っているかを確認します
  2. 署名コンポーネント範囲チェック

    • 条件: 0 < r < n かつ 0 < s < n
    • 意味: rとsが曲線の位数nの範囲内にあるかを確認します。0やn以上の値は無効です
  3. 公開鍵座標範囲チェック

    • 条件: 0 ≤ qx < p かつ 0 ≤ qy < p
    • 意味: 公開鍵の座標が有限体Fpの範囲内にあるかを確認します。p以上の値は体の要素ではありません
  4. 点の曲線上チェック

    • 条件: qy² ≡ qx³ + a·qx + b (mod p)
    • 意味: 公開鍵(qx, qy)がsecp256r1の曲線方程式を満たすかを確認します。曲線上にない点は不正な公開鍵です
  5. 無限遠点チェック

    • 条件: (qx, qy) ≠ (0, 0)
    • 意味: 公開鍵が無限遠点でないかを確認します。無限遠点は公開鍵として使えません

検証アルゴリズム

バリデーションを通過した後、以下の手順で署名を検証します。

P256VERIFY 署名検証アルゴリズム

ステップ1: sの逆数を計算

text
s1 = s^(-1) (mod n)

署名時に使ったランダム数kを打ち消すための逆数s1を法nで計算します。

ステップ2: 署名時の点R'を復元

text
R' = (h * s1) * G + (r * s1) * (qx, qy)

メッセージハッシュh、逆数s1、公開鍵(qx, qy)、基点Gを組み合わせて、署名時に使われたランダム点R'を復元します。

Gは曲線の基点(Generator point)で、secp256r1のパラメータとして固定された点です。 楕円曲線暗号では 公開鍵 = 秘密鍵 × G の関係が成り立ちます。

ステップ3: 無限遠点チェック(RIP7212修正箇所)

text
if R' == 無限遠点:
    return(失敗)

復元したR'が無限遠点になった場合は検証失敗です。RIP7212ではこのチェックが省略されていたため、非決定論的な動作が発生する可能性がありました。

ステップ4: x座標のモジュラー比較(RIP7212修正箇所)

text
r' = R'.x

if r' ≡ r (mod n):
    return 0x0000...0001(成功)
else:
    return(失敗)

R'のx座標r'を取り出し、署名のrとモジュラー比較します。RIP7212では r' == r の単純な等値比較だったため、x座標が位数nを超えるエッジケースで誤った結果を返す可能性がありました。

補足

なぜRecoveryではなくVerificationを使うのか

Ethereumの既存プリコンパイルECRECOVER(secp256k1用)は、署名からアドレスを「復元(Recovery)」する設計です。 一方、P256VERIFYは既に分かっている公開鍵に対して署名が正しいかを「検証(Verification)」する設計です。

この設計選択には以下の理由があります。

セキュアハードウェアのほとんどは秘密鍵をデバイス外に取り出せない設計になっており、署名の検証インターフェースを提供します。 また、NISTのFIPS 186-5標準はVerificationを規定しており、Recoveryは定義されていません。 さらに、secp256r1ではRecoveryよりVerificationの方が効率的です。

RIP7212との互換性

EIP7951はRIP7212と以下の互換性を保っています。

項目 RIP7212 EIP7951
プリコンパイルアドレス 0x100 0x100
入力形式 160バイト 160バイト
出力形式 32バイト / 0バイト 32バイト / 0バイト
ガスコスト 3450 gas 6900 gas
無限遠点チェック なし あり
モジュラー比較 なし あり

RIP7212向けに書かれたコントラクトコードは、ガスコストの違いを除けば変更なしで動作します。 セキュリティ修正は正しく実装された呼び出し元には透過的です。修正が影響するのは、本来失敗すべきエッジケースのみです。

6900ガスというコストはECRECOVER(3000ガス)とのベンチマーク比較を根拠としており、実装ベンチマークの正確性を優先した結果です。 そのため、RIP7212の3450ガスとの互換性は意図的に維持していません。

セキュリティ考慮事項

署名の可鍛性(Malleability)

secp256k1の署名はEIP2でnon-malleable(可鍛性なし)が要求されていますが、secp256r1はNIST FIPS 186-5標準においてnon-malleabilityを要求していません。

EIP2はEthereumの初期のプロトコル改善提案です。secp256k1の署名におけるs値の範囲を制限し、署名の可鍛性を防いでいます。

署名の可鍛性とは

同一のメッセージと秘密鍵から、異なるバイト列を持つ「別の有効な署名」を作り出せる性質を指します。 ビットコインやEthereumの初期には、この性質を悪用したトランザクションIDの改ざんが問題になりました。 Ethereumはsecp256k1のs値を上限以下に制限することで対策しています。

secp256r1の署名では、同一メッセージに対して複数の有効な署名が存在する可能性があります。 non-malleabilityが必要なアプリケーション(例えば署名をIDとして使う場合)は、アプリケーション層で追加のチェックを実装してください。

定数時間アルゴリズムの非必須化

EIP7951は、プリコンパイルの実装が定数時間アルゴリズムを使うことを要求しません。 サイドチャネル攻撃(処理時間の差異から秘密鍵を推測する攻撃)への耐性はプリコンパイルレベルでは保証されません。 秘密鍵の保護はデバイスのセキュアハードウェアが担う想定です。

暗号強度

secp256r1は約128ビットのセキュリティ強度を提供します。 Ethereumで既に使われているsecp256k1と同等の強度であり、曲線パラメータはNIST SP 800-186で標準化されています。 また、RIP7212としてのL2デプロイを通じて広範な暗号解析の実績があります。

互換性

EIP7951が有効化されても既存のコントラクトには影響しません。 0x100番地にコントラクトをデプロイしているケースは現実的に存在しないため、互換性上の問題は発生しません。

Layer 2でRIP7212を使って開発したコントラクトは、ガスコストの違い(3450→6900ガス)を除いてそのまま動作します。

参考実装

secp256r1はハードウェアセキュリティデバイスや標準ライブラリで広く採用されているため、EIP7951は専用のリファレンス実装を提供していません。 実装の参考が必要な場合は、NISTのFIPS 186-5(Digital Signature Standard)を参照してください。

引用

最後に

いかがだったでしょうか?

今回は「EIP7951」についてまとめてきました。

EIP7951は、Apple Secure EnclaveやPasskey、FIDO2デバイスで使われるsecp256r1楕円曲線の署名検証を、6900ガスのプリコンパイルとしてEthereumに追加する提案です。 Layer 2で先行実装されたRIP7212の2つのセキュリティ問題(無限遠点チェックの欠如・モジュラー比較の誤り)を修正しながら、既存の実装との完全なインターフェース互換性を保っています。 このプリコンパイルにより、iPhoneやAndroidの生体認証をEthereumの署名に直接使えるアカウント抽象化パターンが現実的なコストで実現でき、一般ユーザーの参入障壁が大きく下がることが期待されます。

他でも色々記事を書いているのでぜひよろしければ読んでいってください!

もし記事に間違いなどがあれば、コメントやTwitterのDMなどで教えていただけると嬉しいです。 これからも学んだことを記事にしていきますので、よろしければフォローお願いします!