# [ERC7522] OIDCとZKを組み合わせてスマートアカウントを保護する仕組みを理解しよう！

> ERC4337は、スマートアカウントの柔軟な拡張を可能にするAccount Abstraction（アカウント抽象化）の仕組みを利用することで、従来の秘密鍵だけに依存しない認証やリカバリー方法を実現します。

- URL: https://cardene.dev/articles/d89b7032b47d72b280cd
- 公開日: 2025-12-10
- 更新日: 2025-12-10
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、OIDCのIDトークンとZK技術を使ってスマートアカウントの本人確認を安全に行う仕組みを提案している**ERC7522**についてまとめていきます！

:::message
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

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

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

## 概要

**ERC4337**は、スマートアカウントの柔軟な拡張を可能にする**Account Abstraction**（アカウント抽象化）の仕組みを利用することで、従来の秘密鍵だけに依存しない認証やリカバリー方法を実現します。
ここに **OIDC（OpenID Connect）** を組み合わせることで、Googleや各種Webサービスで一般的に使われている「**ソーシャルログイン**」に近い体験をWeb3上でも安全に使えるようになります。

OIDCは現在もっとも広く使われている認証プロトコルで、スマートアカウントとつながることで利便性が大きく向上します。
しかし、そのためにはOIDCの仕組みをブロックチェーン上で安全に検証できなければなりません。
**ERC4337**はこの課題に向けて、ゼロ知識証明（ZK）を用いたOIDC検証モデルを提供します。

ユーザーは、自分のスマートアカウントにOIDCのアイデンティティを紐づけ、オンチェーンでその正当性を検証できます。
これにより、ユーザー操作（UserOperation）の検証時にOIDCを利用することが可能になります。
また、ZKを使うためWeb2のアイデンティティとWeb3アドレスの関連性が外部に漏れず、プライバシーが保護されます。

特に、ZK検証は計算コストが高いため、アカウントのリカバリーにOIDCをガーディアンとして使うといった用途が想定されています。
複数のOIDCアカウント（例えばGoogleとFacebook）を設定することで、特定サービスに依存した中央集権的リスクを避ける設計も可能です。

## 動機

#### OIDCとスマートアカウントを結びつける難しさ

OIDCが発行する `IdToken` は、通常JWT（JSON Web Token）という形式で構成され、以下の3つの要素から成り立っています。

| 項目          | 説明                     |
| ----------- | ---------------------- |
| ヘッダー        | 署名方式やトークンの種類を示す部分      |
| クレーム（Claim） | ユーザー ID などの情報が含まれる本体部分 |
| 署名          | 発行者（IdP）が秘密鍵で署名した部分    |

署名は、発行者が公開しているRSA公開鍵で検証でき、ヘッダーとクレームをまとめた内容のハッシュに対して行われています。
Web2では一般的な仕組みですが、これをそのままオンチェーンで検証するには計算コストが高すぎるため、直接検証するのは現実的ではありません。

#### 従来の解決策：MPCの限界

一般的なアプローチとして**MPC**（Multi-Party Computation：複数の参加者で秘密計算を行う技術）が用いられてきました。
しかし、この方法には以下の問題があります。

- **中央集権的な依存が生じること**
    MPCの署名生成には第三者サービスが関与するため、そのサービスが停止した場合や特定ベンダーに依存するリスクがあります。
- **プライバシーの懸念**
    Web2のアカウント（例：Googleのアカウント）とWeb3のアドレスのつながりが、暗号的に守られないケースがあります。

#### ZKによる検証がもたらす改善

ZK（ゼロ知識証明）を利用することで、これらの課題をまとめて解決できます。

* **プライバシーの保護**
    Web2アカウントとWeb3アドレスの関係を証明しつつ、その内容自体は外部に漏れません。
* **完全な分散性**
    ZK証明の生成はクライアント側で完結し、第三者の署名生成サービスが不要になります。
