# 基于模糊提取器的去中心化身份生物特征密钥派生

> 问题是，如果你的生物特征，比如“人脸”，就是你的私钥呢？没有会丢失的助记词，没有会忘记的密码，也没有需要保管的密钥。你的生物特征生成你的钱包，钱包里存放着你的可验证凭证，而你可以用零知识证明来证明这些凭证。

作者 Shodipo Ayomide，发布于 2025年10月21日。网页版本：https://blog.shodipoayomide.com/zh/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`，这一切成为可能。下面来看看它是如何工作的。

## 它真正能解决的问题

传统的密码学系统是为可复现的比特而设计的。但生物特征打破了这些系统所依赖的两条关键规则：

**密钥必须能够完美复现** - 这些系统要求密钥能够逐比特地精确复现。你用来签名交易的私钥每一次都必须完全相同。但生物特征是有噪声的，需要考虑的变量非常多。由于传感器差异、手指放置的位置、湿度、温度等因素，你今天的指纹扫描结果不会和昨天的逐比特一致 - 这样的因素可以一直列下去。人脸扫描、虹膜纹理或者任何生物测量都是如此。

**密钥必须是均匀随机的** - 密码学安全性假设你的密钥是从整个密钥空间中均匀随机抽取的。但生物特征数据是有结构和模式的。你的虹膜并不是均匀随机的，它受到生物学上的约束，在人群层面存在相似性，而且熵集中在某些特定的特征上。

这就造成了常见的先有鸡还是先有蛋的问题：你不能直接把生物特征当作密钥 **它们的噪声很大**，你没法安全地存储它们 **它们属于个人身份信息（PII），而且无法更改**，你也不能简单地对它们做哈希 **正如你所知，噪声会让哈希函数失效**。那该怎么办呢？

现在，精彩的部分来了！

## 什么是模糊提取器？

你可以把模糊提取器看作是针对噪声数据的纠错密码学。它由一对算法组成，`Gen` *生成* 和 `Rep` *复现*，它们能做一件非常令人兴奋的事情：

$Gen(w) \rightarrow (R, P)$ - 接收你带噪声的生物特征输入 `w`，输出一个均匀随机的字符串 `R` **也就是真正的密码学密钥**，以及一个公开的辅助字符串 `P`。还有一个有趣的事实：`P` 可以公开发布，而不会损害安全性。它不会泄露任何关于你的生物特征的有意义的信息。

$Rep(w', P) \rightarrow R$ - 接收另一次生物特征读数 `w'` 以及同一个公开的辅助字符串 `P`，只要 `w'` 与原始的 `w` **足够接近**，就能复现出完全相同的 `R`。我知道这听起来有点吓人，但在这里，**足够接近** 是通过某个度量空间中的距离来衡量的 **二进制串用汉明距离，序列用编辑距离，特征向量用集合差**。

它的安全保证是信息论意义上的，即使给定了 `P`，只要你的生物特征的最小熵满足 $\geq m$，提取出的密钥 `R` 就与均匀分布 $\varepsilon$-接近。这不需要任何计算困难性假设。即使是拥有无限算力的攻击者，也无法以大于 $\varepsilon$ 的优势把 `R` 与真正的随机密钥区分开来。

这背后的数学非常优美。对于一个参数为 $(m, \ell, t, \varepsilon)$ 的模糊提取器：

- $m$ = 生物特征输入的最小熵 **不可预测的比特数**
- $\ell$ = 提取出的密钥 `R` 的长度
- $t$ = 容错度 **`w'` 与 `w` 可以相差多少**
- $\varepsilon$ = 与均匀分布的统计距离 **安全参数**

## 它实际上是如何工作的

这个构造使用了两个协同工作的原语：

### 安全草图（Secure Sketch）$(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 信任模型中有三个参与方：

- 身份持有者 - 也就是你，持有一个存放凭证的钱包
- 签发方 - 信任的来源（政府、大学、KYC 服务商）
- 验证方 - 信任的消费者（dApp、智能合约）
- 现实世界的信任 - 连接签发方和验证方的桥梁

完整的流程：
**生物特征 → 钱包 → 凭证 → 零知识证明 → 已验证的身份**

![](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 使用的是 `Ed25519`，而不是 `secp256k1`。所以在你的实现中，要使用 `ed25519-hd-key` 来满足 `SLIP-0010` 规范 **与 `Phantom`、`Solflare` 保持一致**。

### 获取你的可验证凭证

现在你的钱包有了一个 `DID`，但要证明任何事情，你还需要一份可验证凭证 **VC**。信任三角就是从这里开始的。
签发方会在链下核验你的证件，然后签发一份经过密码学签名的证明：

```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;
}
```
发生了什么：

- 签发方在链下核验你的证件，例如护照、自拍照等
- 签发一份包含你的声明、经过 `cryptographically` 签名的 `VC`
- `VC` 中包含签发方的 `DID` 签名，用来证明其真实性
- 存储在你本地的钱包中，而不是区块链上
- 可以持有来自不同签发方的多份 `VCs`，比如学历、工作经历、KYC

凭证的 schema 定义了 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..."
  }
}
```
现在，当验证方要求你 **证明你已满 18 岁** 时，你就可以基于这份凭证生成一个零知识证明。

### 零知识证明层

在这一层，你可以证明自己的个人信息，比如 **年龄、国籍、学位、地址、出生日期等**，而不需要泄露这些敏感数据。

```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` 岁
- 凭证由受信任的签发方签名
- 不会泄露的内容：实际年龄、出生日期、姓名或任何个人身份信息

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);
    }
}
```

