# Phase 1: User Authentication
# Pseudonymization and Attribute-Based Credentials PET tools
# hashlib library - https://docs.python.org/3.9/library/hashlib.html
# pqcrypto library (must be builded) - https://github.com/kpdemetriou/pqcrypto
# Author: Norbert Lovinger; xlovin00@vutbr.cz
# Date: 16th May 2023

import hashlib
from pqcrypto.sign.dilithium4 import generate_keypair, sign as dsign, verify as dverify
from dataclasses import dataclass

# ABC class represents an ABC system (Trusted Authority) including issuer,
# a set of attributes and signature. CRYSTALS-Dilithium PQ algorithm
# is used to sign and verify the signature using definied methods sign() and verify().
# For issuing message of issuer and attributes is used SHA3_256 hashing function.
@dataclass
class ABC:
    issuer: str
    attributes: dict
    signature = None

    def sign(self, secret_key):
        h = f"{self.issuer}:{self.attributes}"
        hashed = hashlib.sha3_256(h.encode()).digest()
        message = bytes(hashed)
        self.signature = dsign(secret_key,message)

    def verify(self, public_key):
        h = f"{self.issuer}:{self.attributes}"
        hashed = hashlib.sha3_256(h.encode()).digest()
        return dverify(public_key,hashed,self.signature)

# User class represents an User who wants to enter the service. He has defined attributes
# including name, email, phone, age and driving licence status. Method pseudonymize() uses
# SHA3_256 hashing function to pseudonymize user's private information. Age and driving license
# status are clear text to be able comunicate with the service.
@dataclass
class User:
    name: str
    email: str
    phone: str
    age: int
    driving_license: str

    def pseudonymize(self):
        self.name = hashlib.sha3_256(self.name.encode()).hexdigest()
        self.email = hashlib.sha3_256(self.email.encode()).hexdigest()
        self.phone = hashlib.sha3_256(self.phone.encode()).hexdigest()

    def get_attributes(self):
        return {
            'age': self.age,
            'driving_license': self.driving_license,
            'name': self.name,
            'email': self.email,
            'phone': self.phone
        }

# Service Operator class represents opposite side, the service side in this solution.
# He is responsible for user's attribute verification based on attributes age and driving license status.
# Also he uses PQ Dilithium keypair for ensuring integrity of the sent user attributes.
# Once the user is approved, he is able to using the service.
@dataclass
class ServiceOperator:
    name: str
    public_key = None
    secret_key = None

    def verify(self, user, abc):
        # Pseudonymize user data
        user.pseudonymize()

        # Verify ABC signature
        if not abc.verify(self.public_key):
            print(f"Verification unsuccessful: Invalid signature.")
            return      

        # Verify age and driving license
        if user.age >= 18 and user.driving_license == 'valid':
            print(f"Verification successful for user {user.name} by service {self.name}.")
        else:
            print(f"Verification unsuccessful for user {user.name} by service {self.name}.")

    def generate_keys(self):
        public_key, secret_key = generate_keypair()
        self.public_key = public_key
        self.secret_key = secret_key

#
# Example solution
#  
service = ServiceOperator('Car Sharing Service')
service.generate_keys()
user = User('Norbert Lovinger', 'xlovin00@vutbr.cz', '+1234567890', 25, 'valid')
user.pseudonymize()
abc = ABC('Example Issuer', user.get_attributes())
abc.sign(service.secret_key)
service.verify(user, abc)
print(user)