* **コスト削減につながるアグリゲーション**
    ZK証明をまとめて扱う仕組みにより、大量の検証をより安価に実行できます。

#### ERC4337でのOIDC ZK検証モデルの役割

**ERC4337**は、ZKを使ったOIDC検証をアカウントの検証プロセスに組み込むモデルを定義します。
さらに、異なるZK実装間で共通して使える最小限のインターフェースやZKの入力形式を定義し、開発者が統一的に扱えるようにします。

これにより、ユーザーは自分のスマートアカウントにOIDCのアイデンティティを紐づけられ、検証の時にOIDCの正当性をZKを通してオンチェーンで確認できます。

#### 想定される利用シナリオ

ZK検証にはガスコストがかかるため、特にアカウントリカバリーのガーディアンとして利用するケースが想定されています。
例えば、秘密鍵が失われた場合、あらかじめ登録したOIDCアカウントを使って本人であることを証明し、アカウントを取り戻すことができます。

さらに、複数のOIDCアカウントを登録しておけば、特定の認証提供者に依存する中央集権的なリスクを抑えることができます。

## 仕様

### 定義

OIDC ZK認証に関わる主要コンポーネントは以下です。

| 用語   | 説明       |
| ----------- | --------------- |
| Identity Provider（IDP）  | ユーザーを認証し、署名付き ID Token を発行するサービス。       |
| User   | ID Tokenの取得とZK証明生成を行うクライアント。         |
| ZK Aggregator | 複数ユーザーのZ 証明を集約するオフチェーン処理サービス。   |
| OpenIdZkVerifier | ZK証明をオンチェーンで検証するコントラクト。 |
| EntryPoint / Aggregator / AA Account | **ERC4337**で定義されているアカウント抽象化の実行コンポーネント。 |

### 処理フロー

OIDC × ZK × ERC4337の全体の認証フローは以下の図で示されます。

![image.png](/images/qiita-b350f27a-5a1c-41f9-86e6-e7fdf6b0891c.webp)
https://eips.ethereum.org/EIPS/eip-7522

1. **User**が**IDP**に対して認証を行う。
2. **IDP**が署名付き**ID Token（JWT）**を**User**に返す。
3. **User**がこのJWTを元にZK証明を生成する。
4. **User**がZK証明を**ZK Aggregator**に送信し、集約を依頼する。
5. **ZK Aggregator**が複数の証明をまとめ、集約済み証明を**User**に返す。
6. **User**が**UserOperation（ERC4337）**にZK証明を添えて **EntryPoint**へ送る。
7. **EntryPoint**が証明の妥当性を検証する。
   * 単体証明 → `verify`
   * 集約済み証明 → `verifyAggregated`
8. 検証が通れば**AA Account**が**UserOperation**を実行する。

### Interface

```js
struct OpenIdZkProofPublicInput {
    bytes32 jwtHeaderAndPayloadHash;
    bytes32 userIdHash;
    uint256 expirationTimestamp;
    bytes jwtSignature;
}

interface IOpenIdZkVerifier {
    // @notice get verification key of the open id authenticator
    function getVerificationKeyOfIdp() external view returns(bytes memory);
 
    // @notice get id hash of account
    function getIdHash(address account) external view returns(bytes32);

    // @notice the function verifies the proof and given a user op
    // @params op: the user operation defined by ERC-4337
    //         input: the zk proof input with JWT info to prove
    //         proof: the generated ZK proof for input and op
    function verify(
        UserOp memory op,
        OpenIdZkProofPublicInput input,
        bytes memory proof
    ) external;

    // @notice the function verifies the aggregated proof give a list of user ops
    // @params ops: a list of user operations defined by ERC-4337
    //         inputs: a list of zk proof input with JWT info to prove
    //         aggregatedProof: the aggregated ZK proof for inputs and ops
    function verifyAggregated(
        UserOp[] memory ops,
        OpenIdZkProofPublicInput[] memory inputs,
        bytes memory aggregatedProof
    ) external;
}
```

