I love Django, and I love Django Snippets, but I’ve noticed some snippets are out of date, most notably for me, Django snippet 1095 or Django Encryption. Unfortunately, some folks are hitting a few snags on TypeError: “Non-hexadecimal digit found”.
Luckily, it seems that Django-Fields have solved this problem for us! Here is my (their) technique!
Make a file named encryption.py to go into the same folder as your settings.py containing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import binascii import random import string from django import forms from django.db import models from django.conf import settings class BaseEncryptedField(models.Field): '''This code is based on the djangosnippet #1095 You can find the original at http://www.djangosnippets.org/snippets/1095/''' def __init__(self, *args, **kwargs): cipher = kwargs.pop('cipher', 'AES') imp = __import__('Crypto.Cipher', globals(), locals(), [cipher], -1) self.cipher = getattr(imp, cipher).new(settings.SECRET_KEY[:32]) self.prefix = '$%s$' % cipher max_length = kwargs.get('max_length', 40) mod = max_length % self.cipher.block_size if mod > 0: max_length += self.cipher.block_size - mod kwargs['max_length'] = max_length * 2 + len(self.prefix) models.Field.__init__(self, *args, **kwargs) def _is_encrypted(self, value): return isinstance(value, basestring) and value.startswith(self.prefix) def _get_padding(self, value): mod = len(value) % self.cipher.block_size if mod > 0: return self.cipher.block_size - mod return 0 def to_python(self, value): if self._is_encrypted(value): return self.cipher.decrypt(binascii.a2b_hex(value[len(self.prefix):])).split('\0')[0] return value def get_db_prep_value(self, value): if value is not None and not self._is_encrypted(value): padding = self._get_padding(value) if padding > 0: value += "\0" + ''.join([random.choice(string.printable) for index in range(padding-1)]) value = self.prefix + binascii.b2a_hex(self.cipher.encrypt(value)) return value class EncryptedTextField(BaseEncryptedField): __metaclass__ = models.SubfieldBase def get_internal_type(self): return 'TextField' def formfield(self, **kwargs): defaults = {'widget': forms.Textarea} defaults.update(kwargs) return super(EncryptedTextField, self).formfield(**defaults) class EncryptedCharField(BaseEncryptedField): __metaclass__ = models.SubfieldBase def get_internal_type(self): return "CharField" def formfield(self, **kwargs): defaults = {'max_length': self.max_length} defaults.update(kwargs) return super(EncryptedCharField, self).formfield(**defaults)) |
And then in your models.py:
1 2 3 4 5 6 7 8 9 10 | from encryption import EncryptedCharField ... class Example(models.Model): secret = EncryptedCharField(max_length=255) class Meta: ordering = ('secret',) def __unicode__(self): return self.secret |
This should be pretty explanatory! Have fun!
PS: You need PyCrypto! Google much?