Traduit de l'article original en anglais. Lire en anglais

Les systèmes d'identité actuels nous obligent à faire énormément confiance aux entreprises pour stocker nos données de manière sécurisée. De nombreux fournisseurs de KYC conservent vos données indéfiniment. Chaque app reçoit l'intégralité de vos informations, même quand elle a seulement besoin de savoir que vous avez plus de 18 ans, ce qui fait beaucoup trop d'informations partagées et éparpillées un peu partout.
L'identité auto-souveraine (Self-Sovereign Identity) SSI règle ce problème en vous donnant le contrôle de vos credentials, vous pouvez partager uniquement ce qui est nécessaire et prouver des affirmations sans révéler de données. en gros, les verifiable credentials VC vous permettent de prouver que vous avez plus de 18 ans sans montrer votre date de naissance exacte. Mais la SSI a un problème - où stocker les clés privées qui contrôlent votre identité ?
Il existe beaucoup de solutions de fond à ce problème, et l'une d'elles est celle des biometric crypto identities, qui ne fait pas partie des plus populaires, la question est la suivante : et si votre biometric, par exemple votre face, était votre private-key ? Pas de seed phrases à perdre, pas de passwords à oublier, pas de keys à stocker. Votre biometric génère votre wallet, qui contient vos verifiable credentials, que vous prouvez à l'aide de ZKP.
C'est possible grâce aux fuzzy extractors + zero-knowledge proofs. Voici comment ça marche.
Les systèmes cryptographiques traditionnels ont été conçus pour des bits reproductibles. Mais la biométrie enfreint deux règles essentielles sur lesquelles reposent ces systèmes :
Les clés doivent être parfaitement reproductibles - Ces systèmes exigent une reproduction des clés exacte au bit près. Votre clé privée pour signer des transactions doit être identique à chaque fois. Mais la biométrie est bruitée, il y a beaucoup de variables à prendre en compte. Le scan de votre empreinte digitale aujourd'hui ne correspondra pas bit pour bit à celui d'hier, à cause des variations du capteur, du placement du doigt, de l'humidité, de la température - et la liste est encore longue. Même chose pour les scans du visage, les motifs de l'iris ou n'importe quelle mesure biologique.
Les clés doivent être uniformément aléatoires - La sécurité cryptographique suppose que votre clé est tirée uniformément au hasard dans tout l'espace des clés. Mais les données biométriques ont une structure et des motifs. Votre iris n'est pas uniformément aléatoire, il est soumis à des contraintes biologiques, présente des similarités à l'échelle de la population, et son entropie est concentrée dans certaines caractéristiques précises.
Cela crée le problème classique de l'œuf et de la poule : vous ne pouvez pas utiliser directement la biométrie comme clé elle est très bruitée, vous ne pouvez pas la stocker de manière sécurisée ce sont des PII, et elles sont immuables, et vous ne pouvez pas simplement la hacher le bruit casse les fonctions de hachage, comme vous le savez. Alors, que faire ?
Et c'est là que ça devient intéressant !
Voyez les fuzzy extractors comme de la cryptographie à correction d'erreurs pour les données bruitées. Il s'agit d'une paire d'algorithmes, Gen générer et Rep reproduire, qui font quelque chose de vraiment passionnant :
- Prend votre entrée biométrique bruitée w et produit une chaîne uniformément aléatoire R la vraie clé cryptographique ainsi qu'une chaîne d'aide publique P. Et voici un autre fait amusant : P peut être publiée ouvertement sans compromettre la sécurité. Elle ne révèle absolument aucune information utile sur votre biométrie.
- Prend une autre lecture biométrique w' et la même chaîne d'aide publique P, puis reproduit exactement la même R tant que w' est suffisamment proche de la w d'origine. Je sais que ça peut faire peur, mais ici, suffisamment proche se mesure par une distance dans un certain espace métrique distance de Hamming pour les chaînes binaires, distance d'édition pour les séquences, différence d'ensembles pour les vecteurs de caractéristiques.
La garantie de sécurité relève de la théorie de l'information : même en connaissant P, la clé extraite R est -proche de l'uniforme tant que votre biométrie a une min-entropie . Aucune hypothèse de difficulté calculatoire n'est nécessaire. Un adversaire doté d'une puissance de calcul infinie ne peut toujours pas distinguer R d'une clé réellement aléatoire avec un avantage supérieur à .
Les mathématiques sont magnifiques. Pour un fuzzy extractor de paramètres :
Rw' peut différer de wLa construction utilise deux primitives qui travaillent ensemble :
Un secure sketch ressemble à un engagement flou (fuzzy commitment). produit un sketch s qui révèle juste assez d'informations pour corriger les erreurs, mais loin d'être assez pour reconstruire w elle-même. Ensuite, retrouve la w d'origine à partir d'une w' bruitée, grâce au sketch.
Pour les chaînes binaires avec la distance de Hamming, la Syndrome Construction est optimale :
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) % 2Ce qui se passe ici :
sketch() calcule le syndrome de la biométrie w et produit s, soit $n-k$ bitsrecover() corrige les erreurs de la w_prime bruitée à l'aide du sketch s$n-k$ bitsBCH étendu codes de Bose–Chaudhuri–Hocquenghem : on perd 64 bits, et on corrige jusqu'à erreursAprès la correction d'erreurs, on hache la biométrie retrouvée pour en extraire une clé uniforme, par exemple
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 fonction fuzzy_gen() :
hash(biometric || seed)key, helper, où helper = sketch, seedLa fonction fuzzy_rep() :
Pour les biométries courantes :
Note : Les représentations biométriques brutes contiennent de la redondance et des corrélations. Les codes d'iris présentent un ratio de ~8:1 entre les bits bruts (2048) et l'entropie au sens de la théorie de l'information (~249 bits). L'extraction de clé doit respecter l'entropie réelle, pas la taille de la représentation.