#### 構造体

##### `OpenIdZkProofPublicInput`

```javascript
struct OpenIdZkProofPublicInput {
    bytes32 jwtHeaderAndPayloadHash;
    bytes32 userIdHash;
    uint256 expirationTimestamp;
    bytes jwtSignature;
}
```

OIDCのJWT情報をZK証明に渡すための入力データを保持する構造体。
JWTの改ざん検知のためのハッシュ値、ユーザーIDのハッシュ、JWTの期限、署名情報をまとめたものです。
これにより、ユーザーの本来のWeb2情報をオンチェーンで公開せずに、ZK回路内で安全に検証できます。

**パラメータ**

* `jwtHeaderAndPayloadHash`
  * JWTのヘッダーとペイロードを結合してハッシュ化した値。
* `userIdHash`
  * OIDCのユーザーIDをハッシュ化した値（プライバシー保護のため元の ID は公開されない）。
* `expirationTimestamp`
  * JWTが有効な期限を示す値。
* `jwtSignature`
  * Identity ProviderがJWTに対して行った署名データ。

#### 関数

##### `getVerificationKeyOfIdp`

```javascript
function getVerificationKeyOfIdp() external view returns(bytes memory);
```

Identity Providerの検証キーを取得する関数。
ZK回路内でJWTの署名検証を行うために必要な検証キーを返します。
ユーザーが利用した Identity Providerを正しく識別し、その署名が正当な鍵で行われたものであるかを確認するために使われます。

**戻り値**

* 検証キー
  * ZK回路が署名検証で使用するキー。

##### `getIdHash`

```javascript
function getIdHash(address account) external view returns(bytes32);
```

指定アカウントに紐づくユーザーIDのハッシュを返す関数。
AAアカウントがどの IDP ユーザーと紐づいているかを確認するために使われますが、実際のユーザー ID は公開されず、安全性が保たれた形で管理されます。

**引数**

* `account`
  * 対象となるアカウントアドレス。

**戻り値**

* IDハッシュ
  * 登録されたユーザーIDのハッシュ値。

##### `verify`

```javascript
function verify(
    UserOp memory op,
    OpenIdZkProofPublicInput input,
    bytes memory proof
) external;
```

単一の**UserOperation**に対してZK証明を検証する関数。
ZK証明の内容と、JWTに含まれる署名・期限・ユーザー ID の整合性を確認します。
**UserOperation**がユーザー本人によるものかをZK的に判断するために使われます。

**引数**

* `op`
  * **UserOperation**の情報。
* `input`
  * ZK証明に使うJWT情報。
* `proof`
  * ZK証明データ。

##### `verifyAggregated`

```javascript
function verifyAggregated(
    UserOp[] memory ops,
    OpenIdZkProofPublicInput[] memory inputs,
    bytes memory aggregatedProof
) external;
```

複数の**UserOperation**に対応するZK証明をまとめて検証する関数。
**ZK Aggregator**によって生成された集約済み証明を用いて、複数の**UserOperation**を一度に検証します。
処理効率を大幅に改善でき、ガスコストの削減にも貢献します。

**引数**

* `ops`
  * 検証対象の複数**UserOperation**。
* `inputs`
  * 各**UserOperation**に対応するZK証明入力。
* `aggregatedProof`
  * 集約済み証明データ。

## 補足

**ERC4337**では、OIDCのID Tokenをゼロ知識証明（ZK Proof）として扱い、オンチェーンで「**そのユーザーが確かに特定のIDP（Identity Provider）に属している本人である**」ことを検証します。
そのためには、少なくとも以下の3種類の情報が必要になります。

#### ユーザー ID（User ID）

IDPでユーザーを識別するための値です。
OpenID Connect では一般的に `sub`（Subject）として表現されます。
**ERC4337**では、ユーザーのプライバシーを守るため、実際のユーザーIDをそのままオンチェーンに公開せず、`getIdHash` を通じてハッシュ化されたユーザーIDを取得する形にしています。