这个合约会：

- 以密码学方式验证零知识证明
- 检查公开信号 **签发方 DID、年龄门槛**
- 把用户标记为已验证
- 执行业务逻辑 **空投、DAO 访问权限等**

所以从这个流程中，你可以看到你的生物特征变成了：

- **你的钱包** - 多链、非托管、无需助记词
- **你的身份** - 锚定在你的生物特征上的去中心化标识符 `DID`
- **你的凭证** - 来自受信任签发方的可验证凭证 `VC`
- **你的隐私** - 零知识证明 `ZKP` 不会泄露任何关于你的信息

而且这并不是多个相互独立的系统 - 它是一个统一的流程。钱包生成只在注册时发生一次。只要需要身份验证，就可以使用零知识证明流程。可以这样理解：在普通交易中，你可以像使用其他任何加密钱包一样使用你的生物特征钱包。但当某个 dApp 需要 KYC 或任何基于 `PII` 的验证时 **比如 `Polygon ID` 的使用场景**，零知识证明层就会介入，在不暴露你数据的情况下证明你的凭证。

## 一些可能的使用场景

1. **智能体护照：**

每个 AI agent 都可以拥有一个由生物特征派生的 `DID`。想象一个拥有自己的钱包和已验证凭证的 AI 交易 agent：

```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);
    }
}
```

这个 agent：

- 从它的 `biometric` **神经网络哈希** 派生出钱包
- 通过 `ZKP` 证明自己的信誉 **1000 笔以上的成功交易**
- 凭借经过验证的历史记录自主运行

`SingularityNET` 正在构建的正是这样的东西，一个 AI Agent 信任注册中心，agent 可以在其中使用可验证凭证来证明自己的真实性、安全性和审计状态。`biometric-wallet-ZKP` 流程让 agent 能够自主运行，同时保持可问责性。

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 在允许访问之前会先进行查询。已知的诈骗者会在全网范围内被拦截，一些有过异常活动历史的地址也会被标记出来，同时集成方会收到关于过往可疑活动的通知。

## 开发者集成

基于生物特征身份进行构建需要基础设施。整个生态提供了三个核心组件：

### 签发节点（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
```

功能：

- 创建签发方 `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 schema
- Query Builder：在 `tools.privado.id/query-builder` 设计零知识证明查询
- Schema Explorer：浏览社区的 `schemas`
- Marketplace：发现凭证 `issuers`

## 一些技术挑战与解决方案

1：**生物特征的可变性**

你的指纹在不同扫描之间会有 10-15% 的差异。解决方案：`BCH` 码是比特级纠错的最优选择。对于一个错误率为 10%（约 205 个比特错误）的 2048 比特虹膜编码 **其中实际熵约为 249 比特**：

- 使用定义在 $GF(2^{11})$ 上的 BCH $[2048, 1122, 341]$ 码 - 最多可纠正 170 个比特错误
- 熵损失：$2048 - 1122 = 926$ 比特（草图长度）
- 剩余熵：$249 - (926 / 8.2) \approx 136$ 比特有效熵
- 提取 128 比特的密钥 **保守取值，已考虑熵损失**

**关键区别**：这 2048 比特是原始的虹膜编码表示 **Daugman 算法的输出**。由于生物学上的相关性和冗余，实际的信息论熵只有约 249 比特 **比例约为 8:1**。

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 许可证），可以在移动端运行
- 零知识证明：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 });
}
```

普通的钱包流程和身份流程可以共存。只有在需要验证时，零知识证明才会登场，无论是用于 KYC，还是用于任何基于 `PII` 的验证，比如年龄、国籍、学位、地址、出生日期等。

你的生物特征就是你的密钥，你的钱包就是你在所有支持的链上的身份，而且你永远不用担心弄丢助记词或私钥，一切都与你的身体特征绑定在一起，在 BMoni 的实现中，那就是你的脸！

## 重要链接

- [模糊提取器综述](https://www.cs.bu.edu/~reyzin/papers/fuzzysurvey.pdf) - Dodis, Reyzin, Smith
- [模糊提取器（SIAM）](https://epubs.siam.org/doi/10.1137/060651380)
- [基于 LWE 的高效可重用模糊提取器](https://www.cs.umd.edu/~jkatz/papers/reusable-FE.pdf) - 后量子构造
- [CompFE](https://github.com/benjaminfuller/CompFE)
- [Polygon ID SDK](https://github.com/0xPolygonID) - 零知识证明身份系统
- [BMoni 的安全性](https://bmoni.com/security/#22patents) - 我们的安全架构
- [snarkjs](https://github.com/iden3/snarkjs) - ZK-SNARK 库
