Zero-Knowledge Proofs Integration
This document explains how Oneliac uses zero-knowledge proofs (ZK-SNARKs) to verify patient eligibility and prescription validity without exposing sensitive medical information.
What are Zero-Knowledge Proofs?
A zero-knowledge proof is a cryptographic method that allows one party (prover) to convince another party (verifier) that a statement is true without revealing the underlying data.
Key Properties
- Completeness: Valid statements can always be proven
- Soundness: Invalid statements cannot be proven
- Zero-Knowledge: Proof reveals nothing beyond the statement's validity
ZK-SNARK Architecture in Oneliac
Oneliac uses Groth16, a specific ZK-SNARK scheme that:
- Generates proofs ~200 bytes (compressed)
- Performs fast proof verification (O(n) in circuit size)
- Is production-ready and audited
Proof Generation Pipeline
Step 1: Circuit Definition (Circom)
↓
Define logical constraints for:
- Patient eligibility rules
- Age requirements
- Insurance coverage rules
Step 2: Private Inputs
↓
patient_id (encrypted)
medical_history_hash (SHA256)
Step 3: Public Inputs
↓
procedure_code (PROC001, PROC002)
insurance_provider_id (INS123)
required_age (18)
Step 4: Circuit Compilation
↓
Convert circuit to rank-1 constraint system (R1CS)
Step 5: Witness Generation
↓
Compute satisfying assignments for constraints
Step 6: Proof Generation (Groth16)
↓
Generate cryptographic proof
Output: 256-byte proof
Step 7: Blockchain Submission
↓
Submit proof + public inputs to Solana
Step 8: On-Chain Verification
↓
Smart contract verifies proof
Returns: eligible (true/false)
Eligibility Verification Circuit
Logical Rules
The eligibility circuit verifies:
- Patient Identity:
hash(patient_id + nonce) == committed_hash - Age Requirement:
age >= 18 - Medical History:
hash(medical_history) == provided_hash - Coverage Rule:
procedure_code in coverage_database
Circuit Pseudocode (Circom)
template EligibilityChecker() {
// Private inputs - kept secret
signal private input patientID;
signal private input medicalHistoryHash;
// Public inputs - revealed
signal input procedureCode;
signal input insuranceProviderID;
signal input requiredAge;
// Outputs
signal output eligibility;
// Constraints
component hashCheck = PoseidonHash(2);
hashCheck.inputs[0] <== patientID;
hashCheck.inputs[1] <== medicalHistoryHash;
// hashCheck.out == stored_commitment
// Age verification (assume age is derived from medicalHistoryHash)
// requiredAge <= derivedAge
// Coverage verification
// procedureCode must be in authorized list
eligibility <== 1; // Valid if all constraints satisfied
}
Prescription Validation Circuit
Similar to eligibility, but adds:
- Drug interaction checking
- Pharmacy verification constraints
- Dosage validation rules
Circuit Constraint Example
// Ensure drug is not contraindicated
signal hasContraindication <== drugContraindicationCheck(
drugCode,
medicalHistoryHash
);
signal validPrescription <== 1 - hasContraindication;
Implementation in Oneliac
ZKProofGenerator Class
class ZKProofGenerator:
def __init__(self, circuit_path: str):
self.circuit_path = circuit_path # Path to compiled circuit
async def generate_proof(
self,
private_inputs: Dict,
public_inputs: Dict
) -> bytes:
"""
Generate a Groth16 ZK proof.
Args:
private_inputs: {
"patientID": "patient_123",
"medicalHistoryHash": "abc123..."
}
public_inputs: {
"procedureCode": "PROC001",
"insuranceProviderID": "INS123"
}
Returns:
256-byte Groth16 proof
"""
# Witness generation
witness = await self._generate_witness(
private_inputs,
public_inputs
)
# Proof generation
proof = self._groth16_prove(witness)
return proof
async def verify_proof(
self,
proof: bytes,
public_inputs: Dict
) -> bool:
"""
Verify a Groth16 proof.
Args:
proof: 256-byte proof from generate_proof
public_inputs: Public inputs used in proof
Returns:
True if proof is valid, False otherwise
"""
vkey = self._load_verification_key()
return vkey.verify(proof, public_inputs)
Proof Submission to Blockchain
async def _submit_to_blockchain(
self,
proof: bytes,
public_inputs: Dict,
ipfs_hash: str
) -> bool:
"""Submit ZK proof to Solana smart contract."""
payload = {
"proof": proof.hex(),
"public_inputs": json.dumps(public_inputs),
"ipfs_hash": ipfs_hash # Link to patient data
}
# Call smart contract via RPC
async with aiohttp.ClientSession() as session:
response = await session.post(
f"{self.solana_endpoint}/verify",
json=payload
)
return response.status == 200
Proof Lifecycle
Client Generates Request
↓
Patient Data: encrypted
Procedure Code: PROC001
↓
Oneliac API Receives Request
↓
Extract Private Inputs
- patient_id
- medical_history_hash
Extract Public Inputs
- procedure_code
- insurance_provider_id
↓
Generate ZK Proof
(256-byte Groth16 proof)
↓
Submit to Blockchain
- Proof
- Public inputs
- IPFS hash reference
↓
Smart Contract Verifies
- Proof verification
- Record transaction
- Emit event
↓
Return Result to Client
- Eligibility status
- Coverage percentage
- zk_proof_verified: true
Security Analysis
Threat Model
| Threat | Mitigation | Proof |
|---|---|---|
| Medical data exposure | Encrypted storage + ZK proof | Proof never reveals plaintext data |
| Patient ID linkage | Hash with nonce | Different nonces → different proofs |
| Proof forgery | Groth16 cryptographic security | Computationally infeasible to forge |
| Proof replay | On-chain timestamp + nonce | Each proof is unique |
| Collusion attack | Trusted setup ceremony | Only possible with trusted setup compromise |
Assumptions
- Trusted Setup: Groth16 requires a trusted setup. Oneliac uses audited ceremony results.
- Cryptographic Hardness: Assumes discrete log problem is hard
- Correct Circuit: Circuit must accurately encode business logic
- Private Key Security: Encryption keys must be kept secret
Performance Characteristics
| Operation | Time | Size | Notes |
|---|---|---|---|
| Proof Generation | 100-500ms | N/A | CPU-bound, pre-computed possible |
| Proof Size | N/A | 256 bytes | Independent of circuit size (Groth16) |
| Proof Verification | 10-50ms | N/A | Fast, suitable for on-chain |
| Circuit Compilation | ~10s | N/A | One-time, offline |
Best Practices
For Developers
- Verify Circuit Logic: Test all edge cases in circuit constraints
- Randomize Proofs: Use unique nonces to prevent replay attacks
- Store Commitments: Hash sensitive data before ZK proof generation
- Audit Circuits: Have security experts review constraint definitions
For Healthcare Providers
- Encrypt Data: Always encrypt patient data before transmission
- Verify Proofs: Check
zk_proof_verifiedfield in responses - Audit Trail: Review blockchain transaction history
- Key Management: Securely store encryption keys
Limitations
- Large Inputs: Complex circuits require more constraints (slower generation)
- Privacy Trade-off: Public inputs are visible on blockchain
- Proof Size: Unlike STARKs, Groth16 proofs don't scale to millions of constraints
- Trusted Setup: Requires initial trusted ceremony