from django.db import models from django import forms from django.conf import settings import binascii import random import string class EncryptedString ( str ): """A subclass of string so it can be told whether a string is encrypted or not (if the object is an instance of this class then it must [well, should] be encrypted).""" pass class BaseEncryptedField ( models . Field ): 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 ]) models . Field . __init__ ( self , * args , ** kwargs ) def to_python ( self , value ): #values should always be encrypted no matter what! #raise an error if tthings may have been tampered with return self . cipher . decrypt ( binascii . a2b_hex ( str ( value ))) . split ( ' \0 ' )[ 0 ] def get_db_prep_value ( self , value ): if value is not None and not isinstance ( value , EncryptedString ): padding = self . cipher . block_size - len ( value ) % self . cipher . block_size if padding and padding < self . cipher . block_size : value += " \0 " + '' . join ([ random . choice ( string . printable ) for index in range ( padding - 1 )]) value = EncryptedString ( 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 )