This document outlines the security properties, threat model, and mitigations implemented in Uniquity.
- Security Properties
- Threat Model
- Attack Vectors & Mitigations
- Trust Assumptions
- FHE Security
- Cryptographic Choices
- Known Limitations
Uniquity provides the following security guarantees:
| Property | Guarantee |
|---|---|
| Confidentiality | Biometric embeddings are never revealed to any party |
| No Linkability | Cannot link verifications across platforms |
| No Reversibility | Cannot reconstruct face from stored data |
| Property | Guarantee |
|---|---|
| End-to-End Encryption | Only admin can read submission content |
| Key Confidentiality | AES keys protected by FHE |
| Unlinkability | Cannot link submissions without decryption |
| Property | Guarantee | Status |
|---|---|---|
| Uniqueness | One verification per biometric identity | π v2 |
| Non-Transferability | Credentials bound to wallet address | β v1 |
| Temporal Validity | Verification persists until revoked | β v1 |
| Encrypted Storage | Biometrics stored encrypted for future comparison | β v1 |
Note: In v1 (current), users are verified instantly. Uniqueness comparison will be enforced in v2 using off-chain FHE computation. Encrypted embeddings are stored now to enable this future comparison.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β THREAT ACTORS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β Malicious User β β Network Observerβ β Malicious Admin β β
β β βββββββββββββ β β βββββββββββββ β β βββββββββββββ β β
β β β’ Sybil attack β β β’ Traffic β β β’ Data leak β β
β β (v2 mitigated)β β analysis β β β’ False reject β β
β β β’ Replay attackβ β β’ Metadata β β β’ Collusion β β
β β β’ Impersonationβ β β β β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β Comparison Svc β β Compromised Nodeβ β State Adversary β β
β β (v2) β β βββββββββββββ β β βββββββββββββ β β
β β βββββββββββββ β β β’ Data extract β β β’ Coercion β β
β β β’ Wrong result β β β’ Manipulation β β β’ Backdoor β β
β β β’ Denial β β β’ Censorship β β β’ Surveillance β β
β β β’ Collusion β β β β β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Biometric Embeddings - Most sensitive; never to be revealed
- Submission Content - Business sensitive; admin-only access
- AES Keys - Enables decryption; must be FHE-protected
- Verification Status - Public but manipulation-resistant
- User Privacy - Pseudonymity and unlinkability
Vector: Creating multiple accounts with same biometric
Attacker βββΆ Extract face embedding
βββΆ Modify slightly
βββΆ Attempt multiple verifications
v1 Status: Not mitigated (instant verification without comparison)
v2 Mitigation:
- Manhattan distance computed on encrypted values (off-chain)
- Threshold (400,000) calibrated for reasonable variation
- Cannot test distance before submission (encrypted)
- Comparison service computes FHE distance without seeing plaintext
// v2: Distance comparison happens on encrypted values (off-chain)
// Service submits encrypted minDistance result
// User cannot manipulate the comparisonVector: Reusing captured biometric data
Attacker βββΆ Capture victim's face image
βββΆ Submit as own verification
Mitigation:
- Liveness detection in face-api.js (blink, movement)
- Input proof tied to specific user address
- Timestamp validation on-chain
// Input is bound to user address
const input = fhevmInstance.createEncryptedInput(
contractAddress,
userAddress // Proof is specific to this address
);Vector: Submitting fake cleartext values with forged proof
Attacker βββΆ Observe publicDecrypt result
βββΆ Modify cleartext (e.g., set distance = MAX_INT)
βββΆ Submit fake value with original proof
Mitigation:
FHE.checkSignatures()cryptographically binds cleartext to ciphertext- KMS signatures cannot be forged without Zama's private keys
- Proof is only valid for exact (handle, cleartext) pair
- Order of handles matters - proof is order-dependent
function finalizeVerification(
uint256 requestId,
uint64 clearDistance,
bytes calldata decryptionProof
) external {
bytes32[] memory handles = new bytes32[](1);
handles[0] = req.minDistanceHandle;
bytes memory abiEncodedClear = abi.encode(clearDistance);
// Reverts if proof doesn't match (clearDistance, handle) pair
FHE.checkSignatures(handles, abiEncodedClear, decryptionProof);
// Only reaches here if proof is valid
// ...
}Vector: Observing pending verification and racing to register
Attacker βββΆ Monitor mempool for proveHumanity()
βββΆ Extract encrypted values (cannot decrypt)
βββΆ ??? (no viable attack)
Mitigation:
- Encrypted values cannot be reused (bound to address)
- Even with same face, attacker's proof would be different
- v2: Profile comparison happens against all existing profiles
Vector: Admin private key stolen, all submissions decrypted
Attacker βββΆ Steal admin key
βββΆ Call getSubmissionKeyHandle()
βββΆ userDecrypt all keys
βββΆ Decrypt all submissions
Mitigation:
- Per-campaign admin isolation
- Compromise affects only that campaign's submissions
- Cannot retroactively access other campaigns
- Recommended: Use multisig for admin
Vector: Extracting biometric data from on-chain storage
Attacker βββΆ Read contract storage
βββΆ Extract euint64 chunks
βββΆ ??? Decrypt ???
Mitigation:
- Values are FHE-encrypted with network key
- Only Zama KMS can decrypt
- No party (including contract) can read plaintext
- Chunks are meaningless without decryption
Vector: Correlating on-chain activity with identity
Observer βββΆ Track wallet addresses
βββΆ Correlate verification timing
βββΆ Link to real identity
Mitigation:
- Use fresh wallet for verification
- Verification doesn't reveal which campaign
- Submission content is encrypted
Vector: Malicious comparison service returns wrong distance
Attacker βββΆ Run compromised comparison service
βββΆ Always return MAX_INT (everyone unique)
βββΆ Or return 0 (everyone duplicate)
Mitigation:
- Service cannot see decrypted biometrics (FHE)
- All operations emit events for audit trail
- Multiple services can verify each other
- Future: ZK proofs of correct computation
- Future: Zama coprocessor with cryptographic guarantees
| Component | Trust Assumption | Version |
|---|---|---|
| Zama KMS | Correctly signs decryption proofs; won't forge signatures | v1, v2 |
| Zama Relayer | Faithfully relays decryption requests to KMS | v1, v2 |
| FHE Security | TFHE encryption is computationally secure | v1, v2 |
| checkSignatures | Correctly verifies KMS signatures on-chain | v2 |
| face-api.js | Provides accurate, consistent embeddings | v1, v2 |
| Browser | Client-side code executes correctly | v1, v2 |
| IPFS | Data availability (not confidentiality) | v1, v2 |
| Comparison Service | Correctly computes FHE distance (auditable) | v2 |
| Component | Why |
|---|---|
| Blockchain Nodes | Can't see encrypted data |
| Contract Owner | No admin keys for Core contract |
| Campaign Admins | Can only decrypt their campaign's submissions |
| Other Users | Isolated by FHE permissions |
| Network | All sensitive data encrypted in transit |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TRUST HIERARCHY β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β MAXIMUM TRUST β
β βββββββββββββ β
β Zama FHE Network (KMS, Relayer, Coprocessor) β
β β’ KMS signs decryption proofs β
β β’ checkSignatures verifies on-chain β
β β β
β βΌ β
β HIGH TRUST (v2 only) β
β βββββββββββββ β
β Comparison Service (off-chain FHE computation) β
β β’ Computes encrypted distance β
β β’ Cannot see plaintext (FHE) β
β β’ Auditable via event logs β
β β β
β βΌ β
β MODERATE TRUST β
β ββββββββββββββ β
β User's Browser (executes client code) β
β β’ Calls publicDecrypt off-chain (v2) β
β β’ Submits proof on-chain (v2) β
β β β
β βΌ β
β MINIMAL TRUST β
β βββββββββββββ β
β Blockchain (can't see encrypted data) β
β IPFS (encrypted storage) β
β Campaign Admins (isolated access) β
β β β
β βΌ β
β ZERO TRUST β
β ββββββββββ β
β Other Users β
β Network Observers β
β Contract Storage Readers β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Uniquity uses Zama's fhEVM which implements FHE (Fully Homomorphic Encryption):
| Parameter | Value |
|---|---|
| Security Level | 128-bit |
| Key Size | Network-managed |
| Ciphertext Expansion | ~8x |
| Supported Types | euint8, euint16, euint32, euint64, euint256 |
All FHE operations maintain semantic security:
// These operations never reveal plaintext
euint64 sum = FHE.add(a, b); // a, b, sum all encrypted
ebool lt = FHE.lt(a, b); // Result is encrypted boolean
euint64 result = FHE.select(lt, a, b); // Encrypted selectionFHE permissions control who can decrypt:
// Grant permissions (critical for security)
FHE.allow(encryptedKey, campaign.admin); // Admin can decrypt
FHE.allowThis(encryptedKey); // Contract can operate
FHE.allow(encryptedKey, msg.sender); // User can edit
// Without permission, decryption fails
// Even contract owner cannot bypass permissions| Algorithm | Use Case | Rationale |
|---|---|---|
| FHE | Biometric storage & comparison (v2) | Enables encrypted computation |
| AES-256-GCM | Submission encryption | Fast, authenticated encryption |
| SHA3-256 | CID hashing | Collision resistance for validation |
Converting float32 embeddings to FHE-compatible format:
// Original: 128 Γ float32 = 4096 bits
// Quantized: 4 Γ uint64 = 256 bits
// Compression: 16x (with acceptable precision loss)
// Quantization preserves relative distances
// Tests show >95% correlation with original distancesSIMILARITY_THRESHOLD = 400,000
Calibrated to balance:
βββ False Positive Rate (same person rejected): < 1%
βββ False Negative Rate (different person accepted): < 0.1%
βββ Lighting/angle tolerance: Β±15 degrees, Β±20% brightness
Bucket-based comparison reduces:
βββ Comparison scope: O(n/8) vs O(n)
βββ Still maintains security (compare within bucket)
-
No Uniqueness Enforcement
- Users verified instantly without comparison
- Same person can verify with multiple wallets
- Mitigation: Encrypted embeddings stored for v2 comparison
-
Liveness Detection
- Browser-based, could be spoofed with effort
- Mitigation: Threshold distance catches most spoofs (v2)
-
KMS Trust
- Decryption proofs rely on Zama KMS signatures
- Mitigation:
FHE.checkSignatures()cryptographically verifies
-
Comparison Service Trust
- Service must be trusted to compute correctly
- Cannot see encrypted data (FHE protected)
- Mitigation: Audit logs, multiple services, future ZK proofs
-
Replay Protection
- Same proof could theoretically be resubmitted
- Mitigation: Track finalized requests; reject duplicates
-
Bucket Collision
- Users in same bucket compared together
- Mitigation: 8 buckets distribute load evenly
-
Comparison Latency
- Off-chain FHE computation takes time (~10-30 seconds)
- Mitigation: Async flow with status updates
-
Multi-Factor Verification
- Add voice embedding
- Combine multiple biometrics
-
Decentralized Comparison
- Replace single service with MPC network
- Or use Zama coprocessor when available
- No single point of trust
-
ZK Proofs of Computation
- Service generates ZK proof of correct FHE execution
- Fully trustless comparison
-
Revocation Mechanism
- Allow users to revoke verification
- Re-verify with updated biometrics
-
Cross-Chain Verification
- Portable credentials across chains
- Bridge verified status
| Component | Status | Notes |
|---|---|---|
| UniquityCore.sol | πΆ Internal Review | Pending external audit |
| UniquitySubmit.sol | πΆ Internal Review | Pending external audit |
| Client-side crypto | πΆ Internal Review | Uses standard libraries |
| FHE operations | β Zama Audited | Part of fhEVM |
| Comparison Service | π v2 | Not yet implemented |
If you discover a security vulnerability:
- Do NOT disclose publicly
- Email: bossfemzy10@gmail.com
- Include: Description, steps to reproduce, impact
- We will respond within 48 hours