Skip to content

Commit

Permalink
resolve issue where one mode overwrites the secret of another mode
Browse files Browse the repository at this point in the history
  • Loading branch information
erikvw committed Mar 28, 2017
1 parent f1ed7ee commit 7bf7331
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 19 deletions.
3 changes: 1 addition & 2 deletions django_crypto_fields/crypt_model_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ class CryptModelMixin(models.Model):
hash = models.CharField(
verbose_name="Hash",
max_length=128,
db_index=True,
unique=True)
db_index=True)

# causes problems with Postgres!!
secret = models.BinaryField(
Expand Down
43 changes: 30 additions & 13 deletions django_crypto_fields/field_cryptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ class FieldCryptor(object):
def __init__(self, algorithm, mode, keys=None, aes_encryption_mode=None):
self._using = None
self._cipher_model = None
self.cipher_buffer = OrderedDict()
self.algorithm = algorithm
self.mode = mode
self.aes_encryption_mode = aes_encryption_mode
self.cipher_buffer_key = '{}_{}'.format(self.algorithm, self.mode)
self.cipher_buffer = {self.cipher_buffer_key: {}}
if not self.aes_encryption_mode:
try:
# do not use MODE_CFB, see comments in pycrypto.blockalgo.py
Expand Down Expand Up @@ -173,15 +174,21 @@ def using(self):
return self._using

def update_cipher_model(self, ciphertext):
""" Updates cipher model (Crypt) and temporary buffer."""
""" Updates cipher model (Crypt) and temporary buffer.
"""
if self.verify_ciphertext(ciphertext):
hashed_value = self.get_hash(ciphertext)
secret = self.get_secret(ciphertext)
self.cipher_buffer.update({hashed_value: secret})
self.cipher_buffer[self.cipher_buffer_key].update(
{hashed_value: secret})
try:
cipher_model = self.cipher_model.objects.using(
self.using).get(hash=hashed_value)
self.using).get(
hash=hashed_value,
algorithm=self.algorithm,
mode=self.mode)
cipher_model.secret = secret
cipher_model.save()
except self.cipher_model.DoesNotExist:
self.cipher_model.objects.using(self.using).create(
hash=hashed_value,
Expand Down Expand Up @@ -226,15 +233,17 @@ def get_prep_value(self, value):
return value # returns into CharField

def get_hash(self, ciphertext):
"""Returns the hashed_value given a ciphertext or None."""
"""Returns the hashed_value given a ciphertext or None.
"""
try:
ciphertext.encode(ENCODING)
except AttributeError:
pass
return ciphertext[len(HASH_PREFIX):][:self.hash_size] or None

def get_secret(self, ciphertext):
""" Returns the secret given a ciphertext."""
""" Returns the secret given a ciphertext.
"""
if ciphertext is None:
secret = None
if self.is_encrypted(ciphertext):
Expand All @@ -243,16 +252,21 @@ def get_secret(self, ciphertext):

def fetch_secret(self, hash_with_prefix):
hashed_value = self.get_hash(hash_with_prefix)
secret = self.cipher_buffer.get(hashed_value)
secret = self.cipher_buffer[self.cipher_buffer_key].get(hashed_value)
if not secret:
try:
cipher = self.cipher_model.objects.using(
self.using).values('secret').get(hash=hashed_value)
self.using).values('secret').get(
hash=hashed_value,
algorithm=self.algorithm,
mode=self.mode)
secret = cipher.get('secret')
self.cipher_buffer.update({hashed_value: secret})
self.cipher_buffer[self.cipher_buffer_key].update(
{hashed_value: secret})
except self.cipher_model.DoesNotExist:
raise EncryptionError(
'Failed to get secret for given hash. Got {0}'.format(hashed_value))
'Failed to get secret for given {} {} hash. Got \'{}\''.format(
self.algorithm, self.mode, hash_with_prefix))
return secret

def is_encrypted(self, value, has_secret=None):
Expand Down Expand Up @@ -307,7 +321,8 @@ def verify_value(self, value, has_secret=None):
return value # note, is original passed value

def verify_hash(self, ciphertext):
"""Verifies hash segment of ciphertext (bytes) and raises an exception if not OK."""
"""Verifies hash segment of ciphertext (bytes) and raises an exception if not OK.
"""
try:
ciphertext = ciphertext.encode(ENCODING)
except AttributeError:
Expand All @@ -327,7 +342,8 @@ def verify_hash(self, ciphertext):
return True

def verify_secret(self, ciphertext):
"""Verifies secret segment of ciphertext and raises an exception if not OK."""
"""Verifies secret segment of ciphertext and raises an exception if not OK.
"""
if ciphertext[:len(HASH_PREFIX)] == HASH_PREFIX.encode(ENCODING):
try:
secret = ciphertext.split(CIPHER_PREFIX.encode(ENCODING))[1]
Expand All @@ -343,7 +359,8 @@ def verify_secret(self, ciphertext):
'Expected cipher prefix to be followed by a secret. Got nothing (3)')

def mask(self, value, mask=None):
""" Returns 'mask' if value is encrypted."""
""" Returns 'mask' if value is encrypted.
"""
mask = mask or '<encrypted>'
if self.is_encrypted(value):
return mask
Expand Down
4 changes: 1 addition & 3 deletions django_crypto_fields/fields/base_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,17 @@ def decrypt(self, value):
sys.stdout.write(
style.ERROR('CipherError. Got {}\n'.format(str(e))))
sys.stdout.flush()
pass
# raise ValidationError(e)
except EncryptionError as e:
sys.stdout.write(
style.ERROR('EncryptionError. Got {}\n'.format(str(e))))
sys.stdout.flush()
pass
raise
# raise ValidationError(e)
except MalformedCiphertextError as e:
sys.stdout.write(
style.ERROR('MalformedCiphertextError. Got {}\n'.format(str(e))))
sys.stdout.flush()
pass
# raise ValidationError(e)
return decrypted_value

Expand Down
20 changes: 20 additions & 0 deletions django_crypto_fields/migrations/0006_auto_20170328_0728.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.6 on 2017-03-28 05:28
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('django_crypto_fields', '0005_auto_20170106_1849'),
]

operations = [
migrations.AlterField(
model_name='crypt',
name='hash',
field=models.CharField(db_index=True, max_length=128, verbose_name='Hash'),
),
]
2 changes: 1 addition & 1 deletion django_crypto_fields/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

class Crypt(CryptModelMixin, BaseUuidModel):

class Meta:
class Meta(CryptModelMixin.Meta):
app_label = 'django_crypto_fields'
verbose_name = 'Crypt'
unique_together = (('hash', 'algorithm', 'mode'),)

0 comments on commit 7bf7331

Please sign in to comment.