Leverages Google Tink to encrypt Django's core model fields. This work is heavily based on the work of Django Fernet Fields which seems to have been abandoned last few years.
Use pip to install django-tink-fields:
pip install django-tink-fieldsEdit settings.py and introduce a configuration section for TINK_FIELDS_CONFIG:
TINK_FIELDS_CONFIG = {
"default": {
"cleartext": True,
"path": "/path/to/a/plaintext_keyset.json",
}
}Tink keysets can be created via tinkey and may optionally be wrapped via a key-management system such as AWS KMS or GCP KMS.
To create a cleartext keyset for testing purposes:
tinkey create-keyset --out-format json --out test_plaintext_keyset.json --key-template AES128_GCMAlternatively, to create an encrypted keyset that is wrapped by GCP KMS, specify --master-key-uri as follow:
tinkey create-keyset --out-format json --out test_encrypted_keyset.json --key-template AES128_GCM --master-key-uri=gcp-kms://projects/foo1/locations/global/keyRings/foo2/cryptoKeys/foo3If you specify an encrypted keyset, you must also specify an AEAD construct that can unwrapped the keyset:
from tink.integration import gcpkms
from tink import aead
aead.register()
TINK_MASTER_KEY_URI = os.getenv(
"TINK_MASTER_KEY_URI",
"gcp-kms://projects/some-project/locations/global/keyRings/some-keyring/cryptoKeys/some-key",
)
gcp_client = gcpkms.GcpKmsClient(TINK_MASTER_KEY_URI, "")
gcp_aead = gcp_client.get_aead(TINK_MASTER_KEY_URI)
TINK_FIELDS_CONFIG = {
"default": {
"cleartext": False,
"path": "/path/to/an/encrypted_jeyset.json",
"master_key_aead": gcp_aead,
}
}To learn more about tinkey read the relevant documentation.
The following model creates an encrypted char field that makes use of the default keyset.
from django.db import models
from tink_fields import EncryptedCharField
class SomeModel(models.Model):
some_value = EncryptedCharField(max_length=32)You may specify a specific keyset by providing a keyset keyword argument:
from django.db import models
from tink_fields import EncryptedCharField
class AnotherModel(models.Model):
some_value = EncryptedCharField(max_length=32, keyset="another")Supported field types include: EncryptedCharField, EncryptedTextField, EncryptedDateField, EncryptedDateTimeField, EncryptedEmailField, and EncryptedIntegerField.
DeterministicEncryptedCharField provides support for Deterministic AEAD which means value in the field can be queried with exact matches. However, unlike normal AEAD encryption, an attacker can verify that two messages are equal.
Deterministic encryption requires key of type AES-SIV and supports Associated Data.
The encrypted fields make use of Authenticated Encryption With Associated Data (AEAD) which offers confidentiality and integrity within the same mode of operation. This allows the caller to specify a cleartext fragment named additional authenticated data (aad) to the encryption and decryption operations and receive cryptographic guarantees that the ciphertext data has not been tampered with.
To specify the aad fragment, provide a callback function aad_callback in the keyword arguments:
from django.db import models
from tink_fields import EncryptedCharField
class AnotherModel(models.Model):
some_value = EncryptedCharField(max_length=32, aad_callback=lambda x: b"some value")The value passed to the callback is the instance of the model field, with a signature of Callable[[models.Field], bytes]. As a reminder, the associated data is not encrypted so do not store sensitive data in it.