# Derivación de claves biométricas para identidad descentralizada con fuzzy extractors

> La pregunta es: ¿y si tu biometría, por ejemplo tu 'cara', fuera tu clave privada? Sin seed phrases que perder, sin contraseñas que olvidar, sin claves que guardar. Tu biometría genera tu wallet, que guarda tus credenciales verificables, que pruebas usando ZKPs.

Por Shodipo Ayomide, publicado el 21 oct 2025. Versión web: https://blog.shodipoayomide.com/es/biometric-bound-key-derivation-for-decentralized-identity-using-fuzzy-extractors

![Derivación de claves ligada a la biometría para DID usando fuzzy extractors](https://blog.shodipoayomide.com/banners/biometric-bound-key-derivation-for-decentralized-identity-using-fuzzy-extractors.png)

Los sistemas de identidad de hoy depositan muchísima confianza en las empresas para que guarden tus datos de forma segura. Varios proveedores de KYC conservan tus datos indefinidamente. Cada app recibe todos tus datos aunque solo necesite saber que tienes más de 18 años, lo cual es demasiada información para compartir y dejar fragmentada por todas partes.

La identidad autosoberana **SSI** resuelve esto dándote el control de tus credenciales, puedes compartir solo lo necesario y probar afirmaciones sin revelar datos. básicamente, las credenciales verificables **VC** te permiten probar que tienes más de 18 años sin mostrar tu fecha de nacimiento exacta. Pero **SSI** tiene un problema - ¿dónde guardas las claves privadas que controlan tu identidad?

Hay muchas soluciones de fondo para esto y una de ellas son las `biometric crypto identities`, que no es de las más populares, la pregunta es: ¿y si tu `biometric`, por ejemplo tu `face`, fuera tu `private-key`? Sin `seed phrases` que perder, sin `passwords` que olvidar, sin `keys` que guardar. Tu `biometric` genera tu `wallet`, que guarda tus credenciales verificables, que pruebas usando ZKPs.

Esto es posible gracias a `fuzzy extractors` + `zero-knowledge proofs`. Así es como funciona.

## El problema real que esto podría resolver

Los sistemas criptográficos tradicionales se diseñaron para bits reproducibles. Pero la biometría rompe dos reglas clave de las que dependen estos sistemas:

**Las claves deben ser perfectamente reproducibles** - Exigen que la clave se reproduzca bit a bit. Tu clave privada para firmar transacciones debe ser idéntica todas y cada una de las veces. Pero la biometría tiene ruido, hay muchas variables que considerar. El escaneo de tu huella de hoy no va a coincidir bit a bit con el de ayer por variaciones del sensor, la posición del dedo, la humedad, la temperatura - y la lista sigue y sigue. Lo mismo pasa con los escaneos faciales, los patrones del iris o cualquier medición biológica.

**Las claves deben ser uniformemente aleatorias** - La seguridad criptográfica asume que tu clave se toma de forma uniformemente aleatoria de todo el espacio de claves. Pero los datos biométricos tienen estructura y patrones. Tu iris no es uniformemente aleatorio, tiene restricciones biológicas, similitudes a nivel de población y entropía concentrada en rasgos específicos.

Esto crea el típico problema del huevo y la gallina: no puedes usar la biometría directamente como clave **tiene muchísimo ruido**, no puedes guardarla de forma segura **es PII e inmutable**, y no puedes simplemente hashearla **el ruido rompe las funciones hash**, como ya sabes. Entonces, ¿qué haces?

¡Y aquí viene lo bueno!

## ¿Qué son los fuzzy extractors?

Piensa en los fuzzy extractors como criptografía con corrección de errores para datos con ruido. Son un par de algoritmos, `Gen` *generar* y `Rep` *reproducir*, que hacen algo muy emocionante:

$Gen(w) \rightarrow (R, P)$ - Toma tu entrada biométrica con ruido `w` y devuelve un string uniformemente aleatorio `R` **la clave criptográfica en sí** y un string auxiliar público `P`. Aquí va otro dato curioso: `P` se puede publicar abiertamente sin comprometer la seguridad. No revela absolutamente ninguna información significativa sobre tu biometría.

$Rep(w', P) \rightarrow R$ - Toma una lectura biométrica distinta `w'` y el mismo auxiliar público `P`, y reproduce exactamente el mismo `R` siempre que `w'` esté **lo bastante cerca** del `w` original. Ya sé que eso suena aterrador, pero en este caso **lo bastante cerca** se mide como una distancia en algún espacio métrico **distancia de Hamming para strings binarios, distancia de edición para secuencias, diferencia de conjuntos para vectores de características**.

La garantía de seguridad es de teoría de la información, incluso conociendo `P`, la clave extraída `R` está $\varepsilon$-cerca de la uniforme siempre que tu biometría tenga una min-entropía $\geq m$. No hace falta ninguna suposición de dureza computacional. Un adversario con poder de cómputo infinito sigue sin poder distinguir  `R` de una clave realmente aleatoria con una ventaja mayor que $\varepsilon$.

Las matemáticas son preciosas. Para un fuzzy extractor con parámetros $(m, \ell, t, \varepsilon)$:

- $m$ = min-entropía de tu entrada biométrica **bits de impredecibilidad**
- $\ell$ = longitud de la clave extraída `R`
- $t$ = tolerancia a errores **qué tan distinto puede ser `w'` de `w`**
- $\varepsilon$ = distancia estadística respecto a la uniforme **parámetro de seguridad**

## Cómo funciona esto en realidad

La construcción usa dos primitivas que trabajan juntas:

### Secure Sketches $(SS, Rec)$

Un secure sketch es como un fuzzy commitment. $SS(w) \rightarrow s$ devuelve un sketch `s` que revela la información justa para corregir errores, pero ni de lejos la suficiente para reconstruir el propio `w`. Luego $Rec(w', s) \rightarrow w$ recupera el `w` original a partir de un `w'` con ruido usando el sketch.

Para strings binarios con distancia de Hamming, la Syndrome Construction es óptima:

```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
```

Lo que está pasando aquí:

- `sketch()` calcula el síndrome de la biometría `w` y devuelve `s` de `$n-k$` bits
- `recover()` corrige los errores del `w_prime` con ruido usando el sketch `s`
- La pérdida de entropía es exactamente la longitud del sketch, `$n-k$` bits
- Para el código `BCH` extendido $(128, 64, 22)$ **códigos Bose–Chaudhuri–Hocquenghem**: pierdes 64 bits y corriges hasta $t = \lfloor(22-1)/2\rfloor = 10$ errores

### Ahora, de sketch a extractor

Después de la corrección de errores, hasheas la biometría recuperada para extraer una clave uniforme, por ejemplo:

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

La función `fuzzy_gen()`:

- Crea el sketch a partir de la biometría
- Genera una semilla aleatoria para la función hash
- Extrae la clave usando `hash(biometric || seed)`
- Devuelve `key, helper`, donde helper = `sketch, seed`

La función `fuzzy_rep()`:

- Recupera la biometría original a partir del escaneo con ruido usando el sketch
- Aplica la misma función hash con la misma semilla
- ¡Produce exactamente la misma clave!

Para las biometrías típicas:

- Iris: código en bruto de 2048 bits **~249 bits de entropía real**, 10-30% de error → extrae **claves de 40-105 bits**
- Huella dactilar: 20-80 minucias **~50-150 bits**, mucho ruido → extrae **claves de 8-40 bits**
- Cara: embedding de 512 dimensiones **20-60 bits de entropía efectiva**

> Nota: Las representaciones biométricas en bruto contienen redundancia y correlaciones. Los códigos de iris muestran una proporción de ~8:1 entre los bits en bruto (2048) y la entropía en términos de teoría de la información (~249 bits). La extracción de claves debe respetar la entropía real, no el tamaño de la representación.

## La jugada de la identidad
![](https://i.ibb.co/MyZNpbG3/Screenshot-2025-10-21-at-4-02-19-PM.png)

El modelo de confianza de SSI tiene tres actores:

- Titular de la identidad - Tú, con una wallet que contiene credenciales
- Emisor - Fuente de confianza (gobierno, universidad, proveedor de KYC)
- Verificador - Consumidor de confianza (dApps, contratos inteligentes)
- Confianza en el mundo real - Puente entre el emisor y el verificador

El flujo completo:
**Biometría → Wallet → Credenciales → Pruebas ZK → Identidad verificada**

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

Veámoslo paso a paso:

### De la biometría a la wallet

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

El proceso:

- Preprocesar la biometría → vector binario **código de iris, embedding facial, etc.**
- Ejecutar `fuzzy_gen()` → obtener una clave de 40-128 bits + el auxiliar público
- Convertir a un mnemónico `BIP39` → derivar la semilla de la wallet HD
- Derivar direcciones para BTC `P2WPKH`, ETH `EVM`, SOL `Ed25519`
- Devolver las direcciones + el string auxiliar + el salt **¡guárdalos todos públicamente!**

Para Solana en concreto es una **curva distinta**:

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

> Nota: Solana usa `Ed25519`, no `secp256k1`. Así que en tu implementación usa `ed25519-hd-key` para cumplir con `SLIP-0010` **coincide con `Phantom`, `Solflare`**.

### Obteniendo tus credenciales verificables

Tu wallet ya tiene un `DID`, pero para probar cualquier cosa necesitas unas Verifiable Credentials **VC**. Aquí es donde empieza el triángulo de confianza.
El emisor verifica tus documentos off-chain y emite una prueba firmada criptográficamente:

```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;
}
```
Lo que pasó:

- El emisor verifica tus documentos off-chain, por ejemplo pasaporte, selfie, etc.
- Emite una `VC` firmada `cryptographically` con tus afirmaciones
- La `VC` incluye la firma del `DID` del emisor, que prueba su autenticidad
- Se guarda localmente en tu wallet, no en la blockchain
- Varias `VCs` de distintos emisores: educación, empleo, KYC

El schema de la credencial define qué hay en la 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..."
  }
}
```
Ahora, cuando un verificador te pide **prueba que tienes más de 18**, generas una ZKP a partir de esta credencial.

### La capa de pruebas de conocimiento cero

Aquí es donde puedes probar tus datos personales, como **edad, ciudadanía, un título, dirección, fecha de nacimiento, etc.**, sin revelar esos datos sensibles.

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

Lo que se prueba:

- El usuario tiene más de `minAge` años
- La credencial está firmada por un emisor de confianza
- NO se revela: la edad real, la fecha de nacimiento, el nombre ni ningún dato PII

El circuito en 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;
}
```

Verificación on-chain - `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);
    }
}
```

El contrato:

- Verifica la prueba ZK criptográficamente
- Revisa las señales públicas **DID del emisor, umbral de edad**
- Marca al usuario como verificado
- Ejecuta la lógica de negocio **airdrop, acceso a una DAO, etc.**

Así que en este flujo puedes ver cómo tu biometría se convirtió en:

- **Tu wallet** - Multi-chain, non-custodial, sin seed phrase
- **Tu identidad** - Identificador descentralizado `DID` anclado a tu biometría
- **Tus credenciales** - Credenciales verificables `VC` de emisores de confianza
- **Tu privacidad** - Las pruebas de conocimiento cero `ZKP` no revelan nada sobre ti

Y esto no son varios sistemas separados - es UN solo flujo unificado. La generación de la wallet ocurre una sola vez durante el registro. El flujo de ZKP se puede usar cada vez que haga falta verificar la identidad. Piénsalo así: puedes usar tu wallet biométrica igual que cualquier otra wallet cripto para transacciones normales. Pero cuando una dApp necesita KYC o cualquier tipo de verificación basada en `PII` **como los casos de uso de `Polygon ID`**, entra en acción la capa ZKP para probar credenciales sin exponer tus datos.

## Algunos casos de uso posibles

1. **Pasaportes para agentes:**

Cada agente de IA puede tener un `DID` derivado de su biometría. Imagina un agente de trading con IA que tiene su propia wallet y credenciales verificadas:

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

El agente:

- Deriva su wallet de su `biometric` **hash de la red neuronal**
- Prueba su reputación **más de 1000 trades exitosos** vía `ZKP`
- Opera de forma autónoma con un historial verificado

`SingularityNET` está construyendo exactamente esto, un AI Agent Trust Registry donde los agentes prueban su autenticidad, seguridad y estado de auditoría usando credenciales verificables. El flujo `biometric-wallet-ZKP` permite que los agentes operen por su cuenta sin dejar de rendir cuentas.

2. **Capa de identidad para dApps:**

Un protocolo DeFi necesita KYC pero no quiere guardar datos de usuarios **responsabilidad regulatoria + un honeypot para hackers**:

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

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

El protocolo nunca ve tu pasaporte, tu nombre ni tu dirección. Cumplimiento logrado, privacidad preservada.

El mismo patrón sirve para:

- Votaciones en DAOs **prueba membresía, no identidad**
- Airdrops **prueba que eres humano, evita bots**
- Restricciones de edad **prueba que tienes 18+, no tu edad exacta**

3. **Login sin contraseña en 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);
}
```

