django-pgcrypto-fields
is a Django
extension which relies upon pgcrypto
to
encrypt and decrypt data for fields.
django-pgcrypto-fields
has 3 kinds of fields:
- hash based fields
- PGP fields
- Symmetric fields
Hash based fields are:
TextDigestField
TextHMACField
PGP key fields are:
EmailPGPPublicKeyField
IntegerPGPPublicKeyField
TextPGPPublicKeyField
PGP symmetric fields are:
EmailPGPSymmetricKeyField
IntegerPGPSymmetricKeyField
TextPGPSymmetricKeyField
DatePGPSymmetricKeyField
DateTimePGPSymmetricKeyField
- postgres with
pgcrypto
pip install django-pgcrypto-fields
TextDigestField
is a hash based field. The value is hashed in the database when
saved with the digest
pgcrypto function using the sha512
algorithm.
TextHMACField
is a hash based field. The value is hashed in the database when
saved with the hmac
pgcrypto function using a key and the sha512
algorithm.
key
is set in settings.PGCRYPTO_KEY
.
N.B. DatePGPSymmetricKeyField
and DateTimePGPSymmetricKeyField
only support the following lookups:
__exact
__gt
__gte
__lt
__lte
There is not support for __range
yet (SQL BETWEEN
).
EmailPGPPublicKeyField, IntegerPGPPublicKeyField, DatePGPSymmetricKeyField, DateTimePGPSymmetricKeyField and TextPGPPublicKeyField
Public key encryption. It generates a token generated with a public key to encrypt the data and a private key to decrypt it.
Public and private keys can be set in settings with PUBLIC_PGP_KEY
and
PRIVATE_PGP_KEY
.
The public key is going to encrypt the message and the private key will be needed to decrypt the content. The following commands have been taken from the pgcrypto documentation (see Generating PGP Keys with GnuPG).
Generating a public and a private key:
$ gpg --gen-key
$ gpg --list-secret-keys
/home/bob/.gnupg/secring.gpg
---------------------------
sec 2048R/21 2014-10-23
uid Test Key <example@example.com>
ssb 2048R/42 2014-10-23
$ gpg -a --export 42 > public.key
$ gpg -a --export-secret-keys 21 > private.key
Symmetric key encryption. Encrypt and decrypt the data with settings.PGCRYPTO_KEY
.
In settings.py
:
BASEDIR = os.path.dirname(os.path.dirname(__file__))
PUBLIC_PGP_KEY_PATH = os.path.abspath(os.path.join(BASEDIR, 'public.key'))
PRIVATE_PGP_KEY_PATH = os.path.abspath(os.path.join(BASEDIR, 'private.key'))
# Used by PGPPublicKeyField
PUBLIC_PGP_KEY = open(PUBLIC_PGP_KEY_PATH).read()
PRIVATE_PGP_KEY = open(PRIVATE_PGP_KEY_PATH).read()
# Used by TextHMACField and PGPSymmetricKeyField
PGCRYPTO_KEY='ultrasecret'
# And add 'pgcrypto' to `INSTALLED_APPS` to create the extension for
# pgcrypto (in a migration).
INSTALLED_APPS = (
...
'pgcrypto',
...
)
from django.db import models
from pgcrypto import fields, managers
class MyModelManager(managers.PGPManager):
pass
class MyModel(models.Model):
digest_field = fields.TextDigestField()
hmac_field = fields.TextHMACField()
integer_pgp_pub_field = fields.IntegerPGPPublicKeyField()
pgp_pub_field = fields.TextPGPPublicKeyField()
integer_pgp_sym_field = fields.IntegerPGPSymmetricKeyField()
pgp_sym_field = fields.TextPGPSymmetricKeyField()
date_pgp_sym_field = fields.DatePGPSymmetricKeyField()
datetime_pgp_sym_field = fields.DateTimePGPSymmetricKeyField()
objects = MyModelManager()
Data is encrypted when inserted into the database.
Example:
>>> MyModel.objects.create(value='Value to be encrypted...')
If you use the bundled PGPManager
with your custom model manager, all encrypted
fields will automatically decrypted for you (except for hash fields which are one
way).
N.B. The bundled manager does not support decryption of fields from FK joins. For
example if the MyModel
class had a FK to to AnotherModel
class, no encrypted
fields be decrypted in the joined AnotherModel
.
It is recommended that you use the bundled PGPAdmin
class if using the custom
model manager and the Django Admin. The Django Admin performance suffers when
using the bundled custom manager. The PGPAdmin
disables automatic decryption
for all ORM calls for that admin class.
from django.contrib import admin
from pgcrypto.admin import PGPAdmin
class MyModelAdmin(admin.ModelAdmin, PGPAdmin):
# Your admin code here
This is useful if you are not using the custom manager or need to decrypt fields coming from joined FK fields.
When accessing the field name attribute on a model instance we are getting the decrypted value.
Example:
>>> # When using a PGP public key based encryption
>>> my_model = MyModel.objects.get()
>>> my_model.value # field's proxy
'Value decrypted'
To be able to filter PGP values we first need to use an aggregate method to decrypt the values.
Example when using a PGPPublicKeyField
:
>>> from pgcrypto.aggregates import PGPPublicKeyAggregate
>>> my_models = MyModel.objects.annotate(PGPPublicKeyAggregate('pgp_pub_field'))
[<MyModel: MyModel object>, <MyModel: MyModel object>]
>>> my_models.filter(pgp_pub_field__decrypted='Value decrypted')
[<MyModel: MyModel object>]
>>> my_models.first().pgp_pub_field__decrypted
'Value decrypted'
Example when using a PGPSymmetricKeyField
:
>>> from pgcrypto.aggregates import PGPSymmetricKeyAggregate
>>> my_models = MyModel.objects.annotate(PGPSymmetricKeyAggregate('pgp_sym_field'))
[<MyModel: MyModel object>, <MyModel: MyModel object>]
>>> my_models.filter(pgp_pub_field__decrypted='Value decrypted')
[<MyModel: MyModel object>]
>>> my_models.first().pgp_sym_field__decrypted
'Value decrypted'
To filter hash based values we need to compare hashes. This is achieved by using
a __hash_of
lookup.
Example:
>>> my_model = MyModel.objects.filter(digest_field__hash_of='value')
[<MyModel: MyModel object>]
>>> my_model = MyModel.objects.filter(hmac_field__hash_of='value')
[<MyModel: MyModel object>]