Django Tink Fields is a simple, production-ready way to encrypt Django model fields using the Google Tink cryptographic library. It offers drop-in encrypted field types for Django models, so you can protect sensitive data with minimal code changes.
Keywords: Django field encryption, encrypted model fields, Google Tink, AEAD, deterministic encryption.
- π Strong Encryption: Uses Google Tink for state-of-the-art cryptographic operations
- π‘οΈ AEAD Security: Provides both confidentiality and integrity through Authenticated Encryption with Associated Data
- π§ Easy Integration: Drop-in replacement for Django's standard field types
- β‘ High Performance: Optimized with caching and efficient key management
- π Flexible Key Management: Support for both cleartext and encrypted keysets
- βοΈ Cloud Integration: Works with AWS KMS, GCP KMS, and other key management systems
- π Comprehensive Testing: 97%+ test coverage with modern Python practices
- π Modern Python: Supports Python 3.12+ with full type hints
pip install django-tink-fieldsAdd to your settings.py:
TINK_FIELDS_CONFIG = {
"default": {
"cleartext": True,
"path": "/path/to/your/keyset.json",
}
}Generate a test keyset using tinkey:
tinkey create-keyset \
--out-format json \
--out keyset.json \
--key-template AES128_GCMfrom django.db import models
from tink_fields import EncryptedCharField, EncryptedTextField
class UserProfile(models.Model):
name = EncryptedCharField(max_length=100)
bio = EncryptedTextField()
email = EncryptedEmailField()
age = EncryptedIntegerField()
created_at = EncryptedDateTimeField()| Field Type | Django Equivalent | Description |
|---|---|---|
EncryptedCharField |
CharField |
Encrypted character field |
EncryptedTextField |
TextField |
Encrypted text field |
EncryptedEmailField |
EmailField |
Encrypted email field |
EncryptedBooleanField |
BooleanField |
Encrypted boolean field |
EncryptedIntegerField |
IntegerField |
Encrypted integer field |
EncryptedPositiveIntegerField |
PositiveIntegerField |
Encrypted positive integer field |
EncryptedFloatField |
FloatField |
Encrypted float field |
EncryptedDecimalField |
DecimalField |
Encrypted decimal field |
EncryptedUUIDField |
UUIDField |
Encrypted UUID field |
EncryptedJSONField |
JSONField |
Encrypted JSON field |
EncryptedURLField |
URLField |
Encrypted URL field |
EncryptedSlugField |
SlugField |
Encrypted slug field |
EncryptedDateField |
DateField |
Encrypted date field |
EncryptedDateTimeField |
DateTimeField |
Encrypted datetime field |
EncryptedBinaryField |
BinaryField |
Encrypted binary field |
| Field Type | Django Equivalent | Description |
|---|---|---|
DeterministicEncryptedTextField |
TextField |
Deterministic encrypted text field |
DeterministicEncryptedCharField |
CharField |
Deterministic encrypted character field |
DeterministicEncryptedEmailField |
EmailField |
Deterministic encrypted email field |
DeterministicEncryptedIntegerField |
IntegerField |
Deterministic encrypted integer field |
DeterministicEncryptedUUIDField |
UUIDField |
Deterministic encrypted UUID field |
DeterministicEncryptedBooleanField |
BooleanField |
Deterministic encrypted boolean field |
DeterministicEncryptedDateField |
DateField |
Deterministic encrypted date field |
DeterministicEncryptedDateTimeField |
DateTimeField |
Deterministic encrypted datetime field |
TINK_FIELDS_CONFIG = {
"default": {
"cleartext": True,
"path": "/path/to/cleartext_keyset.json",
}
}from tink.integration import gcpkms
from tink import aead
# Register AEAD primitives
aead.register()
# Configure GCP KMS
TINK_MASTER_KEY_URI = "gcp-kms://projects/your-project/locations/global/keyRings/your-keyring/cryptoKeys/your-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/encrypted_keyset.json",
"master_key_aead": gcp_aead,
}
}TINK_FIELDS_CONFIG = {
"default": {
"cleartext": True,
"path": "/path/to/default_keyset.json",
},
"sensitive": {
"cleartext": False,
"path": "/path/to/sensitive_keyset.json",
"master_key_aead": sensitive_aead,
}
}class SensitiveData(models.Model):
# Uses the "sensitive" keyset
secret = EncryptedCharField(max_length=100, keyset="sensitive")
# Uses the default keyset
public_data = EncryptedCharField(max_length=100)Add additional context to your encryption for enhanced security:
def get_aad_for_field(field):
"""Generate AAD based on field and model context."""
return f"model_{field.model._meta.label}_{field.name}".encode()
class UserData(models.Model):
# Each field gets unique AAD
ssn = EncryptedCharField(
max_length=11,
aad_callback=get_aad_for_field
)Encrypted fields support all standard Django field validators:
class ValidatedModel(models.Model):
email = EncryptedEmailField(unique=True)
age = EncryptedIntegerField(validators=[MinValueValidator(18)])
name = EncryptedCharField(max_length=50, blank=False)Cleartext keyset (development):
tinkey create-keyset \
--out-format json \
--out dev_keyset.json \
--key-template AES128_GCMEncrypted keyset with GCP KMS:
tinkey create-keyset \
--out-format json \
--out prod_keyset.json \
--key-template AES256_GCM \
--master-key-uri=gcp-kms://projects/my-project/locations/global/keyRings/my-keyring/cryptoKeys/my-keyEncrypted keyset with AWS KMS:
tinkey create-keyset \
--out-format json \
--out prod_keyset.json \
--key-template AES256_GCM \
--master-key-uri=aws-kms://arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012- Key Management: Use encrypted keysets in production with proper key management systems
- Key Rotation: Implement regular key rotation strategies
- Access Control: Restrict access to keyset files and master keys
- AAD Usage: Use AAD to bind encryption to specific contexts
- Field Selection: Only encrypt truly sensitive data to maintain performance
- No Database Queries: Encrypted fields cannot be used in database queries (except
isnull) - No Indexing: Encrypted fields cannot be indexed or used as primary keys
- Performance: Encryption/decryption adds computational overhead
- Key Management: Requires careful key management and rotation
The package includes comprehensive tests with 97%+ coverage:
# Run tests
pytest
# Run with coverage
pytest --cov=tink_fields --cov-report=html
# Run specific test categories
pytest tink_fields/test/test_fields.py # Basic functionality
pytest tink_fields/test/test_coverage.py # Edge casesThis repo ships a minimal Django project under example_project/ that exercises
real model usage and verifies ciphertext at rest, tamper detection, deterministic
lookups, and AAD behavior:
pytest -c example_project/pytest.ini example_project/example_app/tests# Clone the repository
git clone https://github.com/script3r/django-tink-fields.git
cd django-tink-fields
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install development dependencies
pip install -r requirements-dev.txt
# Install package in development mode
pip install -e .The project uses modern Python tooling:
# Format code
black tink_fields/
isort tink_fields/
# Lint code
flake8 tink_fields/
# Type checking
mypy tink_fields/
# Run all quality checks
tox| Operation | Time (ΞΌs) | Memory (KB) |
|---|---|---|
| Encrypt 1KB | ~50 | ~2 |
| Decrypt 1KB | ~45 | ~2 |
| Field Creation | ~5 | ~1 |
Benchmarks on Python 3.13, Django 5.2, with AES128_GCM
- Use appropriate field types -
CharFieldfor short text,TextFieldfor long content - Cache keysets - Keysets are automatically cached for performance
- Minimize AAD complexity - Keep AAD callbacks simple and fast
- Batch operations - Process multiple records together when possible
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
- β¨ Modernized codebase with Python 3.10+ support
- π§ Updated dependencies to latest versions
- π Improved test coverage to 97%+
- π¨ Applied modern Python formatting and linting
- π Enhanced documentation and examples
- π Fixed compatibility issues
- π¦ Updated package structure
This project is licensed under the BSD License - see the LICENSE.txt file for details.
- Google Tink - The cryptographic library powering this package
- Django Fernet Fields - Original inspiration for this project
- Django Community - For the amazing framework
- π Documentation
- π Issue Tracker
- π¬ Discussions
Made with β€οΈ for the Django community