2018-06-21 18:03:46 +00:00
|
|
|
# coding:utf-8
|
|
|
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
|
|
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
|
|
|
# quelques clics.
|
|
|
|
#
|
|
|
|
# Copyright © 2017 Gabriel Détraz
|
2019-09-29 14:02:28 +00:00
|
|
|
# Copyright © 2017 Lara Kermarec
|
2018-06-21 18:03:46 +00:00
|
|
|
# Copyright © 2017 Augustin Lemesle
|
|
|
|
# Copyright © 2018 Maël Kervella
|
2018-07-04 16:03:23 +00:00
|
|
|
# Copyright © 2018 Hugo Levy-Falk
|
2018-06-21 18:03:46 +00:00
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License along
|
|
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
|
2018-07-04 16:03:23 +00:00
|
|
|
"""
|
2018-06-21 18:03:46 +00:00
|
|
|
Module defining a AESEncryptedField object that can be used in forms
|
|
|
|
to handle the use of properly encrypting and decrypting AES keys
|
|
|
|
"""
|
|
|
|
|
|
|
|
import string
|
|
|
|
import binascii
|
|
|
|
from random import choice
|
|
|
|
from Crypto.Cipher import AES
|
|
|
|
|
|
|
|
from django.db import models
|
2018-07-04 16:03:23 +00:00
|
|
|
from django import forms
|
2018-06-21 18:03:46 +00:00
|
|
|
from django.conf import settings
|
|
|
|
|
2019-11-04 16:55:03 +00:00
|
|
|
EOD_asbyte = b"`%EofD%`" # This should be something that will not occur in strings
|
|
|
|
EOD = EOD_asbyte.decode("utf-8")
|
|
|
|
|
2018-06-21 18:03:46 +00:00
|
|
|
|
|
|
|
def genstring(length=16, chars=string.printable):
|
|
|
|
""" Generate a random string of length `length` and composed of
|
|
|
|
the characters in `chars` """
|
2019-11-04 16:55:03 +00:00
|
|
|
return "".join([choice(chars) for i in range(length)])
|
2018-06-21 18:03:46 +00:00
|
|
|
|
|
|
|
|
2018-11-08 01:47:25 +00:00
|
|
|
def encrypt(key, secret):
|
|
|
|
""" AES Encrypt a secret with the key `key` """
|
2018-06-21 18:03:46 +00:00
|
|
|
obj = AES.new(key)
|
2018-11-08 01:47:25 +00:00
|
|
|
datalength = len(secret) + len(EOD)
|
2018-06-21 18:03:46 +00:00
|
|
|
if datalength < 16:
|
|
|
|
saltlength = 16 - datalength
|
|
|
|
else:
|
|
|
|
saltlength = 16 - datalength % 16
|
2019-11-04 16:55:03 +00:00
|
|
|
encrypted_secret = "".join([secret, EOD, genstring(saltlength)])
|
2018-11-08 01:47:25 +00:00
|
|
|
return obj.encrypt(encrypted_secret)
|
2018-06-21 18:03:46 +00:00
|
|
|
|
|
|
|
|
2018-11-08 01:47:25 +00:00
|
|
|
def decrypt(key, secret):
|
|
|
|
""" AES Decrypt a secret with the key `key` """
|
2018-06-21 18:03:46 +00:00
|
|
|
obj = AES.new(key)
|
2018-11-08 01:47:25 +00:00
|
|
|
uncrypted_secret = obj.decrypt(secret)
|
|
|
|
return uncrypted_secret.split(EOD_asbyte)[0]
|
2018-06-21 18:03:46 +00:00
|
|
|
|
|
|
|
|
2018-07-04 16:03:23 +00:00
|
|
|
class AESEncryptedFormField(forms.CharField):
|
|
|
|
widget = forms.PasswordInput(render_value=True)
|
|
|
|
|
|
|
|
|
2018-06-21 18:03:46 +00:00
|
|
|
class AESEncryptedField(models.CharField):
|
|
|
|
""" A Field that can be used in forms for adding the support
|
|
|
|
of AES ecnrypted fields """
|
2018-07-11 15:25:53 +00:00
|
|
|
|
2018-06-21 18:03:46 +00:00
|
|
|
def save_form_data(self, instance, data):
|
2019-11-04 16:55:03 +00:00
|
|
|
setattr(
|
|
|
|
instance,
|
|
|
|
self.name,
|
|
|
|
binascii.b2a_base64(encrypt(settings.AES_KEY, data)).decode("utf-8"),
|
|
|
|
)
|
2018-06-21 18:03:46 +00:00
|
|
|
|
|
|
|
def to_python(self, value):
|
|
|
|
if value is None:
|
|
|
|
return None
|
2018-07-07 22:01:06 +00:00
|
|
|
try:
|
2019-11-04 16:55:03 +00:00
|
|
|
return decrypt(settings.AES_KEY, binascii.a2b_base64(value)).decode("utf-8")
|
2019-01-11 13:02:32 +00:00
|
|
|
except UnicodeDecodeError as e:
|
|
|
|
raise ValueError(
|
|
|
|
"Could not decode your field %s, your settings.AES_KEY "
|
|
|
|
"is probably wrong." % self.name
|
|
|
|
)
|
2018-06-21 18:03:46 +00:00
|
|
|
|
|
|
|
def from_db_value(self, value, *args, **kwargs):
|
|
|
|
if value is None:
|
|
|
|
return value
|
2018-07-07 22:01:06 +00:00
|
|
|
try:
|
2019-11-04 16:55:03 +00:00
|
|
|
return decrypt(settings.AES_KEY, binascii.a2b_base64(value)).decode("utf-8")
|
2019-01-11 13:02:32 +00:00
|
|
|
except UnicodeDecodeError as e:
|
|
|
|
raise ValueError(
|
|
|
|
"Could not decode your field %s, your settings.AES_KEY "
|
|
|
|
"is probably wrong." % self.name
|
|
|
|
)
|
2018-06-21 18:03:46 +00:00
|
|
|
|
|
|
|
def get_prep_value(self, value):
|
|
|
|
if value is None:
|
|
|
|
return value
|
2019-11-04 16:55:03 +00:00
|
|
|
return binascii.b2a_base64(encrypt(settings.AES_KEY, value)).decode("utf-8")
|
2018-07-04 16:03:23 +00:00
|
|
|
|
|
|
|
def formfield(self, **kwargs):
|
2019-11-04 16:55:03 +00:00
|
|
|
defaults = {"form_class": AESEncryptedFormField}
|
2018-07-04 16:03:23 +00:00
|
|
|
defaults.update(kwargs)
|
|
|
|
return super().formfield(**defaults)
|