Encryption¶
Symmetric wraps Fernet from the
cryptography package. Fernet provides authenticated symmetric encryption: a token cannot
be decrypted or tampered with without the key.
Generate a key¶
from shadow_data.cryptohash.symmetric_cipher import Symmetric
symmetric = Symmetric()
key = symmetric.create_key()
ciphertext = symmetric.encrypt('Hello World!')
plaintext = symmetric.decrypt(ciphertext)
print(ciphertext)
print(plaintext)
encrypt takes a str and returns bytes; decrypt does the reverse.
Use an existing key¶
Pass the key to the constructor, or assign it afterwards. Both paths validate it.
from shadow_data.cryptohash.symmetric_cipher import Symmetric
key = b'bpSGcODTJ1iOwxloIQJrAiYDRaqyypdCsQfg1EwVOTc='
symmetric = Symmetric(cipher_key=key)
print(symmetric.decrypt(symmetric.encrypt('Hello World')))
from shadow_data.cryptohash.symmetric_cipher import Symmetric
symmetric = Symmetric()
symmetric.cipher_key = b'bpSGcODTJ1iOwxloIQJrAiYDRaqyypdCsQfg1EwVOTc='
Key handling
The key above is an example from this documentation — never use a published key.
Generate your own with create_key(), store it in a secrets manager or environment
variable, and keep it out of source control and logs.
Errors¶
| Exception | Raised when |
|---|---|
CipherKeyNotFoundError |
Encrypting or decrypting before a key was created or set |
InvalidCipherKeyError |
Constructing or assigning a key Fernet does not accept |
from shadow_data.cryptohash.symmetric_cipher import Symmetric
from shadow_data.exceptions import CipherKeyNotFoundError
try:
Symmetric().encrypt('no key yet')
except CipherKeyNotFoundError as error:
print(error)
Both inherit from CustomError, so you can catch the whole family at once:
When to use something else¶
If you are replacing values in a dataset rather than encrypting a blob of text, use
Pseudonymizer. It is built on this class but returns strings,
handles key derivation for deterministic tokens, and raises a domain-specific error for
invalid tokens.