Cada sitio recibe una clave derivada única. No se envía ninguna contraseña, no hay credenciales que robar con phishing.

4. **Pagos biométricos:**

Tu biometría autoriza la transacción:

```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` ya implementa esto: todas tus wallets se derivan por completo de tu biometría "**Tu cara**" y se aprovisionan wallets en cadenas EVM y tokens específicos, ¡todo con tu cara!

5. **Base de datos para prevenir estafas:**

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

Las dApps consultan antes de permitir el acceso. Los estafadores conocidos quedan bloqueados en toda la red y los que tienen un historial de actividad rara también quedan marcados, mientras que los integradores reciben una notificación de la actividad sospechosa pasada.

## Integraciones para desarrolladores

Construir con identidades biométricas necesita infraestructura. El ecosistema ofrece tres componentes centrales:

### Issuer Node

Las organizaciones corren issuer nodes self-hosted para emitir credenciales:

```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
```

Funcionalidades:

- Crear el `DID` del emisor
- Definir `schemas` de credenciales
- Emitir/revocar `VCs`
- Gestionar las conexiones de usuarios
- Transicionar el estado on-chain **Polygon**

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

**Ecosistema de herramientas**

- Schema Builder: Crea schemas de VC personalizados en `tools.privado.id/builder`
- Query Builder: Diseña consultas ZKP en `tools.privado.id/query-builder`
- Schema Explorer: Explora los `schemas` de la comunidad
- Marketplace: Descubre `issuers` de credenciales

