Skip to the content.

Week 13: Anonymous Authentication

⬅️ Week 10 | Main | Course Completed ➑️

🎯 Learning Goals

By the end of this week, you should understand:


πŸ“– Theoretical Content

Introduction to Anonymous Authentication

Traditional Authentication Problems:

Anonymous Authentication Goals:

  1. Selective Disclosure: Reveal only necessary attributes
  2. Unlinkability: Different authentications cannot be linked
  3. Minimal Information: Prove only what’s required for access
  4. Privacy Preservation: Protect user identity and behavior patterns

Use Cases and Applications

Age Verification:

Access Control:

Anonymous Voting:

Healthcare Privacy:

Anonymous Credential Systems

Basic Concept: Anonymous credentials allow users to prove possession of certified attributes without revealing their identity or enabling linkability across different uses.

Key Components:

  1. Issuer: Authority that grants credentials (government, employer, etc.)
  2. User: Individual who receives and uses credentials
  3. Verifier: Service that checks credential validity
  4. Attributes: Properties certified by the credential (age, membership, etc.)

Credential Lifecycle:

1. Registration: User proves identity to Issuer
2. Issuance: Issuer grants anonymous credential
3. Presentation: User proves attributes to Verifier
4. Verification: Verifier checks proof validity

Idemix (Identity Mixer) System

IBM’s Idemix Architecture:

1. Credential Structure:

2. Zero-Knowledge Proofs:

3. Unlinkability:

Microsoft U-Prove System

Alternative Approach:

Key Features:

Attribute-Based Credentials (ABC)

IRMA (I Reveal My Attributes): Modern implementation of privacy-preserving authentication

Attribute Types:

# Example attribute structure
credential_attributes = {
    'personal': {
        'age': 25,
        'nationality': 'Portuguese',
        'city': 'Porto'
    },
    'academic': {
        'university': 'University of Porto',
        'degree': 'MSc Computer Science',
        'student_id': 'encrypted_value'
    },
    'professional': {
        'employer': 'Tech Company',
        'role': 'Software Engineer',
        'clearance_level': 'confidential'
    }
}

Selective Disclosure Example:

# User wants to prove age β‰₯ 18 for online service
proof_request = {
    'required_predicates': ['age >= 18'],
    'optional_attributes': [],
    'revealed_attributes': []  # Nothing else revealed
}

# System generates zero-knowledge proof
zkp = generate_age_proof(user_credential, proof_request)
# Verifier confirms age requirement without learning exact age

πŸ” Detailed Explanations

Zero-Knowledge Proof Fundamentals

Definition: A zero-knowledge proof allows one party (prover) to convince another party (verifier) that they know a secret without revealing the secret itself.

Three Properties:

  1. Completeness: If statement is true, honest verifier will be convinced
  2. Soundness: If statement is false, no cheating prover can convince verifier
  3. Zero-Knowledge: Verifier learns nothing beyond the truth of the statement

Interactive vs Non-Interactive:

Interactive ZKP:

Prover                    Verifier
  |                         |
  |---- Commitment -------->|
  |<----- Challenge --------|
  |---- Response ---------->|
  |                         |

Non-Interactive ZKP (using Fiat-Shamir):

def non_interactive_proof(secret, public_params):
    # Generate commitment
    commitment = generate_commitment(secret, random_nonce)
    
    # Generate challenge using hash function
    challenge = hash(commitment, public_params, statement)
    
    # Generate response
    response = compute_response(secret, random_nonce, challenge)
    
    return (commitment, response)  # Proof

Schnorr Identification Protocol

Basic Protocol for Proving Knowledge of Discrete Logarithm:

Setup: Public parameters (g, p, q), public key y = g^x mod p

Protocol:

