georgemarshall / django-cryptography

Easily encrypt data in Django
https://django-cryptography.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
371 stars 70 forks source link

Upgrade results in InvalidSignature: Signature did not match digest. #55

Open LiamK opened 3 years ago

LiamK commented 3 years ago

Hi, I'm experiencing a problem accessing existing encrypted data after upgrading django-cryptotgraphy and Django.

cryptography==2.7 Django==2.2.7 django-cryptography==0.3 Python 3.6.3

to this:

cryptography==3.4.7 Django==3.2 django-cryptography==1.0 Python 3.6.9

The two installations exist independently, and are accessing the same database, with the same CRYPTOGRAPHY_KEY. The first one still works. The second one throws this error when trying to access that model:

Traceback (most recent call last):
  File "/home/liam/.virtualenvs/myapplication/lib/python3.6/site-packages/django_cryptography/core/signing.py", line 237, in unsign
    self.signature(signed_value[:-d_size]).verify(sig)
  File "/home/liam/.virtualenvs/myapplication/lib/python3.6/site-packages/cryptography/hazmat/primitives/hmac.py", line 74, in verify
    ctx.verify(signature)
  File "/home/liam/.virtualenvs/myapplication/lib/python3.6/site-packages/cryptography/hazmat/backends/openssl/hmac.py", line 75, in verify
    raise InvalidSignature("Signature did not match digest.")
cryptography.exceptions.InvalidSignature: Signature did not match digest.

Is it possible something changed in the cryptography package? Any suggestions on where to look?

Thanks for any tips.

LiamK commented 3 years ago

I tried again with a more recent version of Python:

cryptography==3.4.7 Django==2.2.7 django-cryptography==1.0 Python 3.9.1

Same problem. Is anyone able to help fix this?

bareynol commented 2 years ago

If the two Django installations are using different SECRET_KEYs, that might be causing the issue.

It appears that data is being encrypted with the CRYPTOGRAPHY_KEY, but the data is being signed with the Django SECRET_KEY.

EncryptedMixin (the wrapper for the model Field added by the encrypt() function) uses FernetBytes without passing in a signer argument: https://github.com/georgemarshall/django-cryptography/blob/19d715fd2d641d77f60862295889d4ed0582f06c/django_cryptography/fields.py#L103

FernetBytes by default will create a signer using FernetSigner, passing no arguments: https://github.com/georgemarshall/django-cryptography/blob/19d715fd2d641d77f60862295889d4ed0582f06c/django_cryptography/utils/crypto.py#L108

FernetSigner will use the SECRET_KEY by default: https://github.com/georgemarshall/django-cryptography/blob/19d715fd2d641d77f60862295889d4ed0582f06c/django_cryptography/core/signing.py#L192

Unfortunately this will cause your fields to be encrypted with the same CRYPTOGRAPHY_KEY, but signed with different keys.

matteius commented 1 year ago

@bareynol thanks for the excellent analysis -- what I seemingly have run into with this project is kind of two fold:

1.) On mac os and some systems, I get AttributeError: 'Settings' object has no attribute 'CRYPTOGRAPHY_BACKEND' so to resolve this I try adding the CRYPTOGRAPHY_BACKEND and CRYPTOGRAPHY_KEY settings to the django project settings file. However, initially I try setting it the same way it is in appconf, but that has some implication where internal to django-cryptography it uses that key instead of the SECRET_KEY and ends up with a whole new encrpytion key, which breaks for historical records. 1.) This coupled with with the self.key = force_bytes(key or settings.SECRET_KEY) requires that CRYPTOGRAPHY_KEY be set to the original SECRET_KEY value for historical records to continue functioning, but then a weird thing happens in the pytest runner, where it expects the setting CRYPTOGRAPHY_KEY to be the final AES256 key that is set by appconf (seemingly appconf doesn't get invoked in the test runner but does in the running server, and results in two different behaviors when loading keys from the settings).

I'm not sure what the path forward is to improve this library with regards to these edge cases, but it seems like a tricky problem to solve.