## Algunos retos técnicos y sus soluciones

1: **Variabilidad biométrica**

Tu huella dactilar varía un 10-15% entre escaneos. Solución: los códigos `BCH` son óptimos para la corrección de errores a nivel de bit. Para un código de iris de 2048 bits **que contiene ~249 bits de entropía real** con un 10% de errores (~205 bits erróneos):

- Usa el código BCH $[2048, 1122, 341]$ sobre $GF(2^{11})$ - corrige hasta 170 bits erróneos
- Pérdida de entropía, $2048 - 1122 = 926$ bits (longitud del sketch)
- Entropía restante, $249 - (926 / 8.2) \approx 136$ bits de entropía efectiva
- Extrae una clave de 128 bits **conservador, tiene en cuenta la pérdida de entropía**

**Distinción crítica**: Los 2048 bits son la representación en BRUTO del código de iris **la salida del algoritmo de Daugman**. La entropía real en términos de teoría de la información es de solo ~249 bits por las correlaciones biológicas y la redundancia **proporción de ~8:1**.

2: **Envejecimiento de la plantilla**

Problema: Tu cara cambia con el tiempo **envejecimiento, peso, vello facial, etc.**

Solución: Registro periódico con migración de claves:

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

Vuelve a registrarte cada 2-3 años para la cara, cada 5 años para el iris, cada año para la voz, etc.

