diff --git a/docs/CLI.md b/docs/CLI.md index c34b891f..3df06d9f 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -86,14 +86,12 @@ the Targets role or its key is used. The Snapshot and Timestamp role are also automatically signed, if possible. ```Bash $ repo.py --sign -$ repo.py --sign -$ repo.py --sign [--role ] $ repo.py --sign [--role , --path ] ``` For example, to sign new Timestamp metadata: ```Bash -$ repo.py --sign /path/to/timestamp_key --role timestamp +$ repo.py --sign /path/to/foo_key --role foo ``` Note: In the future, the user might have the option of disabling automatic @@ -112,7 +110,8 @@ $ repo.py --delegate ... --delegatee --pubkeys --sign ] ``` -Example: +For example, to delegate trust of `/foo*.gz` packages to the `foo` role: + ``` $ repo.py --delegate "/foo*.tgz" --delegatee foo --pubkeys ./keystore/foo.pub ``` diff --git a/tests/test_repository_tool.py b/tests/test_repository_tool.py index 0bf8b318..7d2e6e6c 100755 --- a/tests/test_repository_tool.py +++ b/tests/test_repository_tool.py @@ -1189,12 +1189,6 @@ def test_delegate(self): self.assertEqual(self.targets_object.get_delegated_rolenames(), ['tuf']) - # Try to delegate to a role that has already been delegated. - self.assertRaises(securesystemslib.exceptions.Error, - self.targets_object.delegate, rolename, public_keys, paths, threshold, - terminating=False, list_of_targets=list_of_targets, - path_hash_prefixes=path_hash_prefixes) - # Test for targets that do not exist under the targets directory. self.targets_object.revoke(rolename) self.assertRaises(securesystemslib.exceptions.Error, diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 673941f3..abeaa1e9 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -2233,11 +2233,6 @@ def delegate(self, rolename, public_keys, paths, threshold=1, if path_hash_prefixes is not None: securesystemslib.formats.PATH_HASH_PREFIXES_SCHEMA.check_match(path_hash_prefixes) - # Check if 'rolename' is not already a delegation. - if tuf.roledb.role_exists(rolename, self._repository_name): - raise securesystemslib.exceptions.Error(repr(rolename) + ' already' - ' delegated.') - # Keep track of the valid keyids (added to the new Targets object) and # their keydicts (added to this Targets delegations). keyids = [] diff --git a/tuf/scripts/repo.py b/tuf/scripts/repo.py index 236d0b5d..a5facafd 100755 --- a/tuf/scripts/repo.py +++ b/tuf/scripts/repo.py @@ -52,6 +52,7 @@ import shutil import errno import getpass +import time import tuf import tuf.log @@ -147,7 +148,7 @@ def delegate(parsed_arguments): public_keys = [] for public_key in parsed_arguments.pubkeys: # In the future, any type of key can be imported... - imported_pubkey = repo_tool.import_ecdsa_publickey_from_file( + imported_pubkey = import_publickey_from_file( public_key) public_keys.append(imported_pubkey) @@ -329,6 +330,21 @@ def import_privatekey_from_file(keypath, password=None): +def import_publickey_from_file(keypath): + + key_metadata = securesystemslib.util.load_json_file(keypath) + key_object, junk = securesystemslib.keys.format_metadata_to_key(key_metadata) + + if key_object['keytype'] not in SUPPORTED_KEY_TYPES: + raise tuf.exceptions.Error('Trying to import an unsupported key' + ' type: ' + repr(key_object['keytype'] + '.' + ' Supported key types: ' + repr(SUPPORTED_KEY_TYPES))) + + else: + return key_object + + + def sign_role(parsed_arguments): repository = repo_tool.load_repository( @@ -351,7 +367,32 @@ def sign_role(parsed_arguments): pass else: - repository.targets(parsed_arguments.role).load_signing_key(role_privatekey) + # TODO: The repository tool will be refactored to clean up the following + # approach, which adds and signs for a non-existent role. + if not tuf.roledb.role_exists(parsed_arguments.role): + + # Load the private key keydb and set the roleinfo in roledb so that + # metadata can be written with repository.write(). + tuf.keydb.remove_key(role_privatekey['keyid'], + repository_name = repository._repository_name) + tuf.keydb.add_key( + role_privatekey, repository_name = repository._repository_name) + + expiration = tuf.formats.unix_timestamp_to_datetime( + int(time.time() + 7889230)) + expiration = expiration.isoformat() + 'Z' + + roleinfo = {'name': parsed_arguments.role, 'keyids': [role_privatekey['keyid']], + 'signing_keyids': [role_privatekey['keyid']], 'partial_loaded': False, 'paths': {}, + 'signatures': [], 'version': 1, 'expires': expiration, + 'delegations': {'keys': {}, 'roles': []}} + + tuf.roledb.add_role(parsed_arguments.role, roleinfo, + repository_name=repository._repository_name) + repository.write(parsed_arguments.role, increment_version_number=False) + + else: + repository.targets(parsed_arguments.role).load_signing_key(role_privatekey) # Update the required top-level roles, Snapshot and Timestamp, to make a new # release. @@ -523,16 +564,16 @@ def set_top_level_keys(repository): # Import the public keys. They are needed so that metadata roles are # assigned verification keys, which clients need in order to verify the # signatures created by the corresponding private keys. - root_public = repo_tool.import_ecdsa_publickey_from_file( + root_public = import_publickey_from_file( os.path.join(parsed_arguments.path, KEYSTORE_DIR, ROOT_KEY_NAME) + '.pub') - targets_public = repo_tool.import_ecdsa_publickey_from_file( + targets_public = import_publickey_from_file( os.path.join(parsed_arguments.path, KEYSTORE_DIR, TARGETS_KEY_NAME) + '.pub') - snapshot_public = repo_tool.import_ecdsa_publickey_from_file( + snapshot_public = import_publickey_from_file( os.path.join(parsed_arguments.path, KEYSTORE_DIR, SNAPSHOT_KEY_NAME) + '.pub') - timestamp_public = repo_tool.import_ecdsa_publickey_from_file( + timestamp_public = import_publickey_from_file( os.path.join(parsed_arguments.path, KEYSTORE_DIR, TIMESTAMP_KEY_NAME) + '.pub')