Traduzido do artigo original em inglês. Ler em inglês

Os sistemas de identidade de hoje exigem que você confie demais nas empresas para guardar os seus dados com segurança. Vários provedores de KYC guardam os seus dados por tempo indeterminado. Cada app recebe todos os seus dados, mesmo quando só precisa saber que você tem mais de 18 anos, o que é informação demais para compartilhar e deixar espalhada por todo lado.
A Self-Sovereign Identity SSI resolve isso dando a você o controle das suas credenciais, você pode compartilhar só o que é necessário e provar afirmações sem revelar dados. basicamente, as verifiable credentials VC permitem que você prove que tem mais de 18 anos sem mostrar a sua data de nascimento exata. Mas a SSI tem um problema - onde você guarda as chaves privadas que controlam a sua identidade?
Existem várias soluções fundamentais para isso e uma delas é biometric crypto identities, que não é uma das mais populares, a pergunta é: e se a sua biometric, por exemplo o face, fosse a sua private-key? Nada de seed phrases para perder, nada de passwords para esquecer, nada de keys para guardar. A sua biometric gera a sua wallet, que guarda as suas credenciais verificáveis, que você prova usando ZKPs.
Isso é possível com fuzzy extractors + zero-knowledge proofs. Veja como funciona.
Os sistemas criptográficos tradicionais foram projetados para bits reproduzíveis. Só que a biometria quebra duas regras importantes das quais esses sistemas dependem:
As chaves precisam ser perfeitamente reproduzíveis - Eles exigem uma reprodução da chave perfeita bit a bit. A sua chave privada para assinar transações precisa ser idêntica todas as vezes. Mas a biometria é ruidosa, com muitas variáveis a considerar. A leitura da sua digital hoje não vai bater bit a bit com a leitura de ontem por causa de variações no sensor, posição do dedo, umidade, temperatura - a lista não acaba. O mesmo vale para leituras de rosto, padrões de íris ou qualquer medição biológica.
As chaves precisam ser uniformemente aleatórias - A segurança criptográfica parte do princípio de que a sua chave é sorteada de forma uniformemente aleatória dentre todo o espaço de chaves. Mas os dados biométricos têm estrutura e padrões. A sua íris não é uniformemente aleatória, ela tem restrições biológicas, semelhanças no nível da população e entropia concentrada em características específicas.
Isso cria o famoso problema do ovo e da galinha: você não pode usar a biometria diretamente como chave ela é muito ruidosa, você não pode guardá-la com segurança ela é PII e imutável, e você não pode simplesmente fazer o hash dela o ruído quebra as funções de hash, como você sabe. Então, o que fazer?
Agora vem a parte boa!
Pense nos fuzzy extractors como uma criptografia com correção de erros para dados ruidosos. Eles são um par de algoritmos, Gen gerar e Rep reproduzir, que fazem algo bem empolgante:
- Recebe a sua entrada biométrica ruidosa w e gera uma string uniformemente aleatória R a chave criptográfica de fato e uma string auxiliar pública P. Aqui vai outro fato curioso: P pode ser publicada abertamente sem comprometer a segurança. Ela não revela absolutamente nenhuma informação relevante sobre a sua biometria.
- Recebe uma leitura biométrica diferente w' e a mesma string auxiliar pública P, e reproduz exatamente o mesmo R, desde que w' esteja perto o suficiente do w original. Eu sei que isso assusta, mas neste caso perto o suficiente é medido pela distância em algum espaço métrico distância de Hamming para strings binárias, distância de edição para sequências, diferença de conjuntos para vetores de características.
A garantia de segurança é da teoria da informação, mesmo com P em mãos, a chave extraída R é -próxima de uniforme desde que a sua biometria tenha min-entropy . Não é preciso nenhuma suposição de dificuldade computacional. Um adversário com poder computacional infinito ainda não consegue distinguir R de uma chave realmente aleatória com vantagem maior que .
A matemática é linda. Para um fuzzy extractor com parâmetros :
Rw' pode ser diferente de wA construção usa duas primitivas trabalhando juntas:
Um secure sketch é como um fuzzy commitment. gera um sketch s que revela informação suficiente só para corrigir erros, mas nem de longe o suficiente para reconstruir o próprio w. Depois, recupera o w original a partir de um w' ruidoso usando o sketch.
Para strings binárias com distância de Hamming, a Syndrome Construction é ótima:
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) % 2O que está acontecendo aqui:
sketch() calcula a síndrome da biometria w e gera s com $n-k$ bitsrecover() corrige os erros no w_prime ruidoso usando o sketch s$n-k$ bitsBCH estendido códigos Bose–Chaudhuri–Hocquenghem: perde 64 bits e corrige até errosDepois da correção de erros, você faz o hash da biometria recuperada para extrair uma chave uniforme, por exemplo:
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)
}A função fuzzy_gen():
hash(biometric || seed)key, helper, em que helper = sketch, seedA função fuzzy_rep():
Para as biometrias mais comuns:
Nota: As representações biométricas brutas têm redundância e correlações. Os códigos de íris mostram uma proporção de ~8:1 entre os bits brutos (2048) e a entropia da teoria da informação (~249 bits). A extração de chaves precisa respeitar a entropia real, e não o tamanho da representação.

