diff --git a/taf/api/roles.py b/taf/api/roles.py index 3f37bbbe..4ebe1f1a 100644 --- a/taf/api/roles.py +++ b/taf/api/roles.py @@ -105,7 +105,7 @@ def add_role( _create_delegations( roles_infos, auth_repo, verification_keys, signing_keys, existing_roles ) - _update_role(auth_repo, parent_role, keystore, roles_infos, scheme=scheme) + _update_role(auth_repo, parent_role, keystore, scheme=scheme) if commit: update_snapshot_and_timestamp(auth_repo, keystore, scheme=scheme) commit_message = input("\nEnter commit message and press ENTER\n\n") @@ -148,6 +148,8 @@ def add_role_paths( auth_repo.commit(commit_message) +@log_on_end(DEBUG, "Adding new roles", logger=taf_logger) +@log_on_end(DEBUG, "Finished adding new roles", logger=taf_logger) def add_roles( auth_path, keystore=None, @@ -160,8 +162,8 @@ def add_roles( Arguments: auth_path: Path to the authentication repository. - keystore: Location of the keystore files. - roles_key_infos: A dictionary containing information about the roles: + keystore (optional): Location of the keystore files. + roles_key_infos (optional): A dictionary containing information about the roles: - total number of keys per role - their parent roles - threshold of signatures per role @@ -172,7 +174,7 @@ def add_roles( scheme (optional): Signing scheme. Set to rsa-pkcs1v15-sha256 by default. Side Effects: - Updates metadata files (parent of new roles, snapshot and timestamp) and creates the metadata files. + Updates metadata files (parent of new roles, snapshot and timestamp) and creates new targets metadata files. Writes changes to disk. Returns: @@ -238,8 +240,10 @@ def add_roles( update_snapshot_and_timestamp(auth_repo, keystore, scheme=scheme) +@log_on_end(DEBUG, "Adding new signing key to roles", logger=taf_logger) +@log_on_end(DEBUG, "Finished adding new signing key to roles", logger=taf_logger) def add_signing_key( - repo_path, + auth_path, roles, pub_key_path=None, keystore=None, @@ -247,10 +251,32 @@ def add_signing_key( scheme=DEFAULT_RSA_SIGNATURE_SCHEME, ): """ - Adds a new signing key to the listed roles. Automatically updates timestamp and - snapshot. + Add a new signing key to the listed roles. Update root metadata if one or more roles is one of the main TUF roles, + parent target role if one of the roles is a delegated target role and timestamp and snapshot in any case. + + Arguments: + auth_path: Path to the authentication repository. + roles: A list of roles whose signing keys need to be extended. + pub_key_path (optional): path to the file containing the public component of the new key. If not provided, + it will be necessary to ender the key when prompted. + keystore (optional): Location of the keystore files. + roles_key_infos (optional): A dictionary containing information about the roles: + - total number of keys per role + - their parent roles + - threshold of signatures per role + - should keys of a role be on Yubikeys or should a keystore files be used + - scheme (the default scheme is rsa-pkcs1v15-sha256) + - keystore path, if not specified via keystore option + scheme (optional): Signing scheme. Set to rsa-pkcs1v15-sha256 by default. + + Side Effects: + Updates metadata files (parents of the affected roles, snapshot and timestamp). + Writes changes to disk. + + Returns: + None """ - taf_repo = Repository(repo_path) + taf_repo = Repository(auth_path) roles_key_infos, keystore = _initialize_roles_and_keystore( roles_key_infos, keystore, enter_info=False ) @@ -285,7 +311,7 @@ def add_signing_key( taf_repo.unmark_dirty_roles(list(set(roles) - parent_roles)) for parent_role in parent_roles: - _update_role(taf_repo, parent_role, keystore, roles_infos, scheme) + _update_role(taf_repo, parent_role, keystore, scheme=scheme) update_snapshot_and_timestamp(taf_repo, keystore, scheme=scheme) @@ -294,6 +320,24 @@ def _enter_roles_infos(keystore, roles_key_infos): """ Ask the user to enter information taf roles and keys, including the location of keystore directory if not entered through an input parameter + + Arguments: + keystore: Location of the keystore files. + roles_key_infos: A dictionary containing information about the roles: + - total number of keys per role + - their parent roles + - threshold of signatures per role + - should keys of a role be on Yubikeys or should a keystore files be used + - scheme (the default scheme is rsa-pkcs1v15-sha256) + - keystore path, if not specified via keystore option + + Side Effects: + None + + Returns: + A dictionary containing entered information about taf roles and keys (total number of keys per role, + parent roles of roles, threshold of signatures per role, indicator if metadata should be signed using + a yubikey for each role, key length and signing scheme for each role) """ mandatory_roles = ["root", "targets", "snapshot", "timestamp"] role_key_infos = defaultdict(dict) @@ -412,8 +456,28 @@ def _read_val(input_type, name, param=None, required=False): def _initialize_roles_and_keystore(roles_key_infos, keystore, enter_info=True): """ - Read or enter roles information and try to extract keystore path from - that json + Read information about roles and keys from a json file or ask the user to enter + this information if not specified through a json file enter_info is True + + Arguments: + roles_key_infos: A dictionary containing information about the roles: + - total number of keys per role + - their parent roles + - threshold of signatures per role + - should keys of a role be on Yubikeys or should a keystore files be used + - scheme (the default scheme is rsa-pkcs1v15-sha256) + - keystore path, if not specified via keystore option + keystore: Location of the keystore files. + enter_info (optional): Indicates if the user should be asked to enter information about the. + roles and keys if not specified. Set to True by default. + + Side Effects: + None + + Returns: + A dictionary containing entered information about taf roles and keys (total number of keys per role, + parent roles of roles, threshold of signatures per role, indicator if metadata should be signed using + a yubikey for each role, key length and signing scheme for each role) and keystore file path """ roles_key_infos_dict = read_input_dict(roles_key_infos) if keystore is None: @@ -441,6 +505,28 @@ def _initialize_roles_and_keystore(roles_key_infos, keystore, enter_info=True): def _create_delegations( roles_infos, repository, verification_keys, signing_keys, existing_roles=None ): + """ + Initialize new delegated target roles, update authentication repository object + + Arguments: + roles_infos: A dictionary containing information about the roles: + - total number of keys per role + - their parent roles + - threshold of signatures per role + - should keys of a role be on Yubikeys or should a keystore files be used + - scheme (the default scheme is rsa-pkcs1v15-sha256) + - keystore path, if not specified via keystore option + repository: Authentication repository. + verification_keys: A dictionary containing mappings of role names to their verification (public) keys. + signing_keys: A dictionary containing mappings of role names to their signing (private) keys. + existing_roles: A list of already initialized roles. + + Side Effects: + Updates authentication repository object + + Returns: + None + """ if existing_roles is None: existing_roles = [] for role_name, role_info in roles_infos.items(): @@ -487,6 +573,9 @@ def _get_roles_key_size(role, keystore, keys_num): def _role_obj(role, repository, parent=None): + """ + Return role TUF object based on its name + """ repository = repository._tuf_repository if role == "targets": return repository.targets @@ -503,6 +592,8 @@ def _role_obj(role, repository, parent=None): return parent(role) +@log_on_end(DEBUG, "Removing role {role:s}", logger=taf_logger) +@log_on_end(DEBUG, "Finished removing the role", logger=taf_logger) def remove_role( auth_path: str, role: str, @@ -512,6 +603,27 @@ def remove_role( remove_targets: bool = False, auth_repo: AuthenticationRepository = None, ): + """ + Remove a delegated target role and update and sign metadata files. + Automatically commit the changes if commit is set to True. + It is not possible to remove any of the main TUF roles + + Arguments: + auth_path: Path to the authentication repository. + role: Name of the role which is to be removed. + keystore: Location of the keystore files. + scheme (optional): Signing scheme. Set to rsa-pkcs1v15-sha256 by default. + commit (optional): Specifies if the changes should be automatically committed. Set to True by default + remove_targets (optional): Indicates if target files should be removed to, or signed by the parent role. + Set to False by default. + auth_repo (optional): Instance of the authentication repository. Will be created if not passed into the function. + + Side Effects: + Updates metadata files, optionally deletes target files, writes changes to disk and optionally commits. + + Returns: + None + """ if role in MAIN_ROLES: print( f"Cannot remove role {role}. It is one of the roles required by the TUF specification" @@ -576,7 +688,26 @@ def remove_role( auth_repo.commit(commit_message) +@log_on_end(DEBUG, "Removing paths", logger=taf_logger) +@log_on_end(DEBUG, "Finished removing paths", logger=taf_logger) def remove_paths(paths, keystore, commit=True, auth_repo=None, auth_path=None): + """ + Remove delegated paths. Update parent roles of the roles associated with the removed paths, + as well as snapshot and timestamp. Optionally commit the changes. + + Arguments: + paths: Paths to be removed. + keystore: Location of the keystore files. + commit (optional): Specifies if the changes should be automatically committed. Set to True by default + auth_repo (optional): Instance of the authentication repository. Needs to be specified if auth_path is not. + auth_path: Path to the authentication repository. Needs to be specified if auth_repo is None. + + Side Effects: + Updates metadata files, writes changes to disk and optionally commits them. + + Returns: + None + """ if auth_repo is None: auth_repo = AuthenticationRepository(path=auth_path) for path in paths: @@ -616,6 +747,9 @@ def _setup_role( signing_keys=None, parent=None, ): + """ + Initialize a new role, add signing and verification keys. + """ role_obj = _role_obj(role_name, repository, parent) role_obj.threshold = threshold if not is_yubikey: @@ -632,7 +766,7 @@ def _setup_role( def _update_role( - taf_repo, role, keystore, roles_infos=None, scheme=DEFAULT_RSA_SIGNATURE_SCHEME + taf_repo, role, keystore, scheme=DEFAULT_RSA_SIGNATURE_SCHEME ): keystore_keys, yubikeys = load_signing_keys(taf_repo, role, keystore, scheme=scheme) if len(keystore_keys):