Cryptographically_Enforced_Access_Control

Desing and implement a Cryptographically Enforced Access Control using CP-ABE

Definition of Data Model

The data model comprises entities: patients, doctors, hospitals, insurance agencies, employers, and health clubs. Additionally, a trusted authority exists, representing an entity trusted by everyone but not an actual entity. For instance, a reputable hospital or a government agency can become a trusted authority.

Entities and Data Involved

  • Authority’s public key $PK$: Accessible to all.
  • Authority’s master key $MK$: Known only to the authority.
  • List of all parties and their attributes: Managed by the authority.
  • Each party’s secret key $SK$: Distributed by the authority and known only to the party.
  • Encrypted health records: Stored in the trusted authority’s server, available for download by all, but decrypting them enforces specific policies.

Requirements

  • Encrypt patients' records.
  • Allow patients access to their data.
  • Enable patients to selectively share records with doctors, insurance agencies, and employers.
  • Permit hospitals to insert new records during patient visits.
  • Allow health clubs to insert records for their members.

Access Control Protocol Description

CP-ABE (Ciphertext-Policy Attribute-Based Encryption)

Our implementation is based on the CP-ABE protocol defined in BSW07 \cite{BSW}. CP-ABE associates each encrypted file with a policy tree dictating access. Entities have attribute lists.

CP-ABE Protocol Procedure

  1. The trusted authority runs the Setup algorithm, publishing $PK$, and retains $MK$.
from charm.toolbox.pairinggroup import PairingGroup
from charm.schemes.abenc.abenc_bsw07 import CPabe_BSW07

class KeyDistributor:
    global master_public_key, master_key

    # init method or constructor
    def __init__(self, cpabe):
        self.cpabe = cpabe
        
        self.master_public_key, self.master_key = cpabe.setup()
 
    def create_secret_key(self, attributes: list):

        return self.cpabe.keygen(self.master_public_key, self.master_key, attributes)

  1. The authority maintains a list of involved parties and their attributes, running KeyGen for all parties and distributing secret keys accordingly.
  2. Any entity with $PK$ and a specified access tree can encrypt a document.
def insert(self, data:HealthData, access_policy, password_manager, entities: list[Entity]):
        
        max_attempts = 3  # Adjust the number of maximum attempts as needed
        attempts = 0
        
        while attempts < max_attempts:
            patient_password = input("Patient_{} password: ".format(self.entity_id))
            check = password_manager.check_password(self, patient_password)
            access_policy = f"({self.entity_type}{self.entity_id})"
            if check:
                for entity in entities:
                    if entity.entity_type == "Hospital":
                        self.addRights(entity)
                        print(f"Hospital {entity.name} {entity.location} have write rights on {self.entity_type}{self.entity_id} records")
                    access_policy = f'({access_policy} OR ({entity.entity_type}{entity.entity_id}))'
                #print(access_policy)
                cipher_text = self.cpabe.encrypt(self.master_public_key, data.msg, access_policy)
                self.records.append(cipher_text)

                break
            else:
                attempts += 1
                if attempts < max_attempts:
                    print("Incorrect password. Please try again.")
                    
        if attempts == max_attempts:
            print("Maximum password attempts reached. Access denied.")
        
  1. Entities whose attributes match the access tree can run Decrypt to access the plaintext document.
    def read(self, record):
        
        msg = self.cpabe.decrypt(self.master_public_key, self.secret_key, record)
        print(f"Patient {self.name} {self.surname} read {msg}")

Design Choice

In our Personal Health Record (PHR) system design, we favor attribute-based encryption (ABE) for granular access control. For example, Patient A’s doctor shouldn’t access Patient B’s data if they aren’t treated by the same doctor. This calls for avoiding role-based access control or the access matrix model. Within ABE, we select ciphertext-policy (CP) over key-policy due to the latter’s impractical public key enumeration. However, CP-ABE introduces a single point of failure due to the trusted authority.

We maintain a list of parties: patients, doctors, hospitals, insurance agencies, employers, and health clubs. Each entity has specific attributes for precise access control, including UIDs and doctors' specialties (e.g., cardiology, nephrology). Including all UIDs is crucial for precise access rights control.

Authentication and Insertion

To facilitate insertion, two solutions using Access Control Lists (ACLs) are proposed.

