# ファジーエクストラクターを用いた分散型アイデンティティのための生体情報による鍵導出

> もしあなたの生体情報、たとえば「顔」が秘密鍵だとしたらどうでしょう？失くすシードフレーズも、忘れるパスワードも、保管する鍵もありません。生体情報からウォレットが生成され、そのウォレットが検証可能なクレデンシャルを保持し、あなたは ZKP を使ってそれを証明します。

著者 Shodipo Ayomide、2025年10月21日 公開。Web 版：https://blog.shodipoayomide.com/ja/biometric-bound-key-derivation-for-decentralized-identity-using-fuzzy-extractors

![ファジーエクストラクターを用いた DID のための生体情報バインド型の鍵導出](https://blog.shodipoayomide.com/banners/biometric-bound-key-derivation-for-decentralized-identity-using-fuzzy-extractors.png)

今日のアイデンティティシステムは、データを安全に保管してもらうという点で、企業に非常に大きな信頼を委ねています。複数の KYC プロバイダーがあなたのデータを無期限に保持しています。アプリは、あなたが 18 歳以上であることさえわかればいい場合でも、あなたの詳細な情報をすべて受け取ります。これは共有するには多すぎる情報で、しかもあちこちにバラバラに散らばったままになります。

自己主権型アイデンティティ **SSI** は、クレデンシャルの管理権をあなた自身に与えることでこれを解決します。必要なものだけを共有し、データを明かすことなく主張を証明できるのです。基本的に、検証可能なクレデンシャル **VC** を使えば、正確な生年月日を見せずに 18 歳以上であることを証明できます。しかし **SSI** には課題があります。アイデンティティを管理する秘密鍵を、いったいどこに保管するのでしょうか？

これにはさまざまな根本的な解決策があり、そのひとつが `biometric crypto identities` です。あまりメジャーな方法ではありませんが、問いはこうです。もしあなたの `biometric`、たとえば `face` が `private-key` だとしたら？失くす `seed phrases` も、忘れる `passwords` も、保管する `keys` もありません。あなたの `biometric` から `wallet` が生成され、そのウォレットが検証可能なクレデンシャルを保持し、あなたは ZKP を使ってそれを証明します。

これは `fuzzy extractors` + `zero-knowledge proofs` によって実現できます。その仕組みを見ていきましょう。

## これが解決しうる本当の問題

従来の暗号システムは、再現可能なビット列を前提に設計されていました。しかし生体情報は、こうしたシステムが依存している 2 つの重要なルールを破ってしまいます：

**鍵は完全に再現可能でなければならない** - こうしたシステムは、ビット単位で完全に同じ鍵を再現することを求めます。トランザクションに署名するための秘密鍵は、毎回まったく同じでなければなりません。しかし生体情報はノイズが多く、考慮すべき変数がたくさんあります。今日の指紋スキャンは、センサーのばらつき、指の置き方、湿度、温度などによって、昨日のスキャンとビット単位で一致することはありません。挙げればきりがありません。顔スキャンや虹彩パターン、その他あらゆる生体計測も同じです。

**鍵は一様ランダムでなければならない** - 暗号の安全性は、鍵が鍵空間全体から一様ランダムに選ばれていることを前提としています。しかし生体データには構造やパターンがあります。虹彩は一様ランダムではなく、生物学的な制約や集団レベルでの類似性があり、エントロピーは特定の特徴に集中しています。

これはおなじみの「鶏が先か卵が先か」の問題を生み出します。生体情報をそのまま鍵として使うことはできませんし **ノイズが多すぎる**、安全に保管することもできませんし **PII であり不変**、ご存じのとおり単純にハッシュ化することもできません **ノイズがハッシュ関数を壊す**。では、どうすればいいのでしょうか？

さあ、ここからがおいしいところです！

## ファジーエクストラクターとは？

ファジーエクストラクターは、ノイズの多いデータのための誤り訂正付き暗号だと考えてください。これは `Gen` *生成（generate）* と `Rep` *再現（reproduce）* という 2 つのアルゴリズムのペアで、とてもワクワクすることをやってのけます：

$Gen(w) \rightarrow (R, P)$ - ノイズを含む生体入力 `w` を受け取り、一様ランダムな文字列 `R` **実際の暗号鍵** と、公開のヘルパー文字列 `P` を出力します。さらにもうひとつ面白い事実があります。`P` は安全性を損なうことなく公開できるのです。生体情報について意味のある情報が明かされることは一切ありません。

$Rep(w', P) \rightarrow R$ - 異なる生体読み取り値 `w'` と同じ公開ヘルパー `P` を受け取り、`w'` が元の `w` に **十分近い** 限り、まったく同じ `R` を再現します。ちょっと怖く聞こえるのはわかりますが、ここでいう **十分近い** とは、ある距離空間における距離で測られます **バイナリ文字列ならハミング距離、シーケンスなら編集距離、特徴ベクトルなら集合の差**。

安全性の保証は情報理論的なものです。たとえ `P` が与えられても、生体情報が $\geq m$ の最小エントロピーを持つ限り、抽出された鍵 `R` は一様分布に対して $\varepsilon$-close です。計算量的な困難性の仮定は一切必要ありません。無限の計算能力を持つ攻撃者であっても、$\varepsilon$ より大きいアドバンテージで `R` を真にランダムな鍵と区別することはできません。

数学的にも美しい仕組みです。パラメータ $(m, \ell, t, \varepsilon)$ を持つファジーエクストラクターでは：

- $m$ = 生体入力の最小エントロピー **予測不可能性のビット数**
- $\ell$ = 抽出される鍵 `R` の長さ
- $t$ = 誤り許容度 **`w'` が `w` からどれだけ異なってもよいか**
- $\varepsilon$ = 一様分布からの統計的距離 **セキュリティパラメータ**

## 実際にどう動くのか

この構成では、2 つのプリミティブが連携して動きます：

### セキュアスケッチ $(SS, Rec)$

セキュアスケッチは、ファジーなコミットメントのようなものです。$SS(w) \rightarrow s$ はスケッチ `s` を出力します。このスケッチは誤りを訂正するのにちょうど足りるだけの情報を明かしますが、`w` そのものを再構築できるほどの情報はまったく明かしません。そして $Rec(w', s) \rightarrow w$ は、スケッチを使ってノイズを含む `w'` から元の `w` を復元します。

ハミング距離を使うバイナリ文字列の場合、シンドローム構成（Syndrome Construction）が最適です：

```python
class SyndromeSketch:
    def __init__(self, n, k, t):
        self.n, self.k, self.t = n, k, t
        self.syn_matrix = generate_bch_syndrome_matrix(n, k)

    def sketch(self, w):
        return np.dot(self.syn_matrix, w) % 2

    def recover(self, w_prime, s):
        s_prime = np.dot(self.syn_matrix, w_prime) % 2
        error_syn = (s_prime - s) % 2
        e = bch_decode(error_syn, self.t)
        return (w_prime - e) % 2
```

ここで何が起きているのか：

- `sketch()` は生体情報 `w` のシンドロームを計算し、`s` `$n-k$` ビットを出力します
- `recover()` はスケッチ `s` を使って、ノイズを含む `w_prime` の誤りを訂正します
- エントロピーの損失は、ちょうどスケッチの長さである `$n-k$` ビットです
- $(128, 64, 22)$ の拡張 `BCH` 符号 **Bose–Chaudhuri–Hocquenghem 符号** の場合：64 ビットを失い、最大 $t = \lfloor(22-1)/2\rfloor = 10$ 個の誤りを訂正できます

### さて、スケッチからエクストラクターへ

誤り訂正の後、復元した生体情報をハッシュ化して一様な鍵を抽出します。たとえば：

```rust
pub fn fuzzy_gen(biometric: &[u8]) -> (Vec<u8>, Vec<u8>) {
    let sketch = secure_sketch(biometric);
    let seed: Vec<u8> = (0..32).map(|_| rng.gen()).collect();
    let key = extract_key(biometric, &seed);
    let helper = [sketch, seed].concat();
    (key, helper)
}

pub fn fuzzy_rep(noisy_biometric: &[u8], helper: &[u8]) -> Vec<u8> {
    let (sketch, seed) = split_helper(helper);
    let recovered = sketch_recover(noisy_biometric, sketch);
    extract_key(&recovered, &seed)
}
```

`fuzzy_gen()` 関数は：

- 生体情報からスケッチを作成します
- ハッシュ関数用のランダムなシードを生成します
- `hash(biometric || seed)` を使って鍵を抽出します
- `key, helper` を返します。ここで helper = `sketch, seed` です

`fuzzy_rep()` 関数は：

- スケッチを使って、ノイズを含むスキャンから元の生体情報を復元します
- 同じシードで同じハッシュ関数を適用します
- まったく同じ鍵が得られます！

代表的な生体情報の場合：

- 虹彩：2048 ビットの生コード **実際のエントロピーは約 249 ビット**、誤り率 10-30% → **40-105 ビットの鍵** を抽出
- 指紋：20-80 個の特徴点 **約 50-150 ビット**、ノイズが大きい → **8-40 ビットの鍵** を抽出
- 顔：512 次元の埋め込み **実効エントロピーは 20-60 ビット**

> 注：生の生体表現には冗長性や相関が含まれています。虹彩コードでは、生のビット数（2048）と情報理論的なエントロピー（約 249 ビット）の間に約 8:1 の比率があります。鍵の抽出は、表現のサイズではなく、実際のエントロピーに従わなければなりません。

## アイデンティティへの応用
![](https://i.ibb.co/MyZNpbG3/Screenshot-2025-10-21-at-4-02-19-PM.png)

SSI のトラストモデルには 3 つのアクターがいます：

- Identity Holder - クレデンシャルを含むウォレットを持つあなた
- Issuer - 信頼の発信源（政府、大学、KYC プロバイダー）
- Verifier - 信頼の消費者（dApp、スマートコントラクト）
- Real World Trust - Issuer と Verifier をつなぐ橋渡し

全体の流れ：
**生体情報 → ウォレット → クレデンシャル → ZK 証明 → 検証済みアイデンティティ**

![](https://i.ibb.co/WpHYM9Fq/image.png)

ステップごとに見ていきましょう：

### 生体情報からウォレットへ

```javascript
async function biometricToWallet(rawBiometric) {
    const binary = await preprocessBiometric(rawBiometric);
    const { key, helper } = await fuzzyExtractor.gen(binary);

    const expandedKey = await hkdf(
        key,
        128,
        'bip39-seed',
        await crypto.getRandomValues(new Uint8Array(32))
    );

    const mnemonic = bip39.entropyToMnemonic(expandedKey);
    const seed = await bip39.mnemonicToSeed(mnemonic);
    const master = bip32.fromSeed(seed);

    return {
        btc: deriveBitcoin(master),
        eth: deriveEthereum(master),
        sol: deriveSolana(seed),
        helper,
        salt
    };
}
```

処理の流れ：

- 生体情報を前処理 → バイナリベクトル **虹彩コード、顔の埋め込みなど**
- `fuzzy_gen()` を実行 → 40-128 ビットの鍵 + 公開ヘルパーを取得
- `BIP39` ニーモニックに変換 → HD ウォレットのシードを導出
- BTC `P2WPKH`、ETH `EVM`、SOL `Ed25519` のアドレスを導出
- アドレス + ヘルパー文字列 + ソルトを返す **すべて公開して保存！**

Solana の場合は特に、**異なる曲線** を使います：

```javascript
import { derivePath } from 'ed25519-hd-key';

const solSeed = derivePath("m/44'/501'/0'/0'", seed.toString('hex')).key;
const solKeypair = Keypair.fromSeed(solSeed);
const solAddress = solKeypair.publicKey.toBase58();
```

> 注：Solana は `secp256k1` ではなく `Ed25519` を使います。そのため実装では、`SLIP-0010` に準拠するために `ed25519-hd-key` を使ってください **`Phantom`、`Solflare` と一致します**。

### 検証可能なクレデンシャルを取得する

これでウォレットは `DID` を持つようになりましたが、何かを証明するには検証可能なクレデンシャル **VC** が必要です。ここからトラストトライアングルが始まります。
Issuer はあなたの書類をオフチェーンで検証し、暗号学的に署名された証明を発行します：

```javascript
async function requestCredential(wallet, issuerEndpoint, claimData) {
    const connectionRequest = await issuerEndpoint.createConnection(wallet.did);
    
    const credentialOffer = await issuerEndpoint.offerCredential({
        type: 'KYCCredential',
        schema: 'https://schema.org/kyc-v1',
        claims: {
            country: claimData.country,
            birthYear: claimData.birthYear,
            verified: true
        }
    });
    
    const signedVC = await wallet.acceptCredential(credentialOffer);
    await wallet.storeCredential(signedVC);
    
    return signedVC;
}
```
何が起きたのか：

- Issuer がオフチェーンであなたの書類を検証します。たとえばパスポート、自撮り写真など
- あなたの主張を含む、`cryptographically` 署名された `VC` を発行します
- `VC` には Issuer の `DID` 署名が含まれ、真正性を証明します
- ブロックチェーン上ではなく、あなたのウォレットにローカルで保存されます
- 教育、職歴、KYC など、さまざまな Issuer からの複数の `VCs`

クレデンシャルのスキーマは、VC に何が含まれるかを定義します：
```json
{
  "@context": "https://www.w3.org/2018/credentials/v1",
  "type": ["VerifiableCredential", "KYCCredential"],
  "issuer": "did:polygonid:polygon:amoy:2qH7...",
  "issuanceDate": "2025-01-15T00:00:00Z",
  "credentialSubject": {
    "id": "did:polygonid:polygon:amoy:2qPz...",
    "birthYear": 1995,
    "country": "US",
    "kycVerified": true
  },
  "proof": {
    "type": "BJJSignature2021",
    "issuerData": {...},
    "signature": "0x..."
  }
}
```
これで、Verifier から **18 歳以上であることを証明して** と求められたら、このクレデンシャルから ZKP を生成できます。

### ゼロ知識証明レイヤー

ここでは、**年齢、市民権、学位、住所、生年月日など** の個人情報を、それらの機密データを明かすことなく証明できます。

```javascript
async function proveAge(wallet, minAge) {
    const credential = await wallet.getCredential('kyc');
    const inputs = {
        birthYear: credential.birthYear,
        currentYear: 2025,
        minAge: minAge,
        signature: credential.signature
    };

    const { proof, publicSignals } = await snarkjs.groth16.fullProve(
        inputs,
        "ageCheck.wasm",
        "ageCheck.zkey"
    );

    return { proof, publicSignals };
}
```

証明される内容：

- ユーザーが `minAge` 歳以上であること
- クレデンシャルが信頼できる Issuer によって署名されていること
- 明かされないもの：実際の年齢、生年月日、名前、その他あらゆる PII

Circom の回路：

```circom
template AgeCheck() {
    signal input birthYear;
    signal input currentYear;
    signal input minAge;

    signal age <== currentYear - birthYear;

    component check = GreaterEqThan(16);
    check.in[0] <== age;
    check.in[1] <== minAge;
    
    signal output isValid;
    isValid <== check.out;
}
```

オンチェーンでの検証 - `Solidity`：

```solidity
contract IdentityGate {
    mapping(address => mapping(uint256 => bool)) public verified;

    function submitProof(
        uint256 requestId,
        uint[2] calldata pA,
        uint[2][2] calldata pB,
        uint[2] calldata pC,
        uint[3] calldata pubSignals
    ) external {
        require(zkpVerifier.verifyProof(pA, pB, pC, pubSignals), "Invalid proof");
        require(pubSignals[2] >= minAge, "Age requirement not met");

        verified[msg.sender][requestId] = true;
        _grantAccess(msg.sender);
    }
}
```

このコントラクトは：

- ZK 証明を暗号学的に検証します
- 公開シグナル **Issuer の DID、年齢の閾値** をチェックします
- ユーザーを検証済みとしてマークします
- ビジネスロジック **エアドロップ、DAO へのアクセスなど** を実行します

この流れから、あなたの生体情報が次のものになったことがわかるでしょう：

- **あなたのウォレット** - マルチチェーン、ノンカストディアル、シードフレーズ不要
- **あなたのアイデンティティ** - 生体情報に紐づいた分散型識別子 `DID`
- **あなたのクレデンシャル** - 信頼できる Issuer からの検証可能なクレデンシャル `VC`
- **あなたのプライバシー** - ゼロ知識証明 `ZKP` はあなたについて何も明かさない

そしてこれは、複数の別々のシステムではありません。たったひとつの統合されたフローなのです。ウォレットの生成は登録時に一度だけ行われます。ZKP のフローは、本人確認が必要なときにいつでも使えます。こう考えてみてください。生体ウォレットは、通常のトランザクションでは他の暗号資産ウォレットとまったく同じように使えます。しかし dApp が KYC や何らかの `PII` ベースの検証 **`Polygon ID` のユースケースのようなもの** を必要とするときには、ZKP レイヤーが動き出し、データを公開することなくクレデンシャルを証明します。

## 考えられるユースケース

1. **エージェント用パスポート：**

すべての AI エージェントが、生体情報から導出された `DID` を持てます。独自のウォレットと検証済みのクレデンシャルを持つ AI トレーディングエージェントを想像してみてください：

```javascript
class TradingAgent {
    constructor(agentFingerprint) {
        this.wallet = await biometricToWallet(agentFingerprint);
        this.did = await createDID(this.wallet);
    }

    async trade(dex, params) {
        const proof = await this.proveReputation();
        return await dex.trade(params, proof);
    }
}
```

このエージェントは：

- 自身の `biometric` **ニューラルネットワークのハッシュ** からウォレットを導出します
- `ZKP` を使って評判 **1000 回以上の成功したトレード** を証明します
- 検証済みの実績を持って自律的に動作します

`SingularityNET` はまさにこれを構築しています。AI Agent Trust Registry と呼ばれるもので、エージェントは検証可能なクレデンシャルを使って、真正性、安全性、監査ステータスを証明します。`biometric-wallet-ZKP` のフローによって、エージェントは説明責任を保ちながら自律的に動作できるようになります。

2. **dApp のアイデンティティレイヤー：**

ある DeFi プロトコルは KYC を必要としていますが、ユーザーデータは保存したくありません **規制上の責任 + ハッカーにとってのハニーポット**：

```solidity
contract DeFiProtocol {
    modifier onlyKYCVerified() {
        require(identityGate.verified(msg.sender, KYC_ID), "KYC required");
        _;
    }

    function borrowUndercollateralized(uint256 amount) external onlyKYCVerified {
        _issueLoan(msg.sender, amount);
    }
}
```

プロトコルがあなたのパスポートや名前、住所を目にすることは一切ありません。コンプライアンスを満たしつつ、プライバシーも守られます。

同じパターンは次のような用途にも使えます：

- DAO の投票 **アイデンティティではなく、メンバーであることを証明**
- エアドロップ **人間であることを証明し、ボットを防ぐ**
- 年齢制限 **正確な年齢ではなく、18 歳以上であることを証明**

3. **Web2 のパスワードレスログイン**

```javascript
async function login(website) {
    const { key } = await fuzzyExtractor.rep(biometric, helper);
    const siteKey = deriveKey(key, website.domain);
    const signature = sign(challenge, siteKey);
    await website.verify(signature);
}
```

サイトごとに固有の鍵が導出されます。パスワードは送信されず、フィッシングされるクレデンシャルもありません。

4. **生体認証による支払い：**

あなたの生体情報がトランザクションを承認します：

```javascript
async function pay(merchant, amount) {
    const wallet = await biometricToWallet(scan);

    if (amount > 1000) {
        await merchant.verifyProof(generateKYCProof(wallet));
    }

    await wallet.sendTransaction({ to: merchant.address, value: amount });
}
```

[BMONI](https://bmoni.com) + `BKey` は現在まさにこれを実装しています。あなたのウォレットはすべて生体情報「**あなたの顔**」から完全に導出され、特定の EVM チェーンやトークンにまたがってウォレットがプロビジョニングされます。すべて顔だけで！

5. **詐欺防止データベース：**

```solidity
contract AML {
    bytes32 public scamMerkleRoot;

    function checkClean(address wallet, bytes32[] calldata proof) public view returns (bool) {
        bytes32 hash = keccak256(abi.encodePacked(wallet));
        return !MerkleProof.verify(proof, scamMerkleRoot, hash);
    }
}
```

dApp はアクセスを許可する前に問い合わせます。既知の詐欺師はネットワーク全体でブロックされ、不審な活動の履歴があるものも記録されます。一方で、インテグレーターは過去の不審な活動について通知を受け取ります。

## 開発者向けのインテグレーション

生体アイデンティティを使って構築するには、インフラが必要です。エコシステムは 3 つのコアコンポーネントを提供しています：

### Issuer Node

組織はセルフホストの Issuer Node を運用して、クレデンシャルを発行します：

```bash
docker-compose up -d
make import-private-key-to-kms private_key=<eth-key>

# access the ui at localhost:8088
# access the api at localhost:3001
```

機能：

- Issuer の `DID` を作成
- クレデンシャルの `schemas` を定義
- `VCs` の発行/失効
- ユーザーとの接続を管理
- オンチェーンでのステート遷移 **Polygon**

API の例：

```javascript
const issuerAPI = new IssuerNode({
    url: 'https://issuer.example.com',
    auth: { user: 'admin', password: 'secure' }
});

await issuerAPI.createIdentity({
    blockchain: 'polygon',
    network: 'amoy',
    method: 'polygonid'
});

await issuerAPI.issueCredential({
    did: 'did:polygonid:...',
    schema: 'KYCCredential',
    claims: { ageOver: 18 }
});
```

**ツールのエコシステム**

- Schema Builder：`tools.privado.id/builder` でカスタムの VC スキーマを作成
- Query Builder：`tools.privado.id/query-builder` で ZKP のクエリを設計
- Schema Explorer：コミュニティの `schemas` を閲覧
- Marketplace：クレデンシャルの `issuers` を探す

## 技術的な課題と解決策

1: **生体情報のばらつき**

指紋はスキャンごとに 10-15% 変動します。解決策：`BCH` 符号はビットレベルの誤り訂正に最適です。2048 ビットの虹彩コード **実際のエントロピーは約 249 ビット** で、誤り率が 10%（約 205 ビットの誤り）の場合：

- $GF(2^{11})$ 上の BCH $[2048, 1122, 341]$ 符号を使う - 最大 170 ビットの誤りを訂正
- エントロピーの損失：$2048 - 1122 = 926$ ビット（スケッチの長さ）
- 残りのエントロピー：$249 - (926 / 8.2) \approx 136$ ビットの実効エントロピー
- 128 ビットの鍵を抽出 **保守的な値で、エントロピーの損失を考慮済み**

**重要な違い**：2048 ビットというのは、生の虹彩コード表現 **Daugman のアルゴリズムの出力** です。実際の情報理論的なエントロピーは、生物学的な相関と冗長性 **約 8:1 の比率** のために、わずか約 249 ビットしかありません。

2: **テンプレートの経年変化**

問題：顔は時間とともに変化します **加齢、体重、ひげなど**

解決策：鍵の移行を伴う定期的な再登録：

```javascript
async function migrate(oldBio, newBio) {
    const oldWallet = await recover(oldBio);
    const { newWallet, newHelper } = await enroll(newBio);
    await oldWallet.transferAll(newWallet.address);
    await storage.update(newHelper);
}
```

顔なら 2-3 年ごと、虹彩なら 5 年ごと、声なら毎年といった具合に再登録します。

3: **ライブネス検知**

問題：あなたの写真を持っている攻撃者が、あなたのウォレットにアクセスできてはなりません。

解決策：生体情報の取得時にライブネスチェックを行う：

```javascript
const checks = await Promise.all([
    depthSensor.measure(),
    motionDetector.verify(['blink', 'nod']),
    thermalCamera.capture(),
    videoAnalyzer.checkTemporal()
]);
```

WorldCoin の Orb は、虹彩スキャンでこれを行っています。25 以上のライブネスチェック、3D イメージング、赤外線 + RGB カメラです。驚くほどうまく機能していて、他人受入率は $< 0.0001\%$ です。

4: **ヘルパー文字列のプライバシー**

先ほど説明した `P` は生体情報を漏らすのでしょうか？いいえ。定理 **Dodis ら** によれば、攻撃者が知りうるのは最大でも $n-k$ ビット **スケッチの長さ** で、鍵 `R` は統計的にランダムなままです。私たちの虹彩の例では、408 ビットが知られ、392 ビットのエントロピーが残ります。

## 本番投入できるのか？

必要なピースはすでに揃っています：

- ファジーエクストラクター：`CompFE` ライブラリ（MIT）、モバイルで動作
- ZK 証明：BKey、Privado ID などがメインネットで稼働中
- ハードウェア：D'CENT ウォレット、FaceID、指紋センサーはどこにでもある
- 標準：W3C の DID、VC、WebAuthn
- ネットワーク：BTC、ETH、Polygon、Solana はすべて HD ウォレットに対応 **2013 年から BIP32/39/44**

足りないものは？インテグレーションです...

## 本番実装の例

本番環境で実際にこのシステムを使うと、こんな感じになります

```javascript
async function enrollUser() {
    const bio = await captureLive();
    const { key, helper } = await fuzzyExtractor.gen(bio);

    const salt = await crypto.getRandomValues(new Uint8Array(32));
    const expandedKey = await hkdf('sha256', key, salt, 'bip39-seed', 128/8);
    const wallet = await deriveWallet(expandedKey);

    const did = await createDID(wallet.eth);
    const kycCred = await requestKYC(did, { passport, selfie });

    await storage.save({ helper, salt, did, credentials: [kycCred] });
    return { wallet, did };
}

async function accessDApp(dappURL) {
    const bio = await captureLive();
    const { helper, salt } = await storage.getHelperData();
    const key = await fuzzyExtractor.rep(bio, helper);

    const expandedKey = await hkdf('sha256', key, salt, 'bip39-seed', 128/8);
    const wallet = await deriveWallet(expandedKey);

    const proof = await generateProof(wallet, dappURL.request);
    await submitProof(wallet, proof, dappURL.verifier);

    await dappURL.grantAccess(wallet.address);
}

async function sendPayment(recipient, amount) {
    const bio = await captureLive();
    const { helper, salt } = await storage.getHelperData();
    const key = await fuzzyExtractor.rep(bio, helper);

    const expandedKey = await hkdf('sha256', key, salt, 'bip39-seed', 128/8);
    const wallet = await deriveWallet(expandedKey);

    await wallet.send({ to: recipient, value: amount });
}
```

通常のウォレットのフローとアイデンティティのフローは共存します。ZKP が登場するのは検証が必要なときだけで、KYC のためか、年齢、市民権、学位、住所、生年月日などの `PII` ベースの検証のためです。

あなたの生体情報があなたの鍵であり、あなたのウォレットはサポートされているすべてのチェーンにまたがるあなたのアイデンティティです。シードフレーズや秘密鍵を失くす心配をする必要は一切ありません。すべてはあなたの身体的特徴に結びついていて、BMoni の実装ではそれはあなたの顔なのです！

## 必読リンク

- [ファジーエクストラクターのサーベイ](https://www.cs.bu.edu/~reyzin/papers/fuzzysurvey.pdf) - Dodis, Reyzin, Smith
- [ファジーエクストラクター（SIAM）](https://epubs.siam.org/doi/10.1137/060651380)
- [Efficient Reusable Fuzzy Extractors from LWE](https://www.cs.umd.edu/~jkatz/papers/reusable-FE.pdf) - ポスト量子の構成
- [CompFE](https://github.com/benjaminfuller/CompFE)
- [Polygon ID SDK](https://github.com/0xPolygonID) - ZKP アイデンティティシステム
- [BMoni のセキュリティ](https://bmoni.com/security/#22patents) - 私たちのセキュリティアーキテクチャ
- [snarkjs](https://github.com/iden3/snarkjs) - ZK-SNARK ライブラリ
