EncryptHelper¶
-
class
privex.helpers.crypto.EncryptHelper.
EncryptHelper
(encrypt_key: str, **kwargs)[source]¶ Symmetric AES-128 encryption/decryption made painless - wrapper class for
cryptography.fernet.Fernet
A wrapper class for the
cryptography.fernet.Fernet
encryption system, designed to make usage of Fernet as painless as possible.The class
EncryptHelper
contains various methods for simplifying the use of the Python library Cryptography ‘scryptography.fernet.Fernet
system.encrypt_str()
/decrypt_str()
facilitate painless encryption and decryption of data using AES-128 CBC. They can either be passed a 32-byte Fernet key (base64 encoded) as an argument, or leave the key as None and they’ll try to use the key defined on theEncryptHelper
instance atEncryptHelper.encrypt_key
Basic usage:
>>> from privex.helpers import EncryptHelper >>> key = EncryptHelper.generate_key() # Generates a 32-byte symmetric key, returned as a base64 encoded string >>> crypt = EncryptHelper(key) # Create an instance of EncryptHelper, en/decrypting using ``key`` by default # Encrypts the string 'hello world' with AES-128 CBC using the instance's key, returned as a base64 string >>> enc = crypt.encrypt_str('hello world') >>> print(enc) gAAAAABc7ERTpu2D_uven3l-KtU_ewUC8YWKqXEbLEKrPKrKWT138MNq-I9RRtCD8UZLdQrcdM_IhUU6r8T16lQkoJZ-I7N39g==
>>> crypt.is_encrypted(enc) # Check if a string/bytes is encrypted (only works with data matching the key) True >>> data = crypt.decrypt_str(enc) # Decrypt the encrypted data using the same key, outputs as a string >>> print(data) hello world
-
__init__
(encrypt_key: str, **kwargs)[source] Create an instance of
EncryptHelper
using thecryptography.fernet.Fernet
keyencrypt_key
as the default key for encrypting/decrypting data.- Parameters
encrypt_key (str) – Base64 encoded Fernet key, used by default for encrypting/decrypting data
Methods
__init__
(encrypt_key, **kwargs)Create an instance of
EncryptHelper
using thecryptography.fernet.Fernet
keyencrypt_key
as the default key for encrypting/decrypting data.decrypt_str
(data[, key])Decrypts
data
previously encrypted usingencrypt_str()
with the same Fernet compatiblekey
, and returns the decrypted version as a string.encrypt_str
(data[, key])Encrypts a piece of data
data
passed as a string or bytes using Fernet with the passed 32-bit symmetric encryption keykey
.from_file
(obj, **settings)Create an instance of
EncryptHelper
(or inheriting class) using a Fernet key loaded from a file, or stream object.from_password
(password, salt, **settings)Create an instance of
EncryptHelper
(or inheriting class) from a password derived Fernet key, instead of a pre-generated Fernet key.generate_key
([output, mode])Generate a compatible encryption key for use with
cryptography.fernet.Fernet
get_fernet
([key])Used internally for getting Fernet instance with auto-fallback to
encrypt_key
and exception handling.is_encrypted
(data[, key])Returns True if the passed
data
appears to be encrypted.password_key
(password[, salt, kdf])Generate a
cryptography.fernet.Fernet
key based on a password and salt.-
decrypt_str
(data: Union[str, bytes], key: Union[str, bytes] = None) → str[source]¶ Decrypts
data
previously encrypted usingencrypt_str()
with the same Fernet compatiblekey
, and returns the decrypted version as a string.The
key
cannot just be a random “password”, it must be a 32-byte key encoded with URL Safe base64. Use the methodgenerate_key()
to create a Fernet compatible encryption key.Under the hood, Fernet uses AES-128 CBC to encrypt the data, with PKCS7 padding and HMAC_SHA256 authentication.
If the
key
parameter isn’t passed, or is empty (None / “”), then it will attempt to fall back toencrypt_key
- if that’s also empty, EncryptKeyMissing will be raised.- Parameters
data (str) – The base64 encoded data to be decrypted, in the form of either a str or bytes.
key (str) – A Fernet encryption key (base64) for decryption, if blank, will fall back to
encrypt_key
- Raises
EncryptKeyMissing – Either no key was passed, or something is wrong with the key.
EncryptionError – Something went wrong while attempting to decrypt the data
- Return str decrypted_data
The decrypted data as a string
-
encrypt_key
: str¶ A base64 encoded
Fernet
key, used by default for functions such asencrypt_str()
-
encrypt_str
(data: Union[str, bytes], key: Union[str, bytes] = None) → str[source]¶ Encrypts a piece of data
data
passed as a string or bytes using Fernet with the passed 32-bit symmetric encryption keykey
. Outputs the encrypted data as a Base64 string for easy storage.The
key
cannot just be a random “password”, it must be a 32-byte key encoded with URL Safe base64. Use the methodgenerate_key()
to create a Fernet compatible encryption key.Under the hood, Fernet uses AES-128 CBC to encrypt the data, with PKCS7 padding and HMAC_SHA256 authentication.
If the
key
parameter isn’t passed, or is empty (None / “”), then it will attempt to fall back toself.encrypt_key
- if that’s also empty, EncryptKeyMissing will be raised.- Parameters
data (str) – The data to be encrypted, in the form of either a str or bytes.
key (str) – A Fernet encryption key (base64) to be used, if left blank will fall back to
encrypt_key
- Raises
EncryptKeyMissing – Either no key was passed, or something is wrong with the key.
EncryptionError – Something went wrong while attempting to encrypt the data
- Return str encrypted_data
The encrypted version of the passed
data
as a base64 encoded string.
-
classmethod
from_file
(obj: Union[str, _io.TextIOWrapper], **settings)[source]¶ Create an instance of
EncryptHelper
(or inheriting class) using a Fernet key loaded from a file, or stream object.>>> enc = EncryptHelper.from_file('/home/john/fernet.key') >>> d = enc.encrypt('hello') >>> enc.decrypt(d) 'hello'
- Parameters
obj (TextIOWrapper) – Load the key from the filename
obj
obj – Load the key from the file/stream object
obj
using.read()
-
classmethod
from_password
(password: Union[str, bytes], salt: Union[str, bytes], **settings)[source]¶ Create an instance of
EncryptHelper
(or inheriting class) from a password derived Fernet key, instead of a pre-generated Fernet key.See
password_key()
for more detailed usage information.Example
>>> enc = EncryptHelper.from_password('MySecurePass', salt=b'Sup3rseCr3tsalt') >>> d = enc.encrypt('hello') >>> enc.decrypt(d) 'hello'
- Parameters
password – A password to generate the key from, as
str
orbytes
salt – The salt to use when generating the key, as
str
orbytes
Ifsalt
is a string, it can also be passed in base64 format.
-
static
generate_key
(output: Optional[Union[str, _io.TextIOWrapper]] = None, mode='w') → str[source]¶ Generate a compatible encryption key for use with
cryptography.fernet.Fernet
NOTE: Regardless of whether or not the method is outputting the key to a filename / stream, this method will always return the encryption key as a string after completion. The key returning was redacted from the outputting examples to help readability.
Examples
With no arguments, it will simply return the key as a string.
>>> EncryptHelper.generate_key() '6vJ_o8XQRmX_TgUFTWWV_U2vm71ThnpWsCIvgXFWg9s='
If
output
is astr
- it’s assumed to be a filename, and the Fernet key will be outputted to the fileoutput
usingopen(output, mode)
(wheremode
defaults to'w'
).Below, we call
generate_key
with the string test.key.txt - and we can then see the file was created and contains the Fernet key encoded with Base64.>>> EncryptHelper.generate_key('test.key.txt') >>> open('test.key.txt').read() 'aRDR-gCrmrPrMr9hQnL4epIPl2Szbzfid_vSTO-rl20='
If
output
is a file/stream object, the methodoutput.write(key)
will be called, wherekey
is the Fernet key as a string.Below, we open test2.key.txt in write mode manually, then pass the file stream object to generate_key, which writes the key to the file.
>>> with open('test2.key.txt', 'w') as fp: ... EncryptHelper.generate_key(fp) >>> open('test2.key.txt').read() 'DAFEvRkwG7ws0ccjIv2QL_s5cpeWktqpbc7eSjL-V74='
- Parameters
output (TextIOWrapper) – Simply return the generated key
output – Output the generated key to the filename
output
using the open modemode
output – Output the generated key to the file/stream object
output
using.write(key: str)
mode (str) – If you’re passing a string filename as
output
- then this controls theopen()
mode, e.g. ‘w’, ‘a’, ‘w+’
- Return str key
The generated Fernet key, encoded with Base64.
-
get_fernet
(key: Union[str, bytes] = None) → cryptography.fernet.Fernet[source]¶ Used internally for getting Fernet instance with auto-fallback to
encrypt_key
and exception handling.- Parameters
key (str) – Base64 Fernet symmetric key for en/decrypting data. If empty, will fallback to
encrypt_key
- Raises
EncryptKeyMissing – Either no key was passed, or something is wrong with the key.
- Return Fernet f
Instance of Fernet using passed
key
or self.encrypt_key for encryption.
-
is_encrypted
(data: Union[str, bytes], key: Union[str, bytes] = None) → bool[source]¶ Returns True if the passed
data
appears to be encrypted. Can only verify encryption if the samekey
that was used to encrypt the data is passed.- Parameters
data (str) – The data to check for encryption, either as a string or bytes
key (str) – Base64 encoded Fernet symmetric key for decrypting data. If empty, fallback to
encrypt_key
- Raises
EncryptKeyMissing – Either no key was passed, or something is wrong with the key.
- Return bool is_encrypted
True if the data is encrypted, False if it’s not encrypted or wrong key used.
-
static
password_key
(password, salt=None, kdf: Type[cryptography.hazmat.primitives.kdf.KeyDerivationFunction] = <class 'cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC'>, **kwargs) → Tuple[str, dict][source]¶ Generate a
cryptography.fernet.Fernet
key based on a password and salt. Key derivation is customisable.- Parameters
password – A password to generate the key from, as
str
orbytes
salt – The salt to use when generating the key, as
str
orbytes
Ifsalt
is a string, it can also be passed in base64 format.
Standard Usage with manual salt:
Call
password_key
with a password of your choice, and a salt of your choice (ideally at least 16 chars), and a tuple containing the Fernet key (base64 encoded), and key derivative configuration will be returned.>>> ek = EncryptHelper >>> key, kd = ek.password_key('MySecurePass', salt=b'Sup3rseCr3tsalt') >>> key 'rJ_g-lBT7pxeu4MVrhfi5rAv9yLbX5pTm6vkJj_Mezc=' >>> kd {'length': 32, 'salt': 'U3VwM3JzZUNyM3RzYWx0', 'backend': <...Backend object at 0x7fd1c0220eb8>, 'algorithm': <...SHA256 object at 0x7fd1b0232278>, 'iterations': 100000, 'kdf': <class '...PBKDF2HMAC'>}
You can see when we call the method a second time with the same password and salt, we get the same Fernet key.
>>> key, kd = ek.password_key('MySecurePass', salt=b'Sup3rseCr3tsalt') >>> key 'rJ_g-lBT7pxeu4MVrhfi5rAv9yLbX5pTm6vkJj_Mezc='
Now we can simply initialise the class with this key, and start encrypting/decrypting data:
>>> enc = EncryptHelper(key) >>> mydata = enc.encrypt_str('hello') >>> mydata 'gAAAAABdsJrpZvQAhAEwAGk2GPeJMUjUdp1FHAg42ncArvvQjqGztLslgexF7dKWbJ8bhYNt9MBzzT0WR_XEvl1j5Q95UOVTsQ==' >>> enc.decrypt_str(mydata) 'hello'
Automatic salt generation:
While it’s strongly recommend that you pass your own
salt
(at least 16 bytes recommended), for convenience this method will automatically generate a 16 byte salt and return it as part of the dict (second tuple item) returned.First, we generate a key from the password
helloworld
>>> ek = EncryptHelper >>> key, kd = ek.password_key('helloworld') >>> key '6asAQ0qTQtmjw54RBR_RVmwsyv6EgTY_lcnVgJAVKCQ=' >>> kd {'length': 32, 'salt': 'bDU5MzJaaEhnZ1htSmlQeg==', 'backend': <...Backend object at 0x7ff968053860>, 'algorithm': <...SHA256 object at 0x7ff9685f6160>, 'iterations': 100000, 'kdf': <class '...PBKDF2HMAC'>}
If we call password_key again with
helloworld
, you’ll notice it outputs a completely different key. This is because no salt was specified, so it simply generated yet another salt.>>> ek.password_key('helloworld')[0] 'BfesIzfEPodtHSyPrpnkK0iDipHikaE7T1uuFFPnqmc='
To actually get the same Fernet key back, we have to either:
Pass the entire
kd
dictionary as kwargs (safest option, contains all params used for generation)>>> ek.password_key('helloworld', **kd)[0] '6asAQ0qTQtmjw54RBR_RVmwsyv6EgTY_lcnVgJAVKCQ='
Pass the generated salt from the
kd
object, alongside our password.>>> ek.password_key('helloworld', salt=kd['salt'])[0] '6asAQ0qTQtmjw54RBR_RVmwsyv6EgTY_lcnVgJAVKCQ='
-