Skip to content

Commit

Permalink
Merge pull request #930 from lukpueh/correctly-rotate-root-test
Browse files Browse the repository at this point in the history
Add tests for multiple root key rotation
  • Loading branch information
lukpueh committed Oct 8, 2019
2 parents 3d342e6 + 4d7bb69 commit 4f9faf1
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 2 deletions.
1 change: 1 addition & 0 deletions tests/repository_data/keystore/root_key2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
77c02ab5647ee765d5f6c5fc202a5b32@@@@100000@@@@7c73c1100fab52dc8695c1b955d31770ed6e53f1820d9020aeb6541c948573d9@@@@98280307ffa9c5f6ff1fea1a4b79d0ea@@@@f3342882b1cf842e3377ab4205c0ca8fab564cc55fa742f55b364a1ac597e93d8c56a9a6e6bbb6a812556077be44a1066ac6781a6ed34b86beaf3985f846f007dab31c46af562e921f03c1ea8d299f15324ab137aa426ee61d396a7e20191aa71a70b670775b2ad48f25de367fb48881c55e93f468c6e59402907e82985c27c94c715161c85c5c1904353ba33c3d129988029f03a2d7d00720118697baaf73a3c4e72f8e538b4323866fe525ddccfcfc6dd45598545f65cd7ab581f5172bc253416283a66621eb03dbabaf33923bb1963f9f8cbae6fd6a1c86736a8f80c8d1ba3cbc3f53b0123ba9b0bdd44f25b65033b19a50ee978d2687d6a2ee724515a20026d0213ced59cda9bfdf37c82c59e1356795fd603d85996f448a3c9357b32de2042997a1d27353ee3866c0ed5218d633e0b28991119d77d147354c7fa2de8a168d17efdfd5fa9a8e528bd47ede4ff697
1 change: 1 addition & 0 deletions tests/repository_data/keystore/root_key2.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"keytype": "ed25519", "scheme": "ed25519", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3ba219e69666298bce5d1d653a166346aef807c02e32a846aaefcb5190fddeb4"}}
1 change: 1 addition & 0 deletions tests/repository_data/keystore/root_key3
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ce4624d30171067445ed3fa863f66127@@@@100000@@@@f10f918e9e895ba72fb784e2dccc1b09e4cbc17ff23eda55687e272e217bb09f@@@@63982fe353cdb82ed7825e9569804f0e@@@@9bd085924cbbfff9fbf4cad01f8119a3726b734ea46c573e4d39239c238285a332a73fe1d3e9966a6e958c387f4eec40a0b3e7883b6d347f5506d64f3acf06148261afeca9585e7fd71e2726373a481627bd82c545fdf031bbde9bb3db986e6de29efcad3fc5d7ae5c274d4e0e9b521af6e34263965fa7e5b7e10c7dee4c880424930d4f698ff5a0531ab4a430c74fe6bb831db7c40b0c097c6c22c6cae2b25c712cbf02638cee3cdfea9854ef690b4707e27851ed5e63cda60925e3e4e49467ef031e31dd9524b51e635697d3049cec09ea86cebf60fb2082247e784d48b0c71ef79a4fc4807ad2905140c051795f904baaacee334e34a4d82a78befe735810a77ac40d1a977460026f8513dbccd7fd5c980cc5397d223adc132a6b7230448685f6f1aec40e2a9d6ab39d818c94c7ace66e6a87cc94b690a2018fb3b376010c05ac0587631122d6979b0f575d18655d
1 change: 1 addition & 0 deletions tests/repository_data/keystore/root_key3.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"keytype": "ed25519", "scheme": "ed25519", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "e68d6e173fe21d8bc4a558606784abdbb71f31cd13fa2aeef29972f60f5c5809"}}
108 changes: 106 additions & 2 deletions tests/test_updater_root_rotation_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import subprocess
import sys
import unittest
import filecmp

import tuf
import tuf.log
Expand All @@ -60,6 +61,7 @@
import tuf.repository_tool as repo_tool
import tuf.unittest_toolbox as unittest_toolbox
import tuf.client.updater as updater
import tuf.settings

import securesystemslib
import six
Expand Down Expand Up @@ -213,9 +215,98 @@ def test_root_rotation(self):
shutil.rmtree(os.path.join(self.repository_directory, 'metadata'))
shutil.copytree(os.path.join(self.repository_directory, 'metadata.staged'),
os.path.join(self.repository_directory, 'metadata'))
self.repository_updater.refresh()



def test_root_rotation_full(self):
"""Test that a client whose root is outdated by multiple versions and who
has none of the latest nor next-to-latest root keys can still update and
does so by incrementally verifying all roots until the most recent one. """
# Load initial repository with 1.root.json == root.json, signed by "root"
# key. This is the root.json that is already on the client.
repository = repo_tool.load_repository(self.repository_directory)

# 1st rotation: 1.root.json --> 2.root.json
# 2.root.json will be signed by previous "root" key and by new "root2" key
repository.root.load_signing_key(self.role_keys['root']['private'])
repository.root.add_verification_key(self.role_keys['root2']['public'])
repository.root.load_signing_key(self.role_keys['root2']['private'])
repository.writeall()

# 2nd rotation: 2.root.json --> 3.root.json
# 3.root.json will be signed by previous "root2" key and by new "root3" key
repository.root.unload_signing_key(self.role_keys['root']['private'])
repository.root.remove_verification_key(self.role_keys['root']['public'])
repository.root.add_verification_key(self.role_keys['root3']['public'])
repository.root.load_signing_key(self.role_keys['root3']['private'])
repository.writeall()

