# Phase 3: Vehicle Access
# Anonymous Credentials - Group Signatures
# cryptodome library - https://pycryptodome.readthedocs.io/en/latest/src/installation.html
# Author: Norbert Lovinger; xlovin00@vutbr.cz
# Date: 16th May 2023

from Cryptodome.Hash import SHA3_256
from Cryptodome.PublicKey import RSA
from Cryptodome.Signature import pkcs1_15


# Vehicle class represents each vehicle in the fleet. It has attributes including brand name and license number.
# Two methods are implemented as sign() is responsible to creating unique hash stamp of the vehicle using SHA3_256 hashing function.
# Which is also signed by generated RSA private key. Verify() method is responsibe for checking the user's entered signature against
# selected vehicle. SHA3_256 is selected hashing function. RSA uses 4096 bits key length.
class Vehicle:
    def __init__(self, brand_name, license_number):
        self.brand_name = brand_name
        self.license_number = license_number
    
    def sign(self, key):
        # Create a hash of the vehicle's brand name and number plate id
        hash_obj = SHA3_256.new()
        hash_obj.update(f"{self.brand_name}:{self.license_number}".encode())

        # Sign the hash with the RSA private key
        signature = pkcs1_15.new(key).sign(hash_obj)
        return signature
    
    def verify(self, signature, key):
        # Verify the signature using the group signature public key
        hash_obj = SHA3_256.new()
        hash_obj.update(f"{self.brand_name}:{self.license_number}".encode())

        try:
            pkcs1_15.new(key).verify(hash_obj, signature)
            print("Signature verified successfully!")
            return True
        except ValueError:
            print("Signature not verified.")
            return False

# User class represents the user's activity of unlocking a vehicle from a fleet of vehicles using a public group key.
# One method verify_vehicle() selects a vehicle and checks inputed group public key agains the group of the vehicles.
# If there is a successful match, it means that user has unlocked a vehicle and could use it for rental.
class User:    
    def verify_vehicle(self, vehicle, signatures, key):
        # Verify the signature of the selected vehicle against the group signature public key
        hash_obj = SHA3_256.new()
        hash_obj.update(f"{vehicle.brand_name}:{vehicle.license_number}".encode())
        for signature in signatures:
            try:
                pkcs1_15.new(key).verify(hash_obj, signature)
                print("Signature verified successfully!")
                return True
            except ValueError:
                continue
        print("The selected vehicle's signature could not be verified.")
        return False

#
# Example solution
#  

# Generate the RSA key
group_key = RSA.generate(4096)

# Create a fleet of 10 vehicles with IDs
fleet = []
for i in range(10):
    vehicle = Vehicle(f"Vehicle {i}", f"ID_00{i}")
    fleet.append(vehicle)

# Sign the created data for each vehicle in the fleet
signatures = []
for vehicle in fleet:
    signature = vehicle.sign(group_key)
    signatures.append(signature)

# Add an additional non signed vehicle into the fleet to demostrate the vefify function
vehicle = Vehicle(f"Vehicle 99", f"ID_099")
fleet.append(vehicle)

# Create a user
user = User()

# Select a vehicle to verify the signature
selected_vehicle = fleet[5] #valid selected vehicle
#selected_vehicle = fleet[10] #non-valid selected vehicle

# Verify the signature for the selected vehicle using the user's access key
if user.verify_vehicle(selected_vehicle, signatures, group_key.publickey()):
    print(f"The selected vehicle ({selected_vehicle.brand_name} - {selected_vehicle.license_number}) access has been approved.")
else:
    print(f"The selected vehicle ({selected_vehicle.brand_name} - {selected_vehicle.license_number}) could not be accessed.")
