From 3183a801c7c1b97287234e225f096c93568a616a Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Wed, 17 Jun 2015 18:17:39 +0000 Subject: [PATCH] Test to ensure fernet key rotation results in new key sets This amends the existing rotation test to ensure that after each key rotation, a completely new set of keys exists on disk. This is just a paranoid test to ensure that the key rotation strategy isn't something terribly dumb like "shuffle all key files on disk without creating any new ones." Change-Id: Ie897bd9d41ff96ed6819cbee0053853701afc9d1 --- .../tests/unit/token/test_fernet_provider.py | 71 +++++++++++++++++-- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/keystone/tests/unit/token/test_fernet_provider.py b/keystone/tests/unit/token/test_fernet_provider.py index 20846e82da..5a676bd56d 100644 --- a/keystone/tests/unit/token/test_fernet_provider.py +++ b/keystone/tests/unit/token/test_fernet_provider.py @@ -11,6 +11,7 @@ # under the License. import datetime +import hashlib import os import uuid @@ -335,6 +336,13 @@ class TestPayloads(tests.TestCase): class TestFernetKeyRotation(tests.TestCase): + def setUp(self): + super(TestFernetKeyRotation, self).setUp() + + # A collection of all previously-seen signatures of the key + # repository's contents. + self.key_repo_signatures = set() + @property def keys(self): """Key files converted to numbers.""" @@ -346,6 +354,49 @@ class TestFernetKeyRotation(tests.TestCase): """The number of keys in the key repository.""" return len(self.keys) + @property + def key_repository_signature(self): + """Create a "thumbprint" of the current key repository. + + Because key files are renamed, this produces a hash of the contents of + the key files, ignoring their filenames. + + The resulting signature can be used, for example, to ensure that you + have a unique set of keys after you perform a key rotation (taking a + static set of keys, and simply shuffling them, would fail such a test). + + """ + # Load the keys into a list. + keys = fernet_utils.load_keys() + + # Sort the list of keys by the keys themselves (they were previously + # sorted by filename). + keys.sort() + + # Create the thumbprint using all keys in the repository. + signature = hashlib.sha1() + for key in keys: + signature.update(key) + return signature.hexdigest() + + def assertRepositoryState(self, expected_size): + """Validate the state of the key repository.""" + self.assertEqual(expected_size, self.key_repository_size) + self.assertUniqueRepositoryState() + + def assertUniqueRepositoryState(self): + """Ensures that the current key repo state has not been seen before.""" + # This is assigned to a variable because it takes some work to + # calculate. + signature = self.key_repository_signature + + # Ensure the signature is not in the set of previously seen signatures. + self.assertNotIn(signature, self.key_repo_signatures) + + # Add the signature to the set of repository signatures to validate + # that we don't see it again later. + self.key_repo_signatures.add(signature) + def test_rotation(self): # Initializing a key repository results in this many keys. We don't # support max_active_keys being set any lower. @@ -361,31 +412,37 @@ class TestFernetKeyRotation(tests.TestCase): # active keys. self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) + # Validate the initial repository state. + self.assertRepositoryState(expected_size=min_active_keys) + + # The repository should be initialized with a staged key (0) and a + # primary key (1). The next key is just auto-incremented. exp_keys = [0, 1] - key_no = 2 # keep track of next key + next_key_number = exp_keys[-1] + 1 # keep track of next key self.assertEqual(exp_keys, self.keys) # Rotate the keys just enough times to fully populate the key # repository. for rotation in range(max_active_keys - min_active_keys): fernet_utils.rotate_keys() - self.assertEqual(rotation + 3, self.key_repository_size) + self.assertRepositoryState(expected_size=rotation + 3) - exp_keys.append(key_no) - key_no += 1 + exp_keys.append(next_key_number) + next_key_number += 1 self.assertEqual(exp_keys, self.keys) + # We should have a fully populated key repository now. self.assertEqual(max_active_keys, self.key_repository_size) # Rotate an additional number of times to ensure that we maintain # the desired number of active keys. for rotation in range(10): fernet_utils.rotate_keys() - self.assertEqual(max_active_keys, self.key_repository_size) + self.assertRepositoryState(expected_size=max_active_keys) exp_keys.pop(1) - exp_keys.append(key_no) - key_no += 1 + exp_keys.append(next_key_number) + next_key_number += 1 self.assertEqual(exp_keys, self.keys) def test_non_numeric_files(self):