3: **Detección de vida**

Problema: Un atacante con tu foto no debería poder acceder a tu wallet.

Solución: Comprobaciones de vida durante la captura biométrica:

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

El Orb de WorldCoin hace esto con escaneos de iris: más de 25 comprobaciones de vida, imágenes 3D, cámaras infrarrojas + RGB. Funciona increíblemente bien - tasa de falsa aceptación $< 0.0001\%$.

4: **Privacidad del string auxiliar**

¿El `P` que expliqué antes filtra información biométrica? No. Según el teorema **Dodis et al.**, el atacante aprende como máximo $n-k$ bits **longitud del sketch**, y la clave `R` sigue siendo estadísticamente aleatoria. Para nuestro iris: aprende 408 bits, quedan 392 bits de entropía.

## ¿Está listo para producción?

Las piezas ya existen:

- Fuzzy Extractors: la librería `CompFE` (MIT), corre en móviles
- Pruebas ZK: BKey, Privado ID y otros ya están en mainnet
- Hardware: la wallet D'CENT, FaceID y los sensores de huella están por todas partes
- Estándares: W3C DIDs, VCs, WebAuthn
- Redes: BTC, ETH, Polygon y Solana soportan wallets HD **BIP32/39/44 desde 2013**

¿Qué falta? Integraciones...

## Ejemplo de implementación en producción

Así se ve usar este sistema de verdad en un entorno de producción

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

El flujo normal de la wallet y el flujo de identidad conviven. La ZKP entra en juego solo cuando hace falta una verificación, ya sea para KYC o para cualquier verificación basada en `PII`, como edad, ciudadanía, un título, dirección, fecha de nacimiento, etc. 

Tu biometría es tu clave, tu wallet es tu identidad en todas las cadenas soportadas y nunca tienes que preocuparte por perder tu seed phrase o tus claves privadas, todo está ligado a tus atributos físicos, ¡y en la implementación de BMoni es tu cara!

## Enlaces esenciales

- [Panorama de los fuzzy extractors](https://www.cs.bu.edu/~reyzin/papers/fuzzysurvey.pdf) - Dodis, Reyzin, Smith
- [Fuzzy Extractors (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) - Construcciones post-cuánticas
- [CompFE](https://github.com/benjaminfuller/CompFE)
- [Polygon ID SDK](https://github.com/0xPolygonID) - Sistema de identidad con ZKP
- [La seguridad de BMoni](https://bmoni.com/security/#22patents) - Nuestra arquitectura de seguridad
- [snarkjs](https://github.com/iden3/snarkjs) - Librería de ZK-SNARK
