def test_unpadder_invalid_token(self): data = ('8000000000075bcd15303132333435363738396162636465660ecd40b0f64' '8f001b78b5a77b334b40fbbff559444b3325233e71c24e53f6028116b0377' 'b910ebe5498396de36dee59b') with freeze_time(123456789): fernet = FernetBytes() with self.assertRaises(InvalidToken): fernet.decrypt(binascii.unhexlify(data))
def test_decryptor_invalid_token(self): data = ('8000000000075bcd153031323334353637383961626364656629b930b1955' 'ddaec2d74fb4ff565d549d94cc75de940d1d25507f30763f05c412390d15d' 'a26bccee69f1b4543e75') with freeze_time(123456789): fernet = FernetBytes() with self.assertRaises(InvalidToken): fernet.decrypt(binascii.unhexlify(data))
def test_encrypt_decrypt(self): value = b'hello' iv = b'0123456789abcdef' data = ('8000000000075bcd153031323334353637383961626364656629b930b1955' 'ddaec2d74fb4ff565280abdc39baf116e80f116496cde9515bd7d938e5c74' 'd60bc186286e701ba4fb4004') with freeze_time(123456789): fernet = FernetBytes() self.assertEqual(fernet._encrypt_from_parts(value, iv), binascii.unhexlify(data)) self.assertEqual(fernet.decrypt(binascii.unhexlify(data)), value)
class EncryptedMixin(object): """ A field mixin storing encrypted data :param bytes key: This is an optional argument. Allows for specifying an instance specific encryption key. :param int ttl: This is an optional argument. The amount of time in seconds that a value can be stored for. If the time to live of the data has passed, it will become unreadable. The expired value will return an :class:`Expired` object. """ supported_lookups = ('isnull', ) def __init__(self, *args, **kwargs): self.key = kwargs.pop('key', None) self.ttl = kwargs.pop('ttl', None) self._fernet = FernetBytes(self.key) super(EncryptedMixin, self).__init__(*args, **kwargs) @property def description(self): return _('Encrypted %s') % super(EncryptedMixin, self).description def _dump(self, value): return self._fernet.encrypt(pickle.dumps(value)) def _load(self, value): try: return pickle.loads(self._fernet.decrypt(value, self.ttl)) except SignatureExpired: return Expired def check(self, **kwargs): errors = super(EncryptedMixin, self).check(**kwargs) # Django 1.8 compatibility for `self.rel` if getattr(self, 'remote_field', getattr(self, 'rel', None)): errors.append( checks.Error( 'Base field for encrypted cannot be a related field.', hint=None, obj=self, id='encrypted.E002')) return errors def clone(self): name, path, args, kwargs = super(EncryptedMixin, self).deconstruct() # Determine if the class that subclassed us has been subclassed. if not self.__class__.__mro__.index(EncryptedMixin) > 1: return encrypt( self.base_class(*args, **kwargs), self.key, self.ttl) return self.__class__(*args, **kwargs) def deconstruct(self): name, path, args, kwargs = super(EncryptedMixin, self).deconstruct() # Determine if the class that subclassed us has been subclassed. if not self.__class__.__mro__.index(EncryptedMixin) > 1: path = "%s.%s" % (encrypt.__module__, encrypt.__name__) args = [self.base_class(*args, **kwargs)] kwargs = {} if self.ttl is not None: kwargs['ttl'] = self.ttl return name, path, args, kwargs def get_lookup(self, lookup_name): if lookup_name not in self.supported_lookups: return return super(EncryptedMixin, self).get_lookup(lookup_name) def get_transform(self, lookup_name): if lookup_name not in self.supported_lookups: return return super(EncryptedMixin, self).get_transform(lookup_name) def get_internal_type(self): return "BinaryField" def get_db_prep_value(self, value, connection, prepared=False): value = models.Field.get_db_prep_value(self, value, connection, prepared) if value is not None: return connection.Database.Binary(self._dump(value)) return value get_db_prep_save = models.Field.get_db_prep_save def from_db_value(self, value, expression, connection, context): if value is not None: return self._load(force_bytes(value)) return value
class EncryptedMixin: """ A field mixin storing encrypted data :param bytes key: This is an optional argument. Allows for specifying an instance specific encryption key. :param int ttl: This is an optional argument. The amount of time in seconds that a value can be stored for. If the time to live of the data has passed, it will become unreadable. The expired value will return an :class:`Expired` object. """ supported_lookups = ('isnull', ) def __init__(self, *args, **kwargs): self.key = kwargs.pop('key', None) self.ttl = kwargs.pop('ttl', None) self._fernet = FernetBytes(self.key) super(EncryptedMixin, self).__init__(*args, **kwargs) @property def description(self): return _('Encrypted %s') % super(EncryptedMixin, self).description def _dump(self, value): return self._fernet.encrypt(pickle.dumps(value)) def _load(self, value): try: return pickle.loads(self._fernet.decrypt(value, self.ttl)) except SignatureExpired: return Expired def check(self, **kwargs): errors = super(EncryptedMixin, self).check(**kwargs) if getattr(self, 'remote_field', None): errors.append( checks.Error( 'Base field for encrypted cannot be a related field.', hint=None, obj=self, id='encrypted.E002')) return errors def clone(self): name, path, args, kwargs = super(EncryptedMixin, self).deconstruct() # Determine if the class that subclassed us has been subclassed. if not self.__class__.__mro__.index(EncryptedMixin) > 1: return encrypt(self.base_class(*args, **kwargs), self.key, self.ttl) return self.__class__(*args, **kwargs) def deconstruct(self): name, path, args, kwargs = super(EncryptedMixin, self).deconstruct() # Determine if the class that subclassed us has been subclassed. if not self.__class__.__mro__.index(EncryptedMixin) > 1: path = "%s.%s" % (encrypt.__module__, encrypt.__name__) args = [self.base_class(*args, **kwargs)] kwargs = {} if self.ttl is not None: kwargs['ttl'] = self.ttl return name, path, args, kwargs def get_lookup(self, lookup_name): if lookup_name not in self.supported_lookups: return return super(EncryptedMixin, self).get_lookup(lookup_name) def get_transform(self, lookup_name): if lookup_name not in self.supported_lookups: return return super(EncryptedMixin, self).get_transform(lookup_name) def get_internal_type(self): return "BinaryField" def get_db_prep_value(self, value, connection, prepared=False): value = models.Field.get_db_prep_value(self, value, connection, prepared) if value is not None: return connection.Database.Binary(self._dump(value)) return value get_db_prep_save = models.Field.get_db_prep_save def from_db_value(self, value, *args, **kwargs): if value is not None: return self._load(force_bytes(value)) return value
class EncryptedField(models.Field): @property def description(self): return _('Encrypted %s') % self.base_field.description def __init__(self, base_field, **kwargs): """ :type base_field: django.db.models.fields.Field :rtype: None """ key = kwargs.pop('key', None) ttl = kwargs.pop('ttl', None) self._fernet = FernetBytes(key) self._ttl = ttl self.base_field = base_field super(EncryptedField, self).__init__(**kwargs) @property def model(self): try: return self.__dict__['model'] except KeyError: raise AttributeError("'%s' object has no attribute 'model'" % self.__class__.__name__) @model.setter def model(self, model): self.__dict__['model'] = model self.base_field.model = model def check(self, **kwargs): errors = super(EncryptedField, self).check(**kwargs) if getattr(self.base_field, 'remote_field', self.base_field.rel): errors.append( checks.Error( 'Base field for encrypted cannot be a related field.', hint=None, obj=self, id='encrypted.E002' ) ) else: # Remove the field name checks as they are not needed here. base_errors = self.base_field.check() if base_errors: messages = '\n '.join('%s (%s)' % (error.msg, error.id) for error in base_errors) errors.append( checks.Error( 'Base field for encrypted has errors:\n %s' % messages, hint=None, obj=self, id='encrypted.E001' ) ) return errors def deconstruct(self): name, path, args, kwargs = super(EncryptedField, self).deconstruct() kwargs.update({ 'base_field': self.base_field, }) return name, path, args, kwargs def run_validators(self, value): super(EncryptedField, self).run_validators(value) self.base_field.run_validators(value) def validate(self, value, model_instance): super(EncryptedField, self).validate(value, model_instance) self.base_field.validate(value, model_instance) def set_attributes_from_name(self, name): super(EncryptedField, self).set_attributes_from_name(name) self.base_field.set_attributes_from_name(name) def get_internal_type(self): return "BinaryField" def pre_save(self, model_instance, add): return self._fernet.encrypt( pickle.dumps(self.base_field.pre_save(model_instance, add))) def get_db_prep_value(self, value, connection, prepared=False): value = super(EncryptedField, self).get_db_prep_value(value, connection, prepared) if value is not None: return connection.Database.Binary(value) return value def from_db_value(self, value, expression, connection, context): if value: try: return pickle.loads(self._fernet.decrypt(value, self._ttl)) except SignatureExpired: return Expired return value