First Solution: Static Password

  • Entities provide a static “uploading password” upon creation.
  • Entities verify passwords with the authority for insertion rights.
  • However, this method poses security risks and heavily relies on the central authority.

Second Solution: ABE Access Policy as ACL

  • The authority generates an ACL file encrypted with a policy tree allowing only the patient to read.
  • Patients re-encrypt the file with additional attributes for hospitals/health clubs.
    def addRights(self, entity):
        psw = self.group.random(GT)
        old_policy = self.guestWriteAccess["policy"]
        # print(f"{old_policy = }")
        policy = f'({old_policy} OR ({entity.entity_type}{entity.entity_id}))'
        # print(policy)
        
        cipher_psw = self.cpabe.encrypt(self.master_public_key, psw, policy)
        # print(f"{cipher_psw = }")

        self.guestWriteAccess['psw'] = cipher_psw
        self.guestWriteAccess['policy'] = policy
    
        return
  • Entities retrieve passwords from the ACL file for file insertion, and the authority modifies passwords when necessary.
def writePatient(self, patient, data:HealthData, password_manager):
        max_attempts = 3  # Adjust the number of maximum attempts as needed
        attempts = 0

        if data.type != "Health":
            print("You are not allowed to insert this type of data")
            #raise Exception("You are not allowed to insert this type of data")

        while attempts < max_attempts:
            
            health_password = input("Hospital_{} password: ".format(self.entity_id))
            check = password_manager.check_password(self, health_password)
            if check:
                toDec = patient.get_crypted_psw()
                decrypted = self.cpabe.decrypt(self.master_public_key, self.secret_key, toDec)
                right_check = patient.right_check(decrypted)
                if right_check:
                    access_policy = f'(({patient.entity_type}{patient.entity_id}))'
                    # msg = self.group.random(GT)
                    cipher_text = self.cpabe.encrypt(self.master_public_key, data.msg, access_policy)
                    patient.records.append(cipher_text)
                    break
                else:
                    print("You are not allowed to write for this user")
                    break
            else:
                attempts += 1
                if attempts < max_attempts:
                    print("Incorrect password. Please try again (Max 3 attempts)")
                    
        if attempts == max_attempts:
            print("Maximum password attempts reached. Access denied.")
        return

The second solution, built atop the reading access control system, offers more security but assumes parties' non-collusion and introduces occasional password entries for hospitals.

Implementation Details

  • Operating System: Linux
  • Language: Python v3.10.12

Our implementation utilizes the charm.schemes.abenc.abenc_bsw07 module in the Charm-Crypto v0.50 Python package. We chose a supersingular elliptic curve with a $512$-bit base field as the bilinear group. Each entity, when creating a patient’s health record, determines a policy tree for encryption, enabling selective access. For insertion authentication, passwords are generated via bcrypt hashing.

Serialization and deserialization complexities within the Charm-Crypto package hinder the implementation’s security. A custom serializer/deserializer is necessary for secure information storage.

Access Policy Definition

The mathematical definition of the access policy (policy tree $\mathcal T$) is detailed in Section 2.1, representing various scenarios for reading and inserting files.

Access Policy Scenarios for Reading Files

  1. Patient A’s record after seeing Doctor B: A.patient_id or B.doctor_id
  2. Patient A’s record after seeing Doctor B and agreeing to share it with all neurologists: A.patient_id or B.doctor_id or specialization=neurology
  3. Patient A’s record created by Hospital B after visiting the hospital: A.patient_id or B.hospital_id
  4. Patient A’s record from Hospital B shared with Employer C and Insurance Agency D for insurance claim: A.patient_id or B.hospital_id or C.employer_id or D.insurance_id

Access Policy Scenarios for Inserting Files

  1. When Patient A is born: A.patient_id
  2. When Patient A visits Doctor B and B wants to add new files to A’s record: A.patient_id or B.doctor_id
  3. When Patient A visits Hospital B: A.patient_id or B.hospital_id
  4. When Patient A joins Health Club B: A.patient_id or B.club_id
  5. When Patient A is sent to hospital B while exercising as a member of Health Club C: A.patient_id or B.hospital_id or C.club_id

Conclusion

Acknowledging flaws within our code, the demonstration serves its purpose: showcasing a practical implementation fulfilling requirements and exemplifying CP-ABE’s usage in our problem domain.