2026.06.09

EIP-7951でPasskey向けP-256署名を検証する

はじめに

Ethereum のウォレット UX は、長いあいだ「秘密鍵」や「シードフレーズ」をユーザーが直接管理する前提で作られてきました。

しかし、一般ユーザーにとって 12 words / 24 words のバックアップはかなり重い体験です。
失くせば資産を失う可能性があり、誰かに見られれば盗まれる可能性があります。

一方で、Web2 の認証では Passkey / WebAuthn が広がっています。
スマートフォンの Face ID / Touch ID、Android の生体認証、セキュリティキーなどを使い、「秘密鍵をユーザーに直接見せない」認証体験が当たり前になりつつあります。

では、Ethereum でも Passkey のような体験を使えるのでしょうか。

その文脈で出てくるのが EIP-7951: Precompile for secp256r1 Curve Support です。

この記事では、EIP-7951 の概要を説明したうえで、Node.js で P-256 署名を作り、EIP-7951 の precompile に渡す 160 bytes の入力データを作ってみます。

EIP-7951 とは

EIP-7951 は、Ethereum に secp256r1 / P-256 の ECDSA 署名検証 precompile を追加する提案です。

Ethereum で通常使われる ECDSA は secp256k1 です。
一方、Passkey / WebAuthn、Apple Secure Enclave、Android Keystore、FIDO2 デバイスなどで広く使われるのは secp256r1、別名 P-256 です。

つまり、ざっくり言うとこうです。

  • Ethereum 標準の署名:secp256k1
  • Passkey / WebAuthn 周辺でよく使われる署名:secp256r1 / P-256
  • EIP-7951:Ethereum 上で P-256 署名を安く検証できるようにする提案

EIP-7951 では、P256VERIFY という precompile が 0x100 に追加されます。

この precompile は、P-256 の ECDSA 署名を検証します。
入力は 160 bytes です。

32 bytes: message hash h
32 bytes: signature r
32 bytes: signature s
32 bytes: public key x-coordinate qx
32 bytes: public key y-coordinate qy

成功すると 32 bytes の 0x...01 を返し、失敗すると空の bytes を返します。

Solidity から見ると、呼び出し自体はかなり単純です。

bytes memory input = abi.encodePacked(h, r, s, qx, qy);
(bool success, bytes memory output) = address(0x100).staticcall(input);

これだけで P-256 署名の検証ができる、というのが EIP-7951 の大きなポイントです。

Node.js で P-256 署名を作る

import crypto from "node:crypto";

function toHex(buf) {
  return Buffer.from(buf).toString("hex");
}

function leftPad32(buf) {
  const b = Buffer.from(buf);

  if (b.length > 32) {
    throw new Error("too long");
  }

  return Buffer.concat([Buffer.alloc(32 - b.length), b]);
}

// 1. P-256 key pair を生成
const { privateKey, publicKey } = crypto.generateKeyPairSync("ec", {
  // prime256v1 は secp256r1 / P-256 と同じ曲線
  namedCurve: "prime256v1",
});

// 2. 署名対象メッセージ
const message = Buffer.from("hello EIP-7951");

// EIP-7951 の precompile に渡す h は 32 bytes の message hash
const hash = crypto.createHash("sha256").update(message).digest();

// 3. P-256 ECDSA 署名を作る
// dsaEncoding: "ieee-p1363" にすると signature が r || s の 64 bytes になる
const signature = crypto.sign("sha256", message, {
  key: privateKey,
  dsaEncoding: "ieee-p1363",
});

const r = signature.subarray(0, 32);
const s = signature.subarray(32, 64);

// 4. 公開鍵を JWK 形式で取り出す
const jwk = publicKey.export({ format: "jwk" });

const qx = leftPad32(Buffer.from(jwk.x, "base64url"));
const qy = leftPad32(Buffer.from(jwk.y, "base64url"));

console.log("h :", "0x" + toHex(hash));
console.log("r :", "0x" + toHex(r));
console.log("s :", "0x" + toHex(s));
console.log("qx:", "0x" + toHex(qx));
console.log("qy:", "0x" + toHex(qy));

// 6. EIP-7951 の P256VERIFY に渡す 160 bytes
const input = Buffer.concat([hash, r, s, qx, qy]);

console.log("input:", "0x" + toHex(input));
console.log("input length:", input.length);

上記を実行すると以下が出力されました。

h : 0x50f28603b6d1003f4030e372686927800a632462f1cdace7fedef3aa5e2e5121
r : 0xea102c06130ee2d20da4ea49a8296bfb7e57e41a10e1a79c30bf27e63ed62ad7
s : 0x1103347c750ee2991b5ce6aec3f1e8a0b119230e0fd8fdc734df40a992fe573a
qx: 0xf760b8d9577961ae4145f4eada0dbfbcb06ed34fe92639bcb74318fa01ce367f
qy: 0x4ba0dfd5ebceddb3d5e7beadba7d9cc3fb874983c1e73e066becf22e82d58b2f

Solidity から P256VERIFY を呼ぶ

pragma solidity ^0.8.24;

contract P256Verifier {
    address constant P256VERIFY = address(0x100);

    function verify(
        bytes32 h,
        bytes32 r,
        bytes32 s,
        bytes32 qx,
        bytes32 qy
    ) external view returns (bool) {
        bytes memory input = abi.encodePacked(h, r, s, qx, qy);

        (bool success, bytes memory output) = P256VERIFY.staticcall(input);

        return success
            && output.length == 32
            && uint256(bytes32(output)) == 1;
    }
}

node.js で作成したP256 署名を検証するとtrue になりました。

EIP-7951 でできること・できないこと

最後に、EIP-7951 の位置づけを整理します。

できること

  • P-256 / secp256r1 の ECDSA 署名を Ethereum 上で効率的に検証する
  • Passkey / WebAuthn / Secure Enclave / Android Keystore などの署名を使うための土台を作る
  • Account Abstraction と組み合わせて、デバイスネイティブなウォレット UX を作る
  • 複数デバイスや MFA 的な認証設計をスマートアカウントに組み込む

できないこと

  • Passkey 認証の意味検証を全部やってくれるわけではない
  • ecrecover のように Ethereum address を復元するわけではない
  • シードフレーズ管理の問題を単独で解決するわけではない
  • フィッシングや端末乗っ取りを自動的に防ぐわけではない
  • 署名の malleability 対策をアプリケーション側で一切考えなくてよくなるわけではない

特に malleability は注意点です。

EIP-7951 は P-256 署名検証の precompile を提供しますが、アプリケーション側で「署名の一意性」を必要とする場合は、追加チェックを検討する必要があります。

おわりに

EIP-7951 は、Ethereum に P-256 署名検証の precompile を追加する提案です。

これにより、Passkey / WebAuthn / Secure Enclave / Android Keystore / FIDO2 デバイスなど、既存のセキュアハードウェアや認証基盤と Ethereum をつなぎやすくなります。

EIP-7951 は Ethereum のウォレット UX を大きく変える可能性があります。

 

次世代システム研究室では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。インフラ設計、構築経験者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。

皆さんのご応募をお待ちしています。

参考文献

https://eips.ethereum.org/EIPS/eip-7951

https://tech.boostry.co.jp/entry/eip7951

  • Twitter
  • Facebook
  • はてなブックマークに追加

グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。

 
  • AI研究開発室
  • 大阪研究開発グループ

関連記事