From aa241081c9eb846c8ab2efe0afbf1ef9ec05f8c0 Mon Sep 17 00:00:00 2001 From: Tin Lam Date: Sun, 3 Feb 2019 00:28:51 -0600 Subject: [PATCH] Fix exception handling and add tests Per [0], fernet decrypt can never throw an InvalidSignature exception as that is re-raised as InvalidToken. This patch set corrects the handling of the exception, and added additional unit tests for coverage. [0] https://cryptography.io/en/latest/fernet/#cryptography.fernet.Fernet.decrypt Co-Authored-By: Drew Walters Change-Id: Ic5ee7ef451a5657519c5397fc4b903b5adcb1d18 Signed-off-by: Tin Lam --- pegleg/engine/util/encryption.py | 15 +++++------ test-requirements.txt | 1 + .../unit/engine/test_generate_cryptostring.py | 24 +++++++++++++++++ tests/unit/test_exceptions.py | 26 +++++++++++++++++++ tox.ini | 2 +- 5 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 tests/unit/test_exceptions.py diff --git a/pegleg/engine/util/encryption.py b/pegleg/engine/util/encryption.py index c822cbc9..bd575f59 100644 --- a/pegleg/engine/util/encryption.py +++ b/pegleg/engine/util/encryption.py @@ -15,8 +15,7 @@ import base64 import logging -from cryptography.exceptions import InvalidSignature -from cryptography.fernet import Fernet +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 @@ -57,8 +56,8 @@ def encrypt(unencrypted_data, :rtype: bytes """ - return Fernet(_generate_key(passphrase, salt, key_length, - iterations)).encrypt(unencrypted_data) + return fernet.Fernet(_generate_key( + passphrase, salt, key_length, iterations)).encrypt(unencrypted_data) def decrypt(encrypted_data, @@ -88,14 +87,14 @@ def decrypt(encrypted_data, :type iterations: positive integer. :return: Decrypted secret data :rtype: bytes - :raises InvalidSignature: If the provided passphrase, and/or + :raises InvalidToken: If the provided passphrase, and/or salt does not match the values used to encrypt the data. """ try: - return Fernet(_generate_key(passphrase, salt, key_length, - iterations)).decrypt(encrypted_data) - except InvalidSignature: + 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 diff --git a/test-requirements.txt b/test-requirements.txt index 1f282300..5a165747 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,6 +1,7 @@ # Testing pytest==3.2.1 pytest-cov==2.5.1 +testfixtures mock==2.0.0 # Formatting diff --git a/tests/unit/engine/test_generate_cryptostring.py b/tests/unit/engine/test_generate_cryptostring.py index 2797e8f3..c05bb8bb 100644 --- a/tests/unit/engine/test_generate_cryptostring.py +++ b/tests/unit/engine/test_generate_cryptostring.py @@ -14,9 +14,13 @@ import os import tempfile +import uuid +from cryptography import fernet import mock +import pytest import string +from testfixtures import log_capture import yaml from pegleg.engine.util.cryptostring import CryptoString @@ -176,3 +180,23 @@ def test_generate_passphrases(*_): assert len(decrypted_passphrase) == 25 else: assert len(decrypted_passphrase) == 24 + + +@log_capture() +def test_generate_passphrases_exception(capture): + unenc_data = uuid.uuid4().bytes + passphrase1 = uuid.uuid4().bytes + passphrase2 = uuid.uuid4().bytes + salt1 = uuid.uuid4().bytes + salt2 = uuid.uuid4().bytes + + # Generate random data and encrypt it + enc_data = encryption.encrypt(unenc_data, passphrase1, salt1) + + # Decrypt using the wrong key to see to see the InvalidToken error + with pytest.raises(fernet.InvalidToken): + encryption.decrypt(enc_data, passphrase2, salt2) + capture.check(('pegleg.engine.util.encryption', 'ERROR', + ('Signature verification to decrypt secrets failed. ' + 'Please check your provided passphrase and salt and ' + 'try again.'))) diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py new file mode 100644 index 00000000..2d5774f6 --- /dev/null +++ b/tests/unit/test_exceptions.py @@ -0,0 +1,26 @@ +# 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 logging + +import pytest +from testfixtures import log_capture + +from pegleg.engine import exceptions as exc + + +@log_capture() +def test_exception_with_missing_kwargs(capture): + message = 'Testing missing kwargs exception with {text}' + with pytest.raises(exc.PeglegBaseException): + raise exc.PeglegBaseException(message=message, key="value") + capture.check(('pegleg.engine.exceptions', 'WARNING', 'Missing kwargs')) diff --git a/tox.ini b/tox.ini index 50e498c1..46dceddf 100644 --- a/tox.ini +++ b/tox.ini @@ -60,7 +60,7 @@ commands = {toxinidir}/tools/install-cfssl.sh bash -c 'PATH=$PATH:~/.local/bin; pytest --cov=pegleg --cov-report \ html:cover --cov-report xml:cover/coverage.xml --cov-report term \ - --cov-fail-under 84 tests/' + --cov-fail-under 86 tests/' whitelist_externals = bash