class SchnorrProof:
    def __init__(self, x, g, p, q):
        self.x = x  # Private key (secret)
        self.g = g  # Generator
        self.p = p  # Prime modulus
        self.q = q  # Prime order
        self.y = pow(g, x, p)  # Public key
        
    def generate_proof(self):
        # Step 1: Commitment
        r = random.randint(1, self.q - 1)
        commitment = pow(self.g, r, self.p)
        
        # Step 2: Challenge (non-interactive)
        challenge = self.hash_challenge(commitment)
        
        # Step 3: Response
        response = (r + challenge * self.x) % self.q
        
        return (commitment, response)
        
    def verify_proof(self, commitment, response, public_key):
        challenge = self.hash_challenge(commitment)
        
        # Verify: g^response = commitment * y^challenge
        left = pow(self.g, response, self.p)
        right = (commitment * pow(public_key, challenge, self.p)) % self.p
        
        return left == right

Range Proofs

Proving Age β‰₯ 18 without revealing exact age:

class RangeProof:
    def __init__(self, value, min_value, max_value):
        self.value = value
        self.min_value = min_value
        self.max_value = max_value
        
    def prove_in_range(self):
        """Prove that min_value ≀ value ≀ max_value"""
        
        # Method 1: Bit decomposition
        # Prove each bit of (value - min_value)
        difference = self.value - self.min_value
        bit_proofs = []
        
        for i in range(self.bit_length(self.max_value - self.min_value)):
            bit = (difference >> i) & 1
            bit_proof = self.prove_bit(bit)
            bit_proofs.append(bit_proof)
            
        return bit_proofs
        
    def prove_bit(self, bit):
        """Prove that a value is 0 or 1"""
        # Using quadratic constraint: bit * (bit - 1) = 0
        return self.generate_quadratic_proof(bit)

Anonymous Credential Protocols

Camenisch-Lysyanskaya Signature Scheme:

class CLSignature:
    def __init__(self):
        self.setup_params()
        
    def setup_params(self):
        # Generate strong RSA modulus and generators
        self.n = self.generate_rsa_modulus()
        self.S = self.random_qr(self.n)  # Random quadratic residue
        self.Z = self.random_qr(self.n)
        self.R = []  # Generators for each attribute
        
    def sign_attributes(self, attributes, secret_key):
        """Sign a set of attributes"""
        # Choose random prime e and random value s
        e = self.random_prime()
        s = random.randint(1, self.n)
        
        # Compute signature: A = (Z / (S^s * ∏R_i^{m_i}))^{1/e} mod n
        product = pow(self.S, s, self.n)
        for i, attr in enumerate(attributes):
            product = (product * pow(self.R[i], attr, self.n)) % self.n
            
        A = self.mod_inverse(product, self.n)
        A = pow(A, self.mod_inverse(e, self.phi_n), self.n)
        
        return (A, e, s)
        
    def prove_knowledge(self, signature, revealed_attrs, hidden_attrs):
        """Generate zero-knowledge proof of signature knowledge"""
        A, e, s = signature
        
        # Randomize signature to prevent linkability
        r = random.randint(1, self.n)
        A_prime = (A * pow(self.S, r, self.n)) % self.n
        e_prime = e
        s_prime = (s + r * e) % self.order
        
        # Generate ZK proof for the randomized signature
        return self.generate_signature_proof(A_prime, e_prime, s_prime, 
                                           revealed_attrs, hidden_attrs)

πŸ’‘ Practical Examples

Example 1: Anonymous Age Verification

Scenario: Online alcohol purchase requiring age β‰₯ 21

class AgeVerificationSystem:
    def __init__(self):
        self.min_age = 21
        
    def request_age_proof(self):
        return {
            'requirement': f'age >= {self.min_age}',
            'challenge': self.generate_challenge(),
            'no_linkability': True
        }
        
    def verify_age_proof(self, proof, challenge):
        # Verify zero-knowledge proof of age
        if self.verify_zkp(proof, challenge):
            return {'access_granted': True, 'age_disclosed': False}
        else:
            return {'access_granted': False, 'reason': 'Invalid proof'}