1つのユーザーIDは、複数のスマートアカウントに紐づけられる可能性があります。

#### Identity Providerの公開鍵

IDPがID Tokenに署名する時には、公開鍵暗号を利用します。
署名の妥当性をZK証明で検証するためには、この公開鍵に対応する検証キーが必要になります。

これは `getVerificationKeyOfIdp` 関数で提供されます。

#### ZK 証明（Proof）

UserがJWTを元に生成したZK証明です。
`verify` 関数がこの証明を使ってユーザー操作を検証します。
検証には以下の2つの情報が渡されます。

* **UserOperation**（**ERC4337**の実行内容）
* **OpenIdZkProofPublicInput**（JWT 情報のハッシュや署名）

複数の証明をまとめて検証する場合は `verifyAggregated` を使用し、**UserOperation**とZK証明入力をまとめて扱います。

#### OpenIdZkProofPublicInputについて

ZK証明でJWTの正当性を検証するためには、以下のフィールドが必須です。

| Field                   | Description                      |
| ----------------------- | -------------------------------- |
| jwtHeaderAndPayloadHash | JWT のヘッダーとペイロードを結合した値のハッシュ  |
| userIdHash   | ユーザー ID をハッシュ化した値 |
| expirationTimestamp | JWT の有効期限（exp クレームに相当）     |
| jwtSignature    | JWT 全体に対する Identity Provider の署名 |

#### 構造体に含めなかった項目について

以下の2つは構造体に含めない設計になっています。

| 含めない項目 | 理由                 |
| --------------------- | ----------------------- |
| 公開鍵（Verification Key） | `getVerificationKeyOfIdp` 関数で取得できるため構造体に含める必要がない。             |
| ユーザー操作のハッシュ           | UserOperation そのものを入力として渡せば、コントラクト側でハッシュを計算できるため構造体に含める必要がない。 |

余計な情報をパブリックインプットに含める必要がなくなり、回路の効率やデータの明瞭性が保たれます。

## セキュリティ

ZK証明を使ったOIDC検証を安全に運用するためには、以下のポイントを確実に満たしている必要があります。

#### 有効期限（expirationTimestamp）の検証

`expirationTimestamp` を正しく検証しなければ、攻撃者が過去に取得したID Tokenを使い回すリプレイ攻撃を行う可能性があります。

ZK証明では以下が求められます。

* `expirationTimestamp` が現在時刻より前ではないこと
* `expirationTimestamp` が増える形式（exp クレームの利用）であること

JWTの `exp` クレームをそのまま利用することで、シンプルかつ安全に期限を扱えます。

#### UserOperationの検証

**UserOperation**がそのまま第三者にコピーされて実行されると、フロントランニング攻撃が発生します。
ZK証明では次を確認する必要があります。

* 証明が対象としている**UserOperation**が、実際に実行される**UserOperation**と一致していること（ハッシュ検証）

これにより、第三者がユーザー操作を盗んで先に実行する危険を避けられます。

#### `userIdHash` の検証

目的は、**UserOperation**を送信したアカウントが、本当にそのユーザーIDに紐づいているかどうかを確認することです。
**Verifier**は以下の条件をチェックします。

* ZK証明内の `userIdHash` と、`getIdHash(account)` が返すハッシュが一致していること

これにより、攻撃者が他人のユーザー IDを使って証明を生成し、成りすますことを防ぎます。

## 引用

Shu Dong (@dongshu2013) <shu@hexlink.io>, Yudao Yan <dean@dauth.network>, Song Z <s@misfit.id>, Kai Chen <kai@dauth.network>, "ERC-7522: OIDC ZK Verifier for AA Account [DRAFT]," Ethereum Improvement Proposals, no. 7522, September 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7522.

## 最後に

今回は「OIDCのIDトークンとZK技術を使ってスマートアカウントの本人確認を安全に行う仕組みを提案している**ERC7522**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
