Changed model definitions and added logic to decrypt and set user key in session storage

This commit is contained in:
Daniel Armengod 2023-12-01 06:39:26 +01:00
parent 5eb606c4a4
commit 37dc8335a7
4 changed files with 58 additions and 3 deletions

View file

@ -6,6 +6,8 @@ from django.db import models
from django.conf import settings
from django.template.loader import get_template
from django.utils.translation import gettext_lazy as _
from nacl import secret
from utils.idhub_ssikit import (
generate_did_controller_key,
keydid_from_controller_key,
@ -409,7 +411,9 @@ class DID(models.Model):
# In JWK format. Must be stored as-is and passed whole to library functions.
# Example key material:
# '{"kty":"OKP","crv":"Ed25519","x":"oB2cPGFx5FX4dtS1Rtep8ac6B__61HAP_RtSzJdPxqs","d":"OJw80T1CtcqV0hUcZdcI-vYNBN1dlubrLaJa0_se_gU"}'
key_material = models.CharField(max_length=250)
# CHANGED: `key_material` to `_key_material`, datatype from CharField to BinaryField and the key is now stored encrypted.
key_material = None
_key_material = models.BinaryField(max_length=250)
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
@ -417,6 +421,18 @@ class DID(models.Model):
null=True,
)
def get_key_material(self, session):
if "sensitive_data_encryption_key" not in session:
raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.")
sb = secret.SecretBox(session["sensitive_data_encryption_key"])
return sb.decrypt(self._key_material)
def set_key_material(self, value, session):
if "sensitive_data_encryption_key" not in session:
raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.")
sb = secret.SecretBox(session["sensitive_data_encryption_key"])
self._key_material = sb.encrypt(value)
@property
def is_organization_did(self):
if not self.user:
@ -427,7 +443,8 @@ class DID(models.Model):
self.key_material = generate_did_controller_key()
self.did = keydid_from_controller_key(self.key_material)
def get_key(self):
# TODO: darmengo: esta funcion solo se llama desde un fichero que sube cosas a s3 (??) Preguntar a ver que hace.
def get_key_deprecated(self):
return json.loads(self.key_material)
@ -464,7 +481,10 @@ class VerificableCredential(models.Model):
created_on = models.DateTimeField(auto_now=True)
issued_on = models.DateTimeField(null=True)
subject_did = models.CharField(max_length=250)
data = models.TextField()
# CHANGED: `data` to `_data`, datatype from TextField to BinaryField and the rendered VC is now stored encrypted.
# TODO: verify that BinaryField can hold arbitrary amounts of data (max_length = ???)
data = None
_data = models.BinaryField()
csv_data = models.TextField()
status = models.PositiveSmallIntegerField(
choices=Status.choices,
@ -486,6 +506,18 @@ class VerificableCredential(models.Model):
related_name='vcredentials',
)
def get_data(self, session):
if "sensitive_data_encryption_key" not in session:
raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.")
sb = secret.SecretBox(session["sensitive_data_encryption_key"])
return sb.decrypt(self._data)
def set_data(self, value, session):
if "sensitive_data_encryption_key" not in session:
raise Exception("Ojo! Se intenta acceder a datos cifrados sin tener la clave de usuario.")
sb = secret.SecretBox(session["sensitive_data_encryption_key"])
self._data = sb.encrypt(value)
@property
def get_schema(self):
if not self.data:

View file

@ -25,4 +25,8 @@ class LoginView(auth_views.LoginView):
if self.extra_context['success_url'] == user_dashboard:
self.extra_context['success_url'] = admin_dashboard
auth_login(self.request, user)
# Decrypt the user's sensitive data encryption key and store it in the session.
password = form.cleaned_data.get("password") # TODO: Is this right????????
sensitive_data_encryption_key = user.decrypt_sensitive_data_encryption_key(password)
self.request.session["sensitive_data_encryption_key"] = sensitive_data_encryption_key
return HttpResponseRedirect(self.extra_context['success_url'])

View file

@ -1,5 +1,6 @@
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
from nacl import secret, pwhash
class UserManager(BaseUserManager):
@ -43,6 +44,10 @@ class User(AbstractBaseUser):
is_admin = models.BooleanField(default=False)
first_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255, blank=True, null=True)
# TODO: Hay que generar una clave aleatoria para cada usuario cuando se le da de alta en el sistema.
encrypted_sensitive_data_encryption_key = models.BinaryField(max_length=255)
# TODO: Hay que generar un salt aleatorio para cada usuario cuando se le da de alta en el sistema.
salt_of_sensitive_data_encryption_key = models.BinaryField(max_length=255)
objects = UserManager()
@ -85,3 +90,16 @@ class User(AbstractBaseUser):
for r in s.service.rol.all():
roles.append(r.name)
return ", ".join(set(roles))
def derive_key_from_password(self, password):
kdf = pwhash.argon2i.kdf # TODO: Move the KDF choice to SETTINGS.PY
ops = pwhash.argon2i.OPSLIMIT_INTERACTIVE # TODO: Move the KDF choice to SETTINGS.PY
mem = pwhash.argon2i.MEMLIMIT_INTERACTIVE # TODO: Move the KDF choice to SETTINGS.PY
salt = self.salt_of_sensitive_data_encryption_key
return kdf(secret.SecretBox.KEY_SIZE, password, salt, opslimit=ops, memlimit=mem)
def decrypt_sensitive_data_encryption_key(self, password):
sb_key = self.derive_key_from_password(password)
sb = secret.SecretBox(sb_key)
return sb.decrypt(self.encrypted_sensitive_data_encryption_key)

View file

@ -11,3 +11,4 @@ didkit==0.3.2
jinja2==3.1.2
jsonref==1.1.0
pyld==2.0.3
pynacl==1.5.0