diff --git a/abac/migrations/0004_encrypteddata.py b/abac/migrations/0004_encrypteddata.py new file mode 100644 index 0000000..de269c5 --- /dev/null +++ b/abac/migrations/0004_encrypteddata.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.6 on 2023-10-24 16:16 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('abac', '0003_trustedauthority'), + ] + + operations = [ + migrations.CreateModel( + name='EncryptedData', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('x_values', models.TextField()), + ('token', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), + ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abac.file')), + ], + ), + ] diff --git a/abac/models.py b/abac/models.py index 22fe449..224c5f6 100644 --- a/abac/models.py +++ b/abac/models.py @@ -1,9 +1,13 @@ -import pickle -from base64 import b64encode, b64decode from django.contrib.auth.models import AbstractUser from django.db import models from django.utils import timezone +import pickle +from base64 import b64encode, b64decode +from random import randint +from phe import paillier +from uuid import uuid4 + class User(AbstractUser): pass @@ -52,7 +56,6 @@ class AttributeType(models.Model): class Attribute(models.Model): - attribute_type = models.ForeignKey(AttributeType, on_delete=models.CASCADE) value = models.CharField(max_length=2048) @@ -81,21 +84,39 @@ class Rule(models.Model): write = models.BooleanField(default=False) delete = models.BooleanField(default=False) - def is_satisfied_by(self, user): + def is_satisfied_by(self, user)->(bool, int, int): + x = randint(0,1000) + y = randint(0,1000) + try: + paillier_public_key = paillier.PaillierPublicKey(n=int(user.public_key)) + except: + return False, 0, 0 + encrypted_sum = paillier_public_key.encrypt(x) + for rule_attribute in self.ruleattribute_set.all(): - try: - user_attribute = UserAttribute.objects.get( - user=user, - attribute_type=rule_attribute.attribute_type, - ) - except UserAttribute.DoesNotExist: - # The user does not have this attribute; the rule is not satisfied. - return False - - if not self.attribute_satisfies_rule(user_attribute, rule_attribute): - return False - - return True + if not rule_attribute.attribute_type.is_private: + try: + user_attribute = UserAttribute.objects.get( + user=user, + attribute_type=rule_attribute.attribute_type, + ) + except UserAttribute.DoesNotExist: + # The user does not have this attribute; the rule is not satisfied. + return False, 0, 0 + if not self.attribute_satisfies_rule(user_attribute, rule_attribute): + return False, 0, 0 + else: + try: + user_attribute = UserAttribute.objects.get( + user=user, + attribute_type=rule_attribute.attribute_type, + ) + except UserAttribute.DoesNotExist: + # The user does not have this attribute; the rule is not satisfied. + return False, 0, 0 + encrypted_attribute_value = paillier.EncryptedNumber(paillier_public_key, int(user_attribute.value)) + encrypted_sum += (encrypted_attribute_value-int(rule_attribute.value))*y + return True, x, encrypted_sum.ciphertext() def attribute_satisfies_rule(self, user_attribute, rule_attribute): operator = rule_attribute.operator @@ -127,4 +148,10 @@ class RuleAttribute(Attribute): class TrustedAuthority(models.Model): name = models.CharField(max_length=255) - public_key = models.TextField() \ No newline at end of file + public_key = models.TextField() + + +class EncryptedData(models.Model): + file = models.ForeignKey(File, on_delete=models.CASCADE) + x_values = models.TextField() # Store the x values as comma-separated + token = models.UUIDField(default=uuid4, unique=True, editable=False) diff --git a/abac/templates/decrypt_and_forward.html b/abac/templates/decrypt_and_forward.html new file mode 100644 index 0000000..e3cb200 --- /dev/null +++ b/abac/templates/decrypt_and_forward.html @@ -0,0 +1,77 @@ +{% load static %} + + + + + Decrypt and Forward + + + + + + + + + + diff --git a/abac/templates/upload_public_key.html.old b/abac/templates/upload_public_key.html.old deleted file mode 100644 index f76a812..0000000 --- a/abac/templates/upload_public_key.html.old +++ /dev/null @@ -1,18 +0,0 @@ -{% extends 'base.html' %} -{% block content %} -
-
-
-

Upload Public Key

-
- {% csrf_token %} -
- - -
- -
-
-
-
-{% endblock %} \ No newline at end of file diff --git a/abac/urls.py b/abac/urls.py index 2fcec28..711fc79 100644 --- a/abac/urls.py +++ b/abac/urls.py @@ -19,7 +19,8 @@ from .views import ( upload_public_key, hierarchy_view, trusted_authorities, - delete_authority + delete_authority, + verify_decryption ) app_name = 'abac' @@ -42,4 +43,5 @@ urlpatterns = [ path('visualization/hierarchical', hierarchy_view, name='hierarchy_vis'), path('trusted-authorities', trusted_authorities, name="trusted_authorities"), path('trusted-authorities/delete//', delete_authority, name='delete_authority'), + path('verify_decryption', verify_decryption, name='verify_decryption') ] \ No newline at end of file diff --git a/abac/views.py b/abac/views.py index 5c33ac8..52e87f6 100644 --- a/abac/views.py +++ b/abac/views.py @@ -7,6 +7,7 @@ from django.urls import reverse from django.contrib import messages from django.core.exceptions import ValidationError from django.utils import timezone +from django.views.decorators.csrf import csrf_exempt import hashlib import json @@ -17,7 +18,7 @@ from cryptography.hazmat.backends import default_backend from .forms import FileUploadForm, UploadCertificateForm, RuleAttributeForm, TrustedAuthorityForm -from .models import File, Rule, UserAttribute, RuleAttribute, AttributeType, User, TrustedAuthority +from .models import File, Rule, UserAttribute, RuleAttribute, AttributeType, User, TrustedAuthority, EncryptedData @login_required @@ -209,31 +210,67 @@ def get_or_create_attribute_type(name, value, private=False): @login_required def download_file(request, file_id): file = get_object_or_404(File, id=file_id) - user = request.user - - for rule in file.rule_set.all(): - if rule.is_satisfied_by(user): - return serve_file(file) - - return HttpResponseForbidden('You do not have permission to download this file.') + allowed, x, enc = check_permission(user, file) + if allowed and not x and not enc: + return serve_file(file) + elif not allowed: + return HttpResponseForbidden('You do not have permission to download this file.') + elif enc: + encrypted_data = EncryptedData(file=file, x_values=','.join(map(str, x))) + encrypted_data.save() + context = { + 'encryptions_as_strings': [str(e) for e in enc], + 'token': str(encrypted_data.token) + } + return render(request, 'decrypt_and_forward.html', context) + +@login_required +def verify_decryption(request): + if request.method == 'POST': + token = request.POST.get('token') + decryptions = request.POST.getlist('decryptions') + encrypted_data = get_object_or_404(EncryptedData, token=token) + #Compare decryptions with stored x values TODO: for some reason this didn't work when I tried to make sure the order was preserved + x_values = list(map(int, encrypted_data.x_values.split(','))) + + for decrypted in decryptions: + if int(decrypted) in x_values: + return serve_file(encrypted_data.file) + + return HttpResponseForbidden("You don't have the permission to access this file!") + return HttpResponseNotAllowed(['POST']) + + def check_permission(user, file, mode = None): + """ + Method that checks if a user is allowed to perform the operation specified in mode with the file + TODO: Implement mode + + """ if mode: pass else: # a) Check if the user is the owner of the file. if user == file.owner: - return True + return True, [], [] # b) Check if the user is a superuser. if user.is_superuser: - return True + return True, [], [] # c) Check the user's attributes against the file's rules. + x = [] + enc = [] for rule in file.rule_set.all(): - if rule.is_satisfied_by(user): - return True + result, x_i, enc_i = rule.is_satisfied_by(user) + if result: + x.append(x_i) + enc.append(enc_i) + else: + return False, [], [] + return True, x, enc def serve_file(file): # Serve the file using FileResponse.