Open klall opened 5 years ago
PGPPublicKey fields use public key encryption (PUBLIC_PGP_KEY / PRIVATE_PGP_KEY) and PGPSymmetricKey fields use symmetric key encryption (PGCRYPTO_KEY). If you want to use symmetric key encryption (PGCRYPTO_KEY), you would need to use TextPGPSymmetricKeyField instead of TextPGPPublicKeyField. You can find the chart of supported fields here:
https://github.com/incuna/django-pgcrypto-fields#django-model-field-equivalents
We do not currently support public key encryption with a passphrase. We will welcome a PR. Please be sure to include tests.
@klall If you want to make a PR, the documentation for pgp_public_decrypt() in PGCRYPTO is here:
https://www.postgresql.org/docs/9.5/pgcrypto.html
You'd have to figure out how to add in the psw
parameter.
Hi, is this still open?
Yes, PR are welcome.
On Tue, Aug 4, 2020, 12:29 PM Abder notifications@github.com wrote:
Hi, is this still open?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/incuna/django-pgcrypto-fields/issues/89#issuecomment-668755657, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAVZRZAYFZUHB4Q73LSGAJ3R7BHRFANCNFSM4GLAODBQ .
I've attempted to solve this in #364. Any comments/suggestions there would be welcome.
To add support for password-protected private keys for public key encryption in django-pgcrypto-fields, you can modify the PGPPublicKeyFieldMixin class to accept an additional parameter for the passphrase.
Here's an example implementation:
from pgcrypto import PGPKey
from pgcrypto.fields import PGPPublicKeyField
from pgcrypto.mixins import PGPMixin
class PGPPublicKeyFieldMixin(PGPMixin):
def init(self, args, **kwargs):
self.passphrase = kwargs.pop('passphrase', None)
super().init(args, **kwargs)
def to_python(self, value):
"""
Deserialize the value from the database.
"""
if value is None:
return None
key = PGPKey.from_blob(value)
if self.passphrase is not None:
key.decrypt(self.passphrase)
return key
def get_prep_value(self, value):
"""
Serialize the value to the database.
"""
if isinstance(value, str):
try:
value = PGPKey.from_blob(value)
if self.passphrase is not None:
value.decrypt(self.passphrase)
except ValueError:
raise ValueError("Invalid PGP key data")
except Exception as e:
raise ValueError(f"Invalid PGP key data: {e}")
elif not isinstance(value, PGPKey) and value is not None:
raise ValueError("Value must be a PGPKey instance or None")
if value is not None and self.passphrase is not None:
value.encrypt(self.passphrase)
return super().get_prep_value(value)
In this implementation, the PGPPublicKeyFieldMixin class inherits from the PGPMixin class, which handles the encryption and decryption of the PGP key. The init method is modified to accept an additional passphrase argument, which is stored in the instance variable self.passphrase.
The to_python method deserializes the value from the database and decrypts the PGP key using the passphrase, if one was provided. The get_prep_value method serializes the value to the database and encrypts the PGP key using the passphrase, if one was provided.
With this implementation, you can use the PGPPublicKeyField in the same way as before, with added support for password-protected private keys.
To add support for password protected public key encryption private keys, you can follow these steps:
class PGPPublicKeyFieldMixin(PGPMixin):
passphrase = models.CharField(max_length=255, blank=True, default='')
def _encrypt(self, value):
if self.passphrase:
query = "pgp_pub_encrypt(%s, dearmor(%s), %s)"
return query % (value, self._get_setting('PUBLIC_PGP_KEY'), self.passphrase)
else:
query = "pgp_pub_encrypt(%s, dearmor(%s))"
return query % (value, self._get_setting('PUBLIC_PGP_KEY'))
def _decrypt(self, field):
if self.passphrase:
query = "pgp_pub_decrypt(%s, dearmor(%s), %s)"
return query % (field, self._get_setting('PRIVATE_PGP_KEY'), self.passphrase)
else:
query = "pgp_pub_decrypt(%s, dearmor(%s))"
return query % (field, self._get_setting('PRIVATE_PGP_KEY'))
from pgcrypto.fields import TextPGPPublicKeyField
class MyModel(models.Model):
encrypted_text = TextPGPPublicKeyField(passphrase='your_passphrase')
By making these changes, the PGPPublicKeyFieldMixin class would now support password protected public key encryption private keys. Make sure to test the functionality thoroughly to ensure proper operation.
@some1ataplace Thanks for the in depth comment. PRs are welcome however setting a passphrase on the field (which is configuration) is less than optimal.
The setting PGCRYPTO_KEY is used in PGPSymmetricKeyFieldMixin (https://github.com/incuna/django-pgcrypto-fields/blob/429b9a8ce6971d8d547ea8be6f8234ed4d82673c/pgcrypto/mixins.py#L143)
but not PGPPublicKeyFieldMixin https://github.com/incuna/django-pgcrypto-fields/blob/429b9a8ce6971d8d547ea8be6f8234ed4d82673c/pgcrypto/mixins.py#L128
I was unable to use a public/private key with a passphrase with a TextPGPPublicKeyField. I regenerated a public/private key without a passphrase it worked fine.
Does PGPPublicKeyFieldMixin need to be updated to support PGCRYPTO_KEY?
Thanks