class User:
    def __init__(self, age, credential):
        self.age = age
        self.credential = credential
        
    def generate_age_proof(self, requirement, challenge):
        if self.age >= requirement['min_age']:
            # Generate ZK proof without revealing exact age
            return self.create_range_proof(
                value=self.age,
                min_value=requirement['min_age'],
                challenge=challenge
            )
        else:
            return None  # Cannot prove, insufficient age

# Usage
alice = User(age=25, credential=age_credential)
store = AgeVerificationSystem()

proof_request = store.request_age_proof()
age_proof = alice.generate_age_proof(proof_request, proof_request['challenge'])
result = store.verify_age_proof(age_proof, proof_request['challenge'])

print(f"Access granted: {result['access_granted']}")
print(f"Age disclosed: {result.get('age_disclosed', 'N/A')}")

Example 2: Anonymous Employee Access

Scenario: Company cafeteria with employee discounts

class EmployeeAccessSystem:
    def __init__(self, company_name):
        self.company_name = company_name
        self.valid_departments = ['Engineering', 'Marketing', 'Sales', 'HR']
        
    def request_employee_proof(self):
        return {
            'required_attributes': {
                'employer': self.company_name,
                'status': 'active'
            },
            'optional_attributes': ['department', 'hire_date'],
            'selective_disclosure': True
        }
        
class EmployeeCredential:
    def __init__(self, employee_data):
        self.attributes = employee_data
        self.signature = self.get_company_signature()
        
    def generate_selective_proof(self, request):
        proof = {}
        
        # Required attributes - must prove possession
        for attr, value in request['required_attributes'].items():
            if attr in self.attributes and self.attributes[attr] == value:
                proof[attr] = self.prove_attribute_equality(attr, value)
            else:
                return None  # Cannot satisfy requirement
                
        # Optional attributes - user chooses what to reveal
        for attr in request.get('optional_attributes', []):
            if attr in self.attributes:
                # User can choose to reveal or keep private
                reveal = self.user_choice_to_reveal(attr)
                if reveal:
                    proof[attr] = self.attributes[attr]
                else:
                    proof[attr] = self.prove_attribute_possession(attr)
                    
        return proof

# Example usage
employee = EmployeeCredential({
    'name': 'Bob Smith',
    'employer': 'TechCorp',
    'department': 'Engineering',
    'status': 'active',
    'hire_date': '2020-01-15',
    'salary': 75000
})

cafeteria = EmployeeAccessSystem('TechCorp')
access_request = cafeteria.request_employee_proof()
employee_proof = employee.generate_selective_proof(access_request)

# Result: Proves employment at TechCorp with active status
# Optional: May reveal department but keeps salary private

Example 3: Anonymous Survey Participation

Scenario: Academic research requiring verified student participation

class AnonymousResearchSystem:
    def __init__(self, university, study_requirements):
        self.university = university
        self.requirements = study_requirements
        
    def generate_participation_token(self, student_proof):
        if self.verify_student_eligibility(student_proof):
            # Generate unlinkable token for survey participation
            token = self.create_anonymous_token()
            return {
                'token': token,
                'survey_url': self.get_survey_url(),
                'expires': self.get_expiration_date(),
                'linkable': False
            }
        return None
        
    def verify_student_eligibility(self, proof):
        # Check proof shows:
        # 1. Valid student at this university
        # 2. Meets study criteria (age, program, etc.)
        # 3. Has not participated before (without linking to identity)
        return True  # Simplified for example

class StudentCredential:
    def __init__(self, student_data):
        self.attributes = student_data
        self.university_signature = None
        self.participation_nonce = random.randint(1, 2**128)
        
    def prove_eligibility(self, requirements):
        proof = {}
        
        # Prove university enrollment
        proof['university'] = self.prove_attribute_value(
            'university', requirements['university']
        )
        
        # Prove age in required range
        if 'age_range' in requirements:
            proof['age_range'] = self.prove_age_in_range(
                requirements['age_range']['min'],
                requirements['age_range']['max']
            )
            
        # Prove program eligibility
        if 'eligible_programs' in requirements:
            proof['program'] = self.prove_program_membership(
                requirements['eligible_programs']
            )
            
        # Prove non-participation (without revealing identity)
        proof['fresh_participation'] = self.prove_fresh_participation()
        
        return proof