O modelo de confiança da SSI tem três atores:
O fluxo completo: Biometria → Carteira → Credenciais → Provas ZK → Identidade verificada

Vamos ver passo a passo:
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
};
}O processo:
fuzzy_gen() → obter uma chave de 40-128 bits + a string auxiliar públicaBIP39 → derivar a seed da carteira HDP2WPKH, ETH EVM, SOL Ed25519No caso específico da Solana, é uma curva diferente:
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: A Solana usa
Ed25519, e nãosecp256k1. Então, na sua implementação, useed25519-hd-keypara ter compatibilidade comSLIP-0010igual aPhantom,Solflare.
A sua carteira agora tem um DID, mas para provar qualquer coisa, você precisa de uma Verifiable Credential VC. É aqui que começa o triângulo de confiança.
O issuer verifica os seus documentos off-chain e emite uma prova assinada criptograficamente:
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;
}O que aconteceu:
VC assinada de forma cryptographically com as suas afirmaçõesVC inclui a assinatura do DID do issuer, que prova a autenticidadeVCs de issuers diferentes: educação, emprego, KYCO schema da credencial define o que vai na VC:
{
"@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..."
}
}Agora, quando um verifier pede prove que você tem mais de 18 anos, você gera uma ZKP a partir dessa credencial.
É aqui que você consegue provar os seus dados pessoais, como idade, cidadania, um diploma, endereço, data de nascimento, etc., sem revelar esses dados sensíveis.
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 };
}O que é provado:
minAge anosO circuito em 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;
}Verificação on-chain - 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);
}
}O contrato:
Então, por esse fluxo, dá para ver como a sua biometria se tornou:
DID ancorado na sua biometriaVC de issuers confiáveisZKP que não revelam nada sobre vocêE isso não são vários sistemas separados - é UM fluxo unificado. A geração da carteira acontece uma vez, durante o cadastro. O fluxo de ZKP pode ser usado sempre que for preciso verificar a identidade. Pense assim: você pode usar a sua carteira biométrica como qualquer outra carteira cripto para transações normais. Mas quando um dApp precisa de KYC ou de qualquer tipo de verificação baseada em PII como nos casos de uso do Polygon ID, a camada de ZKP entra em ação para provar as credenciais sem expor os seus dados.
Todo agente de IA pode ter um DID derivado de biometria. Imagine um agente de IA de trading com a sua própria carteira e credenciais verificadas:
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);
}
}O agente:
biometric hash da rede neuralZKPA SingularityNET está construindo exatamente isso, um AI Agent Trust Registry em que os agentes provam autenticidade, segurança e status de auditoria usando verifiable credentials. O fluxo biometric-wallet-ZKP permite que os agentes operem por conta própria e ainda assim prestem contas.
Um protocolo DeFi precisa de KYC, mas não quer guardar os dados dos usuários responsabilidade regulatória + honeypot para hackers:
contract DeFiProtocol {
modifier onlyKYCVerified() {
require(identityGate.verified(msg.sender, KYC_ID), "KYC required");
_;
}
function borrowUndercollateralized(uint256 amount) external onlyKYCVerified {
_issueLoan(msg.sender, amount);
}
}O protocolo nunca vê o seu passaporte, nome ou endereço. Compliance garantido, privacidade preservada.
O mesmo padrão serve para:
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 site recebe uma chave derivada única. Nenhuma senha é enviada, nenhuma credencial para cair em phishing.
A sua biometria autoriza a transação:
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 });
}O BMONI + BKey já implementa isso hoje, as suas carteiras são todas derivadas totalmente da sua biometria "O seu rosto" e provisionadas em chains EVM e tokens específicos, tudo pelo seu rosto!
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);
}
}Os dApps fazem a consulta antes de liberar o acesso. Golpistas conhecidos são bloqueados em toda a rede e alguns com histórico de atividade estranha também são sinalizados, enquanto os integradores recebem uma notificação de atividades suspeitas anteriores.
Construir com identidades biométricas exige infraestrutura. O ecossistema oferece três componentes principais:
As organizações rodam issuer nodes self-hosted para emitir credenciais:
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:3001Funcionalidades:
DID do issuerschemas de credenciaisVCsExemplo de API:
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 }
});Ecossistema de ferramentas
tools.privado.id/buildertools.privado.id/query-builderschemas da comunidadeissuers de credenciais1: Variabilidade biométrica
A sua digital varia 10-15% entre uma leitura e outra. Solução: os códigos BCH são ótimos para correção de erros no nível de bits. Para um código de íris de 2048 bits que contém ~249 bits de entropia real com 10% de erros (~205 bits com erro):
Distinção crítica: Os 2048 bits são a representação BRUTA do código de íris a saída do algoritmo de Daugman. A entropia real, do ponto de vista da teoria da informação, é de só ~249 bits por causa das correlações biológicas e da redundância proporção de ~8:1.
2: Envelhecimento do template
Problema: O seu rosto muda com o tempo envelhecimento, peso, barba, etc.
Solução: Recadastro periódico com migração de chave:
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);
}Refaça o cadastro a cada 2-3 anos para rosto, a cada 5 anos para íris, todo ano para voz etc.
3: Detecção de vivacidade
Problema: Atacantes com uma foto sua não deveriam conseguir acessar a sua carteira.
Solução: Checagens de vivacidade durante a captura biométrica:
const checks = await Promise.all([
depthSensor.measure(),
motionDetector.verify(['blink', 'nod']),
thermalCamera.capture(),
videoAnalyzer.checkTemporal()
]);O Orb da WorldCoin faz isso com leituras de íris: mais de 25 checagens de vivacidade, imagens 3D, câmeras infravermelho + RGB. Funciona incrivelmente bem - taxa de falsa aceitação .
4: Privacidade da string auxiliar
O P que expliquei antes vaza informação biométrica? Não. Pelo teorema Dodis et al., o atacante aprende no máximo bits o tamanho do sketch, e a chave R continua estatisticamente aleatória. Para a nossa íris: ele aprende 408 bits, e restam 392 bits de entropia.
As peças já existem:
CompFE (MIT), que roda em mobileO que está faltando? Integrações...
Veja como fica usar esse sistema de verdade em um ambiente de produção
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 });
}O fluxo normal da carteira e o fluxo de identidade coexistem. A ZKP só entra em cena quando é preciso verificar algo, seja para KYC ou para qualquer verificação baseada em PII, como idade, cidadania, um diploma, endereço, data de nascimento, etc.
A sua biometria é a sua chave, a sua carteira é a sua identidade em todas as chains suportadas e você nunca precisa se preocupar em perder a sua seed phrase ou as suas chaves privadas, tudo está ligado aos seus atributos físicos, e na implementação da BMoni é o seu rosto!
Receba os artigos mais recentes no seu e-mail.