# Move staged metadata to "live" metadata
shutil.rmtree(os.path.join(self.repository_directory, 'metadata'))
shutil.copytree(os.path.join(self.repository_directory, 'metadata.staged'),
os.path.join(self.repository_directory, 'metadata'))

# Update on client 1.root.json --> 2.root.json --> 3.root.json
self.repository_updater.refresh()

# Assert that client updated to the latest root from the repository
self.assertTrue(filecmp.cmp(
os.path.join(self.repository_directory, 'metadata', '3.root.json'),
os.path.join(self.client_metadata_current, 'root.json')))



def test_root_rotation_max(self):
"""Test that client does not rotate beyond a configured upper bound, i.e.
`current_version + MAX_NUMBER_ROOT_ROTATIONS`. """
# NOTE: The nature of below root changes is irrelevant. Here we only want
# the client to update but not beyond a configured upper bound.

# 1.root.json --> 2.root.json (add root2 and root3 keys)
repository = repo_tool.load_repository(self.repository_directory)
repository.root.load_signing_key(self.role_keys['root']['private'])
repository.root.add_verification_key(self.role_keys['root2']['public'])
repository.root.load_signing_key(self.role_keys['root2']['private'])
repository.root.add_verification_key(self.role_keys['root3']['public'])
repository.root.load_signing_key(self.role_keys['root3']['private'])
repository.writeall()

# 2.root.json --> 3.root.json (change threshold)
repository.root.threshold = 2
repository.writeall()

# 3.root.json --> 4.root.json (change threshold again)
repository.root.threshold = 3
repository.writeall()

# Move staged metadata to "live" metadata
shutil.rmtree(os.path.join(self.repository_directory, 'metadata'))
shutil.copytree(os.path.join(self.repository_directory, 'metadata.staged'),
os.path.join(self.repository_directory, 'metadata'))

# Assert that repo indeed has "4.root.json" and that it's the latest root
self.assertTrue(filecmp.cmp(
os.path.join(self.repository_directory, 'metadata', '4.root.json'),
os.path.join(self.repository_directory, 'metadata', 'root.json')))

# Lower max root rotation cap so that client stops updating early
max_rotation_backup = tuf.settings.MAX_NUMBER_ROOT_ROTATIONS
tuf.settings.MAX_NUMBER_ROOT_ROTATIONS = 2

# Update on client 1.root.json --> 2.root.json --> 3.root.json,
# but stop before updating to 4.root.json
self.repository_updater.refresh()

# Assert that the client indeed only updated until 3.root.json
self.assertTrue(filecmp.cmp(
os.path.join(self.repository_directory, 'metadata', '3.root.json'),
os.path.join(self.client_metadata_current, 'root.json')))

# reset
tuf.settings.MAX_NUMBER_ROOT_ROTATIONS = max_rotation_backup



def test_root_rotation_missing_keys(self):
repository = repo_tool.load_repository(self.repository_directory)
Expand Down Expand Up @@ -327,6 +418,7 @@ def test_root_rotation_unmet_threshold(self):




def _load_role_keys(keystore_directory):

# Populating 'self.role_keys' by importing the required public and private
Expand All @@ -342,17 +434,23 @@ def _load_role_keys(keystore_directory):
role_keys = {}

root_key_file = os.path.join(keystore_directory, 'root_key')
root2_key_file = os.path.join(keystore_directory, 'root_key2')
root3_key_file = os.path.join(keystore_directory, 'root_key3')
targets_key_file = os.path.join(keystore_directory, 'targets_key')
snapshot_key_file = os.path.join(keystore_directory, 'snapshot_key')
timestamp_key_file = os.path.join(keystore_directory, 'timestamp_key')
delegation_key_file = os.path.join(keystore_directory, 'delegation_key')

role_keys = {'root': {}, 'targets': {}, 'snapshot': {}, 'timestamp': {},
'role1': {}}
role_keys = {'root': {}, 'root2': {}, 'root3': {}, 'targets': {}, 'snapshot':
{}, 'timestamp': {}, 'role1': {}}

# Import the top-level and delegated role public keys.
role_keys['root']['public'] = \
repo_tool.import_rsa_publickey_from_file(root_key_file+'.pub')
role_keys['root2']['public'] = \
repo_tool.import_ed25519_publickey_from_file(root2_key_file+'.pub')
role_keys['root3']['public'] = \
repo_tool.import_ed25519_publickey_from_file(root3_key_file+'.pub')
role_keys['targets']['public'] = \
repo_tool.import_ed25519_publickey_from_file(targets_key_file+'.pub')
role_keys['snapshot']['public'] = \
Expand All @@ -366,6 +464,12 @@ def _load_role_keys(keystore_directory):
role_keys['root']['private'] = \
repo_tool.import_rsa_privatekey_from_file(root_key_file,
EXPECTED_KEYFILE_PASSWORD)
role_keys['root2']['private'] = \
repo_tool.import_ed25519_privatekey_from_file(root2_key_file,
EXPECTED_KEYFILE_PASSWORD)
role_keys['root3']['private'] = \
repo_tool.import_ed25519_privatekey_from_file(root3_key_file,
EXPECTED_KEYFILE_PASSWORD)
role_keys['targets']['private'] = \
repo_tool.import_ed25519_privatekey_from_file(targets_key_file,
EXPECTED_KEYFILE_PASSWORD)
Expand Down

0 comments on commit 4f9faf1

Please sign in to comment.