# Usage example
student = StudentCredential({
    'name': 'Carol Johnson',
    'university': 'University of Porto',
    'program': 'Computer Science',
    'age': 22,
    'student_id': 'encrypted_id_12345'
})

research_study = AnonymousResearchSystem(
    university='University of Porto',
    study_requirements={
        'age_range': {'min': 18, 'max': 30},
        'eligible_programs': ['Computer Science', 'Mathematics', 'Physics']
    }
)

eligibility_proof = student.prove_eligibility(research_study.requirements)
participation_token = research_study.generate_participation_token(eligibility_proof)

if participation_token:
    print("Anonymous participation granted")
    print(f"Token: {participation_token['token'][:20]}...")
    print(f"Identity revealed: No")
    print(f"Can be linked to other responses: {participation_token['linkable']}")

❓ Self-Assessment Questions

Question 1: What is the fundamental difference between traditional authentication and anonymous authentication? Provide examples of when each is appropriate. (Click to reveal answer) **Answer:** **Traditional Authentication:** - **Purpose:** Verify and record specific identity - **Information:** Full identity disclosure required - **Linkability:** All actions linked to same identity - **Privacy:** Minimal privacy protection **Examples:** - Banking login (need full identity for account access) - Medical records access (legal requirements for identity) - Employee payroll system (specific person must be identified) - Government services (identity required for legal purposes) **Anonymous Authentication:** - **Purpose:** Verify credentials/attributes without identity disclosure - **Information:** Selective disclosure of only necessary attributes - **Linkability:** Actions cannot be linked across sessions - **Privacy:** Strong privacy protection **Examples:** - Age verification for alcohol purchase (only need age β‰₯ 21) - Student discount verification (only need valid student status) - Anonymous surveys requiring verified eligibility - Whistleblowing with credential verification - Online voting with eligibility verification **Key Differences:** | Aspect | Traditional | Anonymous | |--------|------------|-----------| | **Identity** | Full disclosure | Hidden/pseudonymous | | **Tracking** | Complete activity log | Unlinkable sessions | | **Attributes** | All available | Selective disclosure | | **Privacy** | Low | High | | **Accountability** | Full traceability | Limited to session | **When to Use Traditional:** - Legal identity requirements - Personalized services needing history - Financial transactions requiring audit trails - Account-based systems **When to Use Anonymous:** - Privacy-sensitive services - Age/eligibility verification only - Research participation - Freedom of expression scenarios - Avoiding profiling and tracking
Question 2: Explain how zero-knowledge proofs enable anonymous authentication. Walk through a simple example. (Click to reveal answer) **Answer:** **Zero-Knowledge Proofs in Anonymous Authentication:** ZKPs allow users to prove they possess valid credentials or satisfy requirements without revealing the credentials themselves or enabling linking across multiple authentications. **Core Mechanism:** 1. **Prove Knowledge:** Demonstrate possession of valid credential 2. **Prove Attributes:** Show specific properties (age β‰₯ 18) 3. **Hide Identity:** Never reveal who you are 4. **Prevent Linking:** Each proof uses fresh randomness **Simple Example: Proving Age β‰₯ 18** **Setup:** - User has birth certificate signed by government - Certificate contains birthdate: March 15, 1990 - Current date: June 14, 2025 (age = 35) - Requirement: Prove age β‰₯ 18 **Traditional Approach (Bad):** ``` User: "Here's my birth certificate" Verifier: "I see you're 35 years old, born March 15, 1990" Result: βœ“ Age verified, βœ— Exact age and birthdate revealed ``` **ZKP Approach (Good):** ```python # Step 1: User converts birthdate to age current_date = "2025-06-14" birth_date = "1990-03-15" age = calculate_age(birth_date, current_date) # 35 # Step 2: User generates ZKP def generate_age_proof(): # Prove: "I know a value 'age' such that: # 1. age β‰₯ 18 (satisfies requirement) # 2. age is certified by valid government signature # 3. I know the signature without revealing it" statement = "age >= 18 AND valid_government_signature(age)" witness = (age, government_signature) # Generate proof that hides actual age and signature zkp = create_proof(statement, witness) return zkp # Step 3: Verifier checks proof def verify_age_proof(proof): # Verifies mathematical proof that: # - User knows a government-certified age # - That age is β‰₯ 18 # - Without learning the actual age return verify_proof(proof, "age >= 18 AND valid_signature") ``` **ZKP Result:** - βœ“ Age β‰₯ 18 requirement satisfied - βœ“ Government certification verified - βœ— Exact age not revealed (could be 18, 25, 35, 67, etc.) - βœ— Birthdate not revealed - βœ— Cannot link to future age verifications **Technical Implementation (Simplified):** **Range Proof for Age:** ```python class AgeZKP: def prove_age_range(self, actual_age, min_age): # Convert to binary representation age_bits = self.to_binary(actual_age - min_age) # Prove each bit is 0 or 1 (valid binary) bit_proofs = [] for bit in age_bits: bit_proof = self.prove_bit_validity(bit) bit_proofs.append(bit_proof) # Prove the bits represent (actual_age - min_age) sum_proof = self.prove_bit_sum(age_bits, actual_age - min_age) # Combine with signature proof sig_proof = self.prove_government_signature(actual_age) return combine_proofs(bit_proofs, sum_proof, sig_proof) def prove_bit_validity(self, bit): # Prove: bit ∈ {0, 1} # Using constraint: bit Γ— (bit - 1) = 0 return quadratic_constraint_proof(bit, bit - 1, 0) ``` **Security Properties:** - **Completeness:** If age β‰₯ 18, proof will verify - **Soundness:** Cannot create false proof for age < 18 - **Zero-Knowledge:** Verifier learns only "age β‰₯ 18", nothing more **Practical Benefits:** - Privacy-preserving age verification - Unlinkable across different services - Minimal information disclosure - Strong cryptographic guarantees
Question 3: Design an anonymous credential system for a university. What attributes would you include, and how would you handle revocation? (Click to reveal answer) **Answer:** **University Anonymous Credential System Design:** **Credential Structure:** ```python class UniversityCredential: def __init__(self): self.attributes = { # Identity attributes (hidden by default) 'student_id': 'encrypted_unique_id', 'full_name': 'encrypted_name', 'email': 'encrypted_email', # Academic attributes (selectively disclosable) 'university': 'University of Porto', 'enrollment_status': 'active', 'program': 'Computer Science', 'degree_level': 'Masters', 'year_of_study': 2, 'expected_graduation': '2025-07', # Eligibility attributes 'age': 22, 'nationality': 'Portuguese', 'enrollment_date': '2023-09-01', # Academic standing 'gpa_range': 'B_to_A', # Instead of exact GPA 'academic_standing': 'good', 'credits_completed': 45, # Access rights 'library_access': True, 'lab_access': ['CS_lab', 'research_lab'], 'sports_facilities': True, # Temporal validity 'issued_date': '2023-09-01', 'expiry_date': '2025-12-31', 'semester': 'Fall_2024' } self.revocation_info = { 'revocation_id': 'unique_revocation_identifier', 'accumulator_witness': 'cryptographic_witness' } ``` **Use Cases and Selective Disclosure:** **1. Student Discount at Restaurant:** ```python discount_proof = { 'revealed': ['university', 'enrollment_status'], 'proven_predicates': ['age >= 18'], 'hidden': ['student_id', 'full_name', 'program', 'gpa_range'] } # Result: Proves "active student at University of Porto, age β‰₯ 18" ``` **2. Library Access:** ```python library_proof = { 'revealed': ['library_access'], 'proven_predicates': ['enrollment_status == active'], 'hidden': ['student_id', 'program', 'year_of_study'] } # Result: Proves "has library access and is currently enrolled" ``` **3. Research Lab Access:** ```python lab_proof = { 'revealed': ['lab_access'], 'proven_predicates': [ 'program == Computer_Science', 'year_of_study >= 2', 'academic_standing == good' ], 'hidden': ['student_id', 'gpa_range'] } # Result: Proves "CS student, 2+ years, good standing, authorized for labs" ``` **4. Academic Conference Registration:** ```python conference_proof = { 'revealed': ['university', 'degree_level', 'program'], 'proven_predicates': ['enrollment_status == active'], 'hidden': ['student_id', 'full_name', 'year_of_study'] } # Result: "Active Masters student in CS at University of Porto" ``` **Revocation Handling:** **1. Cryptographic Accumulators Approach:** ```python class RevocationManager: def __init__(self): self.accumulator = CryptographicAccumulator() self.valid_credentials = set() def issue_credential(self, student_data): # Generate unique revocation ID revocation_id = generate_unique_id() # Add to accumulator of valid credentials self.accumulator.add(revocation_id) self.valid_credentials.add(revocation_id) # Create credential with witness credential = create_credential(student_data, revocation_id) witness = self.accumulator.generate_witness(revocation_id) return credential, witness def revoke_credential(self, revocation_id, reason): # Remove from accumulator self.accumulator.remove(revocation_id) self.valid_credentials.remove(revocation_id) # Log revocation (privacy-preserving) self.log_revocation(revocation_id, reason, timestamp=now()) def verify_non_revocation(self, revocation_id, witness): # Check if credential is still in accumulator return self.accumulator.verify_membership(revocation_id, witness) ``` **2. Revocation Reasons and Policies:** ```python revocation_triggers = { 'graduation': { 'auto_revoke': True, 'grace_period': '30_days', 'new_credential': 'alumni_credential' }, 'withdrawal': { 'auto_revoke': True, 'grace_period': '0_days', 'new_credential': None }, 'academic_suspension': { 'auto_revoke': True, 'grace_period': '0_days', 'reinstatement': 'manual_review' }, 'credential_compromise': { 'auto_revoke': True, 'grace_period': '0_days', 'new_credential': 'reissue_with_new_keys' } } ``` **3. Privacy-Preserving Revocation Lists:** ```python class PrivateRevocationList: def __init__(self): self.bloom_filter = BloomFilter(size=10000, hash_functions=3) def add_revoked_credential(self, revocation_id): # Add to bloom filter instead of explicit list self.bloom_filter.add(revocation_id) def check_revocation_status(self, revocation_id): # Returns: definitely_not_revoked OR possibly_revoked if revocation_id in self.bloom_filter: return "possibly_revoked" # Need further check else: return "definitely_not_revoked" # Guaranteed valid ``` **System Architecture:** **Components:** 1. **Credential Issuer:** University registrar office 2. **Attribute Authorities:** Different departments for different attributes 3. **Revocation Manager:** Handles credential lifecycle 4. **Verification Services:** Various campus and external services **Security Properties:** - **Unlinkability:** Multiple uses cannot be correlated - **Selective Disclosure:** Reveal only necessary attributes - **Forward Privacy:** Past uses remain private after revocation - **Backward Privacy:** Cannot use revoked credentials **Implementation Challenges:** 1. **Key Management:** Secure distribution and updates 2. **Synchronization:** Revocation status across services 3. **Performance:** Fast verification for high-volume services 4. **Privacy vs. Accountability:** Balance anonymous use with fraud prevention 5. **Integration:** Work with existing university systems **Benefits:** - **Student Privacy:** Minimal disclosure for various services - **Reduced Tracking:** Cannot build profiles across services - **Flexibility:** Support diverse use cases with same credential - **Future-Proof:** Can add new attributes and use cases

⬅️ Week 10 | Main | Course Completed ➑️