From 29f168cd6d156823e2a470982d5ddf5c0fa64854 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Thu, 1 Feb 2018 13:44:39 -0500 Subject: [PATCH 1/4] Add --path option and have --init and --clean use it Signed-off-by: Vladimir Diaz --- tuf/scripts/repo.py | 85 ++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/tuf/scripts/repo.py b/tuf/scripts/repo.py index 23649614..0ab24e3b 100755 --- a/tuf/scripts/repo.py +++ b/tuf/scripts/repo.py @@ -58,9 +58,9 @@ PROG_NAME = 'repo.py' -DEFAULT_REPO_PATH = 'tufrepo' -DEFAULT_CLIENT_PATH = 'tufclient' -DEFAULT_KEYSTORE = 'tufkeystore' +DEFAULT_REPO_DIR = 'tufrepo' +DEFAULT_CLIENT_DIR = 'tufclient' +DEFAULT_KEYSTORE_DIR = 'tufkeystore' DEFAULT_ROOT_KEY = 'root_key' DEFAULT_TARGETS_KEY = 'targets_key' @@ -114,9 +114,9 @@ def process_arguments(parsed_arguments): def clean_repo(parsed_arguments): - repo_dir = os.path.join(parsed_arguments.clean, DEFAULT_REPO_PATH) - client_dir = os.path.join(parsed_arguments.clean, DEFAULT_CLIENT_PATH) - keystore_dir = os.path.join(parsed_arguments.clean, DEFAULT_KEYSTORE) + repo_dir = os.path.join(parsed_arguments.path, DEFAULT_REPO_DIR) + client_dir = os.path.join(parsed_arguments.path, DEFAULT_CLIENT_DIR) + keystore_dir = os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR) shutil.rmtree(repo_dir, ignore_errors=True) shutil.rmtree(client_dir, ignore_errors=True) @@ -125,8 +125,10 @@ def clean_repo(parsed_arguments): def write_to_live_repo(): - staged_meta_directory = os.path.join(DEFAULT_REPO_PATH, DEFAULT_STAGED_DIR) - live_meta_directory = os.path.join(DEFAULT_REPO_PATH, DEFAULT_METADATA_DIR) + staged_meta_directory = os.path.join( + parsed_arguments.path, DEFAULT_REPO_DIR, DEFAULT_STAGED_DIR) + live_meta_directory = os.path.join( + parsed_arguments.path, DEFAULT_REPO_DIR, DEFAULT_METADATA_DIR) shutil.rmtree(live_meta_directory, ignore_errors=True) shutil.copytree(staged_meta_directory, live_meta_directory) @@ -136,8 +138,8 @@ def write_to_live_repo(): def add_targets(parsed_arguments): target_paths = os.path.join(parsed_arguments.add) - repo_targets_path = os.path.join(DEFAULT_REPO_PATH, 'targets') - repository = repo_tool.load_repository(DEFAULT_REPO_PATH) + repo_targets_path = os.path.join(DEFAULT_REPO_DIR, 'targets') + repository = repo_tool.load_repository(DEFAULT_REPO_DIR) for target_path in target_paths: if not os.path.exists(target_path): @@ -158,11 +160,11 @@ def add_targets(parsed_arguments): # Load the top-level, non-root, keys to make a new release. targets_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_TARGETS_KEY), parsed_arguments.pw) + os.path.join(DEFAULT_KEYSTORE_DIR, DEFAULT_TARGETS_KEY), parsed_arguments.pw) snapshot_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_SNAPSHOT_KEY), parsed_arguments.pw) + os.path.join(DEFAULT_KEYSTORE_DIR, DEFAULT_SNAPSHOT_KEY), parsed_arguments.pw) timestamp_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_TIMESTAMP_KEY), parsed_arguments.pw) + os.path.join(DEFAULT_KEYSTORE_DIR, DEFAULT_TIMESTAMP_KEY), parsed_arguments.pw) repository.targets.load_signing_key(targets_private) repository.snapshot.load_signing_key(snapshot_private) @@ -177,10 +179,13 @@ def add_targets(parsed_arguments): def init_repo(parsed_arguments): """ - Create default repo. Each top-level role has one key, if - 'parsed_argument.bare' is False (default). + Create a repo at the specified location in --path (the current working + directory, by default). Each top-level role has one key, if --bare' is False + (default). """ - repository = repo_tool.create_new_repository(DEFAULT_REPO_PATH) + + repo_path = os.path.join(parsed_arguments.path, DEFAULT_REPO_DIR) + repository = repo_tool.create_new_repository(repo_path) if not parsed_arguments.bare: set_top_level_keys(repository) @@ -188,7 +193,8 @@ def init_repo(parsed_arguments): consistent_snapshot=parsed_arguments.consistent_snapshot) else: - repository.write('root', consistent_snapshot=parsed_arguments.consistent_snapshot) + repository.write( + 'root', consistent_snapshot=parsed_arguments.consistent_snapshot) repository.write('targets') repository.write('snapshot') repository.write('timestamp') @@ -198,8 +204,9 @@ def init_repo(parsed_arguments): # Create the client files. The client directory contains the required # directory structure and metadata files for clients to successfully perform # an update. - repo_tool.create_tuf_client_directory(DEFAULT_REPO_PATH, - os.path.join(DEFAULT_CLIENT_PATH, DEFAULT_REPO_PATH)) + repo_tool.create_tuf_client_directory( + os.path.join(parsed_arguments.path, DEFAULT_REPO_DIR), + os.path.join(parsed_arguments.path, DEFAULT_CLIENT_DIR, DEFAULT_REPO_DIR)) @@ -217,36 +224,48 @@ def set_top_level_keys(repository): prompt='Enter a password for the top-level role keys: ', confirm=True) repo_tool.generate_and_write_ecdsa_keypair( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_ROOT_KEY), password=parsed_arguments.pw) + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_ROOT_KEY), password=parsed_arguments.pw) repo_tool.generate_and_write_ecdsa_keypair( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_TARGETS_KEY), password=parsed_arguments.pw) + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_TARGETS_KEY), password=parsed_arguments.pw) repo_tool.generate_and_write_ecdsa_keypair( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_SNAPSHOT_KEY), password=parsed_arguments.pw) + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_SNAPSHOT_KEY), password=parsed_arguments.pw) repo_tool.generate_and_write_ecdsa_keypair( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_TIMESTAMP_KEY), password=parsed_arguments.pw) + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_TIMESTAMP_KEY), password=parsed_arguments.pw) # 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( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_ROOT_KEY) + '.pub') + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_ROOT_KEY) + '.pub') targets_public = repo_tool.import_ecdsa_publickey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_TARGETS_KEY) + '.pub') + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_TARGETS_KEY) + '.pub') snapshot_public = repo_tool.import_ecdsa_publickey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_SNAPSHOT_KEY) + '.pub') + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_SNAPSHOT_KEY) + '.pub') timestamp_public = repo_tool.import_ecdsa_publickey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_TIMESTAMP_KEY) + '.pub') + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_TIMESTAMP_KEY) + '.pub') # Import the private keys. They are needed to generate the signatures # included in metadata. root_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_ROOT_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_ROOT_KEY), parsed_arguments.pw) targets_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_TARGETS_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_TARGETS_KEY), parsed_arguments.pw) snapshot_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_SNAPSHOT_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_SNAPSHOT_KEY), parsed_arguments.pw) timestamp_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE, DEFAULT_TIMESTAMP_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, + DEFAULT_TIMESTAMP_KEY), parsed_arguments.pw) # Add the verification keys to the top-level roles. repository.root.add_verification_key(root_public) @@ -306,6 +325,10 @@ def parse_arguments(): parser.add_argument('-i', '--init', nargs='?', const='.', help='Create a repository.') + parser.add_argument('-p', '--path', nargs='?', default='.', + help='Specify a repository path. If used with --init, the initialized' + ' repository is saved to the specified path.') + parser.add_argument('-b', '--bare', type=bool, nargs='?', const=True, default=False, choices=[True, False], help='If initializing a repository, ' + repr(PROG_NAME) + ' should not' From fb61f1fee9a2baaf7d62bd24791de6b02669dbe7 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Thu, 1 Feb 2018 14:59:12 -0500 Subject: [PATCH 2/4] Refactor constants and enable --path with --add Signed-off-by: Vladimir Diaz --- tuf/scripts/repo.py | 96 +++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/tuf/scripts/repo.py b/tuf/scripts/repo.py index 0ab24e3b..7b9e1074 100755 --- a/tuf/scripts/repo.py +++ b/tuf/scripts/repo.py @@ -58,17 +58,17 @@ PROG_NAME = 'repo.py' -DEFAULT_REPO_DIR = 'tufrepo' -DEFAULT_CLIENT_DIR = 'tufclient' -DEFAULT_KEYSTORE_DIR = 'tufkeystore' +REPO_DIR = 'tufrepo' +CLIENT_DIR = 'tufclient' +KEYSTORE_DIR = 'tufkeystore' -DEFAULT_ROOT_KEY = 'root_key' -DEFAULT_TARGETS_KEY = 'targets_key' -DEFAULT_SNAPSHOT_KEY = 'snapshot_key' -DEFAULT_TIMESTAMP_KEY = 'timestamp_key' +ROOT_KEY_NAME = 'root_key' +TARGETS_KEY_NAME = 'targets_key' +SNAPSHOT_KEY_NAME = 'snapshot_key' +TIMESTAMP_KEY_NAME = 'timestamp_key' -DEFAULT_STAGED_DIR = 'metadata.staged' -DEFAULT_METADATA_DIR = 'metadata' +STAGED_METADATA_DIR = 'metadata.staged' +METADATA_DIR = 'metadata' @@ -114,9 +114,9 @@ def process_arguments(parsed_arguments): def clean_repo(parsed_arguments): - repo_dir = os.path.join(parsed_arguments.path, DEFAULT_REPO_DIR) - client_dir = os.path.join(parsed_arguments.path, DEFAULT_CLIENT_DIR) - keystore_dir = os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR) + repo_dir = os.path.join(parsed_arguments.path, REPO_DIR) + client_dir = os.path.join(parsed_arguments.path, CLIENT_DIR) + keystore_dir = os.path.join(parsed_arguments.path, KEYSTORE_DIR) shutil.rmtree(repo_dir, ignore_errors=True) shutil.rmtree(client_dir, ignore_errors=True) @@ -126,9 +126,9 @@ def clean_repo(parsed_arguments): def write_to_live_repo(): staged_meta_directory = os.path.join( - parsed_arguments.path, DEFAULT_REPO_DIR, DEFAULT_STAGED_DIR) + parsed_arguments.path, REPO_DIR, STAGED_METADATA_DIR) live_meta_directory = os.path.join( - parsed_arguments.path, DEFAULT_REPO_DIR, DEFAULT_METADATA_DIR) + parsed_arguments.path, REPO_DIR, METADATA_DIR) shutil.rmtree(live_meta_directory, ignore_errors=True) shutil.copytree(staged_meta_directory, live_meta_directory) @@ -138,8 +138,9 @@ def write_to_live_repo(): def add_targets(parsed_arguments): target_paths = os.path.join(parsed_arguments.add) - repo_targets_path = os.path.join(DEFAULT_REPO_DIR, 'targets') - repository = repo_tool.load_repository(DEFAULT_REPO_DIR) + repo_targets_path = os.path.join(parsed_arguments.path, REPO_DIR, 'targets') + repository = repo_tool.load_repository( + os.path.join(parsed_arguments.path, REPO_DIR)) for target_path in target_paths: if not os.path.exists(target_path): @@ -160,11 +161,14 @@ def add_targets(parsed_arguments): # Load the top-level, non-root, keys to make a new release. targets_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE_DIR, DEFAULT_TARGETS_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, TARGETS_KEY_NAME), + parsed_arguments.pw) snapshot_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE_DIR, DEFAULT_SNAPSHOT_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, SNAPSHOT_KEY_NAME), + parsed_arguments.pw) timestamp_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(DEFAULT_KEYSTORE_DIR, DEFAULT_TIMESTAMP_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + TIMESTAMP_KEY_NAME), parsed_arguments.pw) repository.targets.load_signing_key(targets_private) repository.snapshot.load_signing_key(snapshot_private) @@ -184,7 +188,7 @@ def init_repo(parsed_arguments): (default). """ - repo_path = os.path.join(parsed_arguments.path, DEFAULT_REPO_DIR) + repo_path = os.path.join(parsed_arguments.path, REPO_DIR) repository = repo_tool.create_new_repository(repo_path) if not parsed_arguments.bare: @@ -205,8 +209,8 @@ def init_repo(parsed_arguments): # directory structure and metadata files for clients to successfully perform # an update. repo_tool.create_tuf_client_directory( - os.path.join(parsed_arguments.path, DEFAULT_REPO_DIR), - os.path.join(parsed_arguments.path, DEFAULT_CLIENT_DIR, DEFAULT_REPO_DIR)) + os.path.join(parsed_arguments.path, REPO_DIR), + os.path.join(parsed_arguments.path, CLIENT_DIR, REPO_DIR)) @@ -224,48 +228,48 @@ def set_top_level_keys(repository): prompt='Enter a password for the top-level role keys: ', confirm=True) repo_tool.generate_and_write_ecdsa_keypair( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_ROOT_KEY), password=parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + ROOT_KEY_NAME), password=parsed_arguments.pw) repo_tool.generate_and_write_ecdsa_keypair( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_TARGETS_KEY), password=parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + TARGETS_KEY_NAME), password=parsed_arguments.pw) repo_tool.generate_and_write_ecdsa_keypair( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_SNAPSHOT_KEY), password=parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + SNAPSHOT_KEY_NAME), password=parsed_arguments.pw) repo_tool.generate_and_write_ecdsa_keypair( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_TIMESTAMP_KEY), password=parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + TIMESTAMP_KEY_NAME), password=parsed_arguments.pw) # 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( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_ROOT_KEY) + '.pub') + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + ROOT_KEY_NAME) + '.pub') targets_public = repo_tool.import_ecdsa_publickey_from_file( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_TARGETS_KEY) + '.pub') + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + TARGETS_KEY_NAME) + '.pub') snapshot_public = repo_tool.import_ecdsa_publickey_from_file( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_SNAPSHOT_KEY) + '.pub') + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + SNAPSHOT_KEY_NAME) + '.pub') timestamp_public = repo_tool.import_ecdsa_publickey_from_file( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_TIMESTAMP_KEY) + '.pub') + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + TIMESTAMP_KEY_NAME) + '.pub') # Import the private keys. They are needed to generate the signatures # included in metadata. root_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_ROOT_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + ROOT_KEY_NAME), parsed_arguments.pw) targets_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_TARGETS_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + TARGETS_KEY_NAME), parsed_arguments.pw) snapshot_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_SNAPSHOT_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + SNAPSHOT_KEY_NAME), parsed_arguments.pw) timestamp_private = repo_tool.import_ecdsa_privatekey_from_file( - os.path.join(parsed_arguments.path, DEFAULT_KEYSTORE_DIR, - DEFAULT_TIMESTAMP_KEY), parsed_arguments.pw) + os.path.join(parsed_arguments.path, KEYSTORE_DIR, + TIMESTAMP_KEY_NAME), parsed_arguments.pw) # Add the verification keys to the top-level roles. repository.root.add_verification_key(root_public) From 6a7db797d5ac7102db9c70d5aa587ffe190211e5 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Thu, 1 Feb 2018 14:59:42 -0500 Subject: [PATCH 3/4] Remove print statement for parsed argument Signed-off-by: Vladimir Diaz --- tuf/scripts/client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tuf/scripts/client.py b/tuf/scripts/client.py index bdba95bf..867f9533 100755 --- a/tuf/scripts/client.py +++ b/tuf/scripts/client.py @@ -197,7 +197,6 @@ def parse_arguments(): parsed_arguments = parser.parse_args() - print('parsed_arguments: ' + repr(parsed_arguments)) # Set the logging level. if parsed_arguments.verbose == 5: From 59b66b45c02ee6095287459fe8dbb644e8b4d83b Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Thu, 1 Feb 2018 15:00:26 -0500 Subject: [PATCH 4/4] Document --path command-line option in CLI.md Signed-off-by: Vladimir Diaz --- docs/CLI.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index df922bb3..f4d26f2d 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -11,14 +11,18 @@ any targets nor does it delegate trust to any roles. ```Bash $ repo.py --init ``` -Note: Support for arbitrary repo paths will be added in the near future. -`$ repo.py --init --path ` -By default, `pw` is used to encrypt the top-level key files created with ---init. Instead, the user can enter a password on the command line, or be -prompted for one. +Optionally, the repository can be written to a specified location. ```Bash -$ repo.py --init --pw my_pw +$ repo.py --init --path +``` + +Note: The default top-level key files created with --init are saved to disk +encrypted, with a default password of 'pw'. Instead of using the default +password, the user can enter one on the command line or be prompted +for it via password masking. +```Bash +$ repo.py --init --pw my_password ``` ```Bash @@ -51,6 +55,12 @@ More than one target file may be specified. ```Bash $ repo.py --add ``` + +Similar to the --init case, the repository location can be specified. +```Bash +$ repo.py --add --path +``` + Note: Support for directories will be added in the near future. `$ repo.py --add [--recursive]` @@ -68,4 +78,6 @@ $ repo.py --verbose <0-5> Remove the files created via `repo.py --init`. ```Bash $ repo.py --clean +$ repo.py --clean --path ``` +(--clean by itself removes TUF files from the current working directory.)