Login

Encrypted Custom Model Field

Author:
ncristian
Posted:
August 17, 2014
Language:
Python
Version:
1.6
Score:
1 (after 1 ratings)

Custom model field to support two ways encryption. The key is provided in the app settings.py file. It should be at least 32 chars.

usage

Assuming that you placed the new code into fields.py

from app.fields import EncyptedField

class Account(models.Model):
    password = EncryptedField()
 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
# -*- coding: utf-8 -*-

import types
import base64

from Crypto import Random
from Crypto.Cipher import AES

from django.db import models
from app.settings import FIELD_ENCRYPTION_KEY


class EncryptedField(models.Field):
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        self.prefix = kwargs.pop('prefix', '_')
        super(EncryptedField, self).__init__(*args, **kwargs)

    def get_internal_type(self):
        return 'TextField'

    def to_python(self, value):
        if value is None or not isinstance(value, types.StringTypes):
            return value

        if self.is_encrypted(value):
            value = value[len(self.prefix):]    # cut prefix
            value = base64.b64decode(value)

            iv, encrypted = value[:AES.block_size], value[AES.block_size:]  # extract iv

            crypto = AES.new(FIELD_ENCRYPTION_KEY[:32], AES.MODE_CBC, iv)
            raw_decrypted = crypto.decrypt(encrypted)
            value = raw_decrypted.rstrip("\0").decode('unicode_escape')

        return value

    def get_db_prep_value(self, value, connection, prepared=False):
        if not prepared:
            iv = Random.new().read(AES.block_size)
            crypto = AES.new(FIELD_ENCRYPTION_KEY[:32], AES.MODE_CBC, iv)

            if isinstance(value, types.StringTypes):
                value = value.encode('unicode_escape')
                value = value.encode('ascii')
            else:
                value = str(value)

            tag_value = (value + (AES.block_size - len(value) % AES.block_size) * "\0")
            value = self.prefix + base64.b64encode(iv + crypto.encrypt(tag_value))

        return value

    def is_encrypted(self, value):
        """checks if a string is encrypted against a static predefined prefix"""
        if self.prefix and value.startswith(self.prefix):
            return True
        else:
            return False

More like this

  1. Serializer factory with Django Rest Framework by julio 3 months, 2 weeks ago
  2. Image compression before saving the new model / work with JPG, PNG by Schleidens 4 months ago
  3. Help text hyperlinks by sa2812 5 months ago
  4. Stuff by NixonDash 7 months, 1 week ago
  5. Add custom fields to the built-in Group model by jmoppel 9 months, 1 week ago

Comments

Please login first before commenting.