Le modèle de confiance de la SSI compte trois acteurs :
Le flux complet : Biométrie → Wallet → Credentials → Preuves ZK → Identité vérifiée

Voyons cela étape par étape :
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
};
}Le processus :
fuzzy_gen() → obtenir une clé de 40 à 128 bits + une chaîne d'aide publiqueBIP39 → dériver la seed du wallet HDP2WPKH, ETH EVM, SOL Ed25519Pour Solana en particulier, c'est une courbe différente :
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();Note : Solana utilise
Ed25519, passecp256k1. Dans votre implémentation, utilisez donced25519-hd-keypour être conforme àSLIP-0010correspond àPhantom,Solflare.
Votre wallet a maintenant un DID, mais pour prouver quoi que ce soit, il vous faut un Verifiable Credential VC. C'est là que commence le triangle de confiance.
L'émetteur vérifie vos documents off-chain et émet une preuve signée cryptographiquement :
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;
}Ce qui s'est passé :
VC signé cryptographically avec vos affirmationsVC inclut la signature du DID de l'émetteur, qui en prouve l'authenticitéVCs provenant de différents émetteurs : formation, emploi, KYCLe schéma du credential définit ce que contient le 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..."
}
}Désormais, quand un vérificateur vous demande de prouver que vous avez plus de 18 ans, vous générez une ZKP à partir de ce credential.
C'est ici que vous pouvez prouver vos informations personnelles, comme l'âge, la citoyenneté, un diplôme, l'adresse, la date de naissance, etc., sans révéler ces données sensibles.
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 };
}Ce qui est prouvé :
minAge ansLe circuit 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;
}Vérification 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);
}
}Le contrat :
À partir de ce flux, vous voyez donc comment votre biométrie est devenue :
DID ancré à votre biométrieVC provenant d'émetteurs de confianceZKP qui ne révèlent rien sur vousEt il ne s'agit pas de plusieurs systèmes séparés - c'est UN seul flux unifié. La génération du wallet a lieu une seule fois, lors de l'enrôlement. Le flux ZKP peut être utilisé chaque fois qu'une vérification d'identité est nécessaire. Voyez-le ainsi : vous pouvez utiliser votre wallet biométrique comme n'importe quel autre wallet crypto pour vos transactions habituelles. Mais quand une dApp a besoin d'un KYC ou de n'importe quel type de vérification basée sur des PII comme les cas d'usage de Polygon ID, la couche ZKP entre en jeu pour prouver vos credentials sans exposer vos données.
Chaque agent IA peut avoir un DID dérivé d'une biométrie. Imaginez un agent IA de trading avec son propre wallet et des credentials vérifiés :
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);
}
}L'agent :
biometric hash du réseau de neuronesZKPSingularityNET construit exactement ça, un AI Agent Trust Registry où les agents prouvent leur authenticité, leur sûreté et leur statut d'audit à l'aide de verifiable credentials. Le flux biometric-wallet-ZKP permet aux agents d'opérer de manière autonome tout en restant responsables de leurs actes.
Un protocole DeFi a besoin du KYC mais ne veut pas stocker les données des utilisateurs responsabilité réglementaire + pot de miel pour les hackers :
contract DeFiProtocol {
modifier onlyKYCVerified() {
require(identityGate.verified(msg.sender, KYC_ID), "KYC required");
_;
}
function borrowUndercollateralized(uint256 amount) external onlyKYCVerified {
_issueLoan(msg.sender, amount);
}
}Le protocole ne voit jamais votre passeport, votre nom ni votre adresse. Conformité assurée, vie privée préservée.
Le même schéma s'applique pour :
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);
}Chaque site obtient une clé dérivée unique. Aucun mot de passe envoyé, aucun identifiant à hameçonner.
Votre biométrie autorise la transaction :
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 + BKey implémente actuellement ce principe : vos wallets sont entièrement dérivés de votre biométrie « votre visage » et provisionnés sur certaines chaînes EVM et certains tokens, le tout via votre visage !
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);
}
}Les dApps l'interrogent avant d'accorder l'accès. Les arnaqueurs connus sont bloqués sur tout le réseau et ceux qui ont un historique d'activité étrange sont également signalés, tandis que les intégrateurs reçoivent une notification de l'activité suspecte passée.
Construire avec des identités biométriques nécessite de l'infrastructure. L'écosystème fournit trois composants principaux :
Les organisations font tourner des issuer nodes auto-hébergés pour émettre des credentials :
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:3001Fonctionnalités :
DID d'émetteurschemas de credentialsVCsExemple d'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 }
});Écosystème d'outils
tools.privado.id/buildertools.privado.id/query-builderschemas de la communautéissuers de credentials1 : Variabilité biométrique
Votre empreinte digitale varie de 10 à 15 % d'un scan à l'autre. Solution : les codes BCH sont optimaux pour la correction d'erreurs au niveau du bit. Pour un code d'iris de 2048 bits qui contient ~249 bits d'entropie réelle avec 10 % d'erreurs (~205 bits erronés) :
Distinction essentielle : les 2048 bits correspondent à la représentation BRUTE du code d'iris sortie de l'algorithme de Daugman. L'entropie réelle, au sens de la théorie de l'information, n'est que de ~249 bits, à cause des corrélations biologiques et de la redondance ratio de ~8:1.
2 : Vieillissement du modèle biométrique
Problème : votre visage change avec le temps vieillissement, poids, pilosité faciale, etc.
Solution : un ré-enrôlement périodique avec migration des clés :
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);
}Ré-enrôlez-vous tous les 2-3 ans pour le visage, tous les 5 ans pour l'iris, chaque année pour la voix, etc.
3 : Détection du vivant
Problème : un attaquant qui possède votre photo ne devrait pas pouvoir accéder à votre wallet.
Solution : des contrôles de détection du vivant lors de la capture biométrique :
const checks = await Promise.all([
depthSensor.measure(),
motionDetector.verify(['blink', 'nod']),
thermalCamera.capture(),
videoAnalyzer.checkTemporal()
]);L'Orb de WorldCoin fait cela avec des scans de l'iris : plus de 25 contrôles de détection du vivant, de l'imagerie 3D, des caméras infrarouges + RVB. Ça fonctionne incroyablement bien - taux de fausse acceptation .
4 : Confidentialité de la chaîne d'aide
Est-ce que P, expliquée plus haut, fait fuiter des informations biométriques ? Non. D'après le théorème Dodis et al., l'attaquant apprend au plus bits la longueur du sketch, et la clé R reste statistiquement aléatoire. Pour notre iris : il apprend 408 bits, il reste 392 bits d'entropie.
Les briques existent déjà :
CompFE (MIT), qui tourne sur mobileQu'est-ce qui manque ? Les intégrations...
Voici à quoi ressemble concrètement l'utilisation de ce système dans un environnement de production
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 });
}Le flux de wallet classique et le flux d'identité coexistent. La ZKP n'intervient que lorsqu'une vérification est nécessaire, que ce soit pour le KYC ou pour toute vérification basée sur des PII, comme l'âge, la citoyenneté, un diplôme, l'adresse, la date de naissance, etc.
Votre biométrie est votre clé, votre wallet est votre identité sur toutes les chaînes prises en charge, et vous n'avez plus jamais à vous soucier de perdre votre seed phrase ou vos clés privées, tout est lié à vos attributs physiques, et dans l'implémentation de BMoni, c'est votre visage !
Recevez les derniers articles dans votre boîte mail.