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 %}
-
-{% 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.