132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import base64
|
|
import logging
|
|
|
|
from cryptography import fernet
|
|
from cryptography.hazmat.backends import default_backend
|
|
from cryptography.hazmat.primitives import hashes
|
|
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
|
|
KEY_LENGTH = 32
|
|
ITERATIONS = 10000
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
__all__ = ('encrypt', 'decrypt')
|
|
|
|
|
|
def encrypt(unencrypted_data,
|
|
passphrase,
|
|
salt,
|
|
key_length=KEY_LENGTH,
|
|
iterations=ITERATIONS):
|
|
"""
|
|
Encrypt the data, using the provided passphrase and salt,
|
|
and return the encrypted data.
|
|
|
|
:param unencrypted_data: Secret data to encrypt
|
|
:type unencrypted_data: bytes
|
|
:param passphrase: Passphrase to use to generate encryption key. Must be
|
|
at least 24-byte long
|
|
:type passphrase: bytes
|
|
:param salt: salt to use to generate encryption key. Must be randomly
|
|
generated.
|
|
:type salt: bytes
|
|
:param key_length: Length of the encryption key to generate, in bytes.
|
|
Will default to 32, if not provided.
|
|
:type key_length: positive integer.
|
|
:param iterations: A large number, used as seed to increase the entropy
|
|
in randomness of the generated key for encryption, and hence greatly
|
|
increase the security of encrypted data. will default to 10000, if not
|
|
provided.
|
|
:type iterations: positive integer.
|
|
:return: Encrypted secret data
|
|
:rtype: bytes
|
|
"""
|
|
|
|
return fernet.Fernet(_generate_key(
|
|
passphrase, salt, key_length, iterations)).encrypt(unencrypted_data)
|
|
|
|
|
|
def decrypt(encrypted_data,
|
|
passphrase,
|
|
salt,
|
|
key_length=KEY_LENGTH,
|
|
iterations=ITERATIONS):
|
|
"""
|
|
Decrypt the data, using the provided passphrase and salt,
|
|
and return the decrypted data.
|
|
|
|
:param encrypted_data: Encrypted secret data
|
|
:type encrypted_data: bytes
|
|
:param passphrase: Passphrase to use to generate decryption key. Must be
|
|
at least 32-byte long.
|
|
:type passphrase: bytes
|
|
:param salt: salt to use to generate decryption key. Must be randomly
|
|
generated.
|
|
:type salt: bytes
|
|
:param key_length: Length of the decryption key to generate, in bytes.
|
|
will default to 32, if not provided.
|
|
:type key_length: positive integer.
|
|
:param iterations: A large number, used as seed to increase entropy in
|
|
the randomness of the generated key for decryption, and hence greatly
|
|
increase the security of encrypted data. Will default to 10000, if not
|
|
provided.
|
|
:type iterations: positive integer.
|
|
:return: Decrypted secret data
|
|
:rtype: bytes
|
|
:raises InvalidToken: If the provided passphrase, and/or
|
|
salt does not match the values used to encrypt the data.
|
|
"""
|
|
|
|
try:
|
|
return fernet.Fernet(_generate_key(
|
|
passphrase, salt, key_length, iterations)).decrypt(encrypted_data)
|
|
except fernet.InvalidToken:
|
|
LOG.error('Signature verification to decrypt secrets failed. Please '
|
|
'check your provided passphrase and salt and try again.')
|
|
raise
|
|
|
|
|
|
def _generate_key(passphrase, salt, key_length, iterations):
|
|
"""
|
|
Use the passphrase and salt and PBKDF2HMAC key derivation algorithm,
|
|
to generate and return a Fernet key to be used for encryption and
|
|
decryption of secret data.
|
|
|
|
:param passphrase: Passphrase to use to generate decryption key. Must be
|
|
at least 24-byte long.
|
|
:type passphrase: bytes
|
|
:param salt: salt to use to generate decryption key. Must be randomly
|
|
generated.
|
|
:type salt: bytes
|
|
:param key_length: Length of the decryption key to generate, in bytes.
|
|
Will default to 32, if not provided.
|
|
:type key_length: positive integer.
|
|
:param iterations: A large number, used as seed to increase the entropy
|
|
of the randomness of the generated key. will default to 10000, if not
|
|
provided.
|
|
:type iterations: positive integer.
|
|
:return: base64 encoded, URL safe Fernet key for encryption or decryption
|
|
"""
|
|
|
|
kdf = PBKDF2HMAC(
|
|
algorithm=hashes.SHA256(),
|
|
length=key_length,
|
|
salt=salt,
|
|
iterations=iterations,
|
|
backend=default_backend())
|
|
return base64.urlsafe_b64encode(kdf.derive(passphrase))
|