From 7306446118b89e3e395340e748a2717b03e0666e Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Wed, 18 Sep 2019 17:50:13 +0200 Subject: [PATCH 1/2] Refactor repository and developer tool API Import some API functions from repository_lib and securesystemslib directly into repository_tool and developer_tool, instead of providing them via wrapper. Also short-circuit some functions that used to point to securesystemslib through repository_lib. This reverts parts of 6f7ba76b9b5a90b78a16a86492b7f4cb0186d639, which introduced some of the wrappers to appease the linter. Here we just disable that specific linter check (unused-import). The advantage of importing over wrapping is: - no duplication of hardcoded defaults for keyword arguments - no duplication of docstrings - less code --> easier maintenance This should also pave the way for more serious refactoring of the repository- and developer-tools: https://github.com/theupdateframework/tuf/issues/840 Signed-off-by: Lukas Puehringer --- tuf/developer_tool.py | 59 ++++++++++++---------------- tuf/repository_tool.py | 88 +++++++++++++++--------------------------- 2 files changed, 56 insertions(+), 91 deletions(-) diff --git a/tuf/developer_tool.py b/tuf/developer_tool.py index 78197ec6b4..2bd34fa658 100755 --- a/tuf/developer_tool.py +++ b/tuf/developer_tool.py @@ -53,15 +53,35 @@ import six -# These imports provide the interface for 'developer_tool.py', since the -# imports are made there. -from securesystemslib.keys import format_keyval_to_metadata - from tuf.repository_tool import Targets from tuf.repository_lib import _check_role_keys -from tuf.repository_lib import generate_targets_metadata from tuf.repository_lib import _metadata_is_partially_loaded + +# Copy API +# pylint: disable=unused-import + +# Copy generic repository API functions to be used via `developer_tool` +from tuf.repository_lib import ( + generate_targets_metadata, + create_tuf_client_directory, + disable_console_log_messages) + +# Copy key-related API functions to be used via `developer_tool` +from tuf.repository_lib import ( + import_rsa_privatekey_from_file) + +from securesystemslib.keys import ( + format_keyval_to_metadata) + +from securesystemslib.interface import ( + generate_and_write_rsa_keypair, + generate_and_write_ed25519_keypair, + import_rsa_publickey_from_file, + import_ed25519_publickey_from_file, + import_ed25519_privatekey_from_file) + + # See 'log.py' to learn how logging is handled in TUF. logger = logging.getLogger('tuf.developer_tool') @@ -986,35 +1006,6 @@ def _strip_prefix_from_targets_metadata(targets_metadata, prefix): -# Wrapper functions that we wish to make available here from repository_lib.py. -# Users are expected to call functions provided by repository_tool.py. We opt -# for this approach, as opposed to using import statements to achieve the -# equivalent, to avoid linter warnings for unused imports. -def generate_and_write_rsa_keypair(filepath, bits, password): - return repo_lib.generate_and_write_rsa_keypair(filepath, bits, password) - -def generate_and_write_ed25519_keypair(filepath, password): - return repo_lib.generate_and_write_ed25519_keypair(filepath, password) - -def import_rsa_publickey_from_file(filepath): - return repo_lib.import_rsa_publickey_from_file(filepath) - -def import_ed25519_publickey_from_file(filepath): - return repo_lib.import_ed25519_publickey_from_file(filepath) - -def import_rsa_privatekey_from_file(filepath, password): - return repo_lib.import_rsa_privatekey_from_file(filepath, password) - -def import_ed25519_privatekey_from_file(filepath, password): - return repo_lib.import_ed25519_privatekey_from_file(filepath, password) - -def create_tuf_client_directory(repository_directory, client_directory): - return repo_lib.create_tuf_client_directory(repository_directory, client_directory) - -def disable_console_log_messages(): - return repo_lib.disable_console_log_messages() - - if __name__ == '__main__': diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index a461ab0a93..b79507310e 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -54,6 +54,37 @@ import six +# Copy API +# pylint: disable=unused-import + +# Copy generic repository API functions to be used via `repository_tool` +from tuf.repository_lib import ( + create_tuf_client_directory, + disable_console_log_messages) + + +# Copy key-related API functions to be used via `repository_tool` +from tuf.repository_lib import ( + import_rsa_privatekey_from_file, + import_ed25519_privatekey_from_file) + +from securesystemslib.interface import ( + generate_and_write_rsa_keypair, + generate_and_write_ecdsa_keypair, + generate_and_write_ed25519_keypair, + import_rsa_publickey_from_file, + import_ecdsa_publickey_from_file, + import_ed25519_publickey_from_file, + import_ecdsa_privatekey_from_file) + +from securesystemslib.keys import ( + generate_rsa_key, + generate_ecdsa_key, + generate_ed25519_key, + import_rsakey_from_pem, + import_ecdsakey_from_pem) + + # See 'log.py' to learn how logging is handled in TUF. logger = logging.getLogger('tuf.repository_tool') @@ -3153,63 +3184,6 @@ def append_signature(signature, metadata_filepath): file_object.move(metadata_filepath) -# Wrapper functions that we wish to make available here from securesystemslib. -# Users are expected to call functions provided by repository_tool.py. We opt -# for wrapper functions, instead of using the import statements to achieve the -# equivalent, to avoid linter warnings for unused imports. -def generate_and_write_ed25519_keypair(filepath=None, password=None): - return repo_lib.generate_and_write_ed25519_keypair(filepath, password) - -def generate_ed25519_key(scheme='ed25519'): - return securesystemslib.keys.generate_ed25519_key(scheme) - -def import_ed25519_publickey_from_file(filepath): - return repo_lib.import_ed25519_publickey_from_file(filepath) - -def import_ed25519_privatekey_from_file(filepath, password=None): - return repo_lib.import_ed25519_privatekey_from_file(filepath, password) - -# NOTE: securesystemslib cannot presently import an Ed25519 key from PEM. - -def generate_and_write_rsa_keypair(filepath=None, - bits=repo_lib.DEFAULT_RSA_KEY_BITS, password=None): - return repo_lib.generate_and_write_rsa_keypair(filepath, bits, password) - -def generate_rsa_key(bits=DEFAULT_RSA_KEY_BITS, scheme='rsassa-pss-sha256'): - return securesystemslib.keys.generate_rsa_key(bits, scheme) - -def import_rsa_publickey_from_file(filepath): - return repo_lib.import_rsa_publickey_from_file(filepath) - -def import_rsa_privatekey_from_file(filepath, password=None): - return repo_lib.import_rsa_privatekey_from_file(filepath, password) - -def import_rsakey_from_pem(pem, scheme='rsassa-pss-sha256'): - return securesystemslib.keys.import_rsakey_from_pem(pem, scheme) - -def generate_and_write_ecdsa_keypair(filepath=None, password=None): - return securesystemslib.interface.generate_and_write_ecdsa_keypair( - filepath, password) - -def generate_ecdsa_key(scheme='ecdsa-sha2-nistp256'): - return securesystemslib.keys.generate_ecdsa_key(scheme) - -def import_ecdsa_privatekey_from_file(filepath, password=None): - return securesystemslib.interface.import_ecdsa_privatekey_from_file( - filepath, password) - -def import_ecdsa_publickey_from_file(filepath): - return securesystemslib.interface.import_ecdsa_publickey_from_file(filepath) - -def import_ecdsakey_from_pem(pem, scheme='ecdsa-sha2-nistp256'): - return securesystemslib.keys.import_ecdsakey_from_pem(pem, scheme) - -def create_tuf_client_directory(repository_directory, client_directory): - return repo_lib.create_tuf_client_directory( - repository_directory, client_directory) - -def disable_console_log_messages(): - return repo_lib.disable_console_log_messages() From 2e6a8cd03b31881ad8a10b87eadec5978c79345b Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Wed, 18 Sep 2019 18:06:22 +0200 Subject: [PATCH 2/2] Remove some repository_lib wrappers for sslib Remove only wrappers and corresponding tests that don't add any new functionality, but blindly forward the caller to sslib, where the same function exists and is tested. Signed-off-by: Lukas Puehringer --- tests/test_repository_lib.py | 164 ++--------------------------------- tuf/repository_lib.py | 148 ------------------------------- 2 files changed, 5 insertions(+), 307 deletions(-) diff --git a/tests/test_repository_lib.py b/tests/test_repository_lib.py index 5be7d40ed6..044b4555d7 100755 --- a/tests/test_repository_lib.py +++ b/tests/test_repository_lib.py @@ -52,6 +52,7 @@ import tuf.repository_tool as repo_tool import securesystemslib +import securesystemslib.interface import six logger = logging.getLogger('tuf.test_repository_lib') @@ -100,49 +101,6 @@ def tearDown(self): - def test_generate_and_write_rsa_keypair(self): - - # Test normal case. - temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - test_keypath = os.path.join(temporary_directory, 'rsa_key') - - repo_lib.generate_and_write_rsa_keypair(test_keypath, password='pw') - self.assertTrue(os.path.exists(test_keypath)) - self.assertTrue(os.path.exists(test_keypath + '.pub')) - - # Ensure the generated key files are importable. - imported_pubkey = \ - repo_lib.import_rsa_publickey_from_file(test_keypath + '.pub') - self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_pubkey)) - - imported_privkey = \ - repo_lib.import_rsa_privatekey_from_file(test_keypath, 'pw') - self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_privkey)) - - # Custom 'bits' argument. - os.remove(test_keypath) - os.remove(test_keypath + '.pub') - repo_lib.generate_and_write_rsa_keypair(test_keypath, bits=2048, - password='pw') - self.assertTrue(os.path.exists(test_keypath)) - self.assertTrue(os.path.exists(test_keypath + '.pub')) - - - # Test improperly formatted arguments. - self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_and_write_rsa_keypair, - 3, bits=2048, password='pw') - self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_and_write_rsa_keypair, - test_keypath, bits='bad', password='pw') - self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_and_write_rsa_keypair, - test_keypath, bits=2048, password=3) - - - # Test invalid 'bits' argument. - self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_and_write_rsa_keypair, - test_keypath, bits=1024, password='pw') - - - def test_import_rsa_privatekey_from_file(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) @@ -179,125 +137,13 @@ def test_import_rsa_privatekey_from_file(self): - def test_import_rsa_publickey_from_file(self): - # Test normal case. - temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - - # Load one of the pre-generated key files from 'tuf/tests/repository_data'. - key_filepath = os.path.join('repository_data', 'keystore', - 'root_key.pub') - self.assertTrue(os.path.exists(key_filepath)) - - imported_rsa_key = repo_lib.import_rsa_publickey_from_file(key_filepath) - self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_rsa_key)) - - - # Test improperly formatted argument. - self.assertRaises(securesystemslib.exceptions.FormatError, - repo_lib.import_rsa_privatekey_from_file, 3) - - - # Test invalid argument. - # Non-existent key file. - nonexistent_keypath = os.path.join(temporary_directory, - 'nonexistent_keypath') - self.assertRaises(IOError, repo_lib.import_rsa_publickey_from_file, - nonexistent_keypath) - - # Invalid key file argument. - invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile') - with open(invalid_keyfile, 'wb') as file_object: - file_object.write(b'bad keyfile') - self.assertRaises(securesystemslib.exceptions.Error, repo_lib.import_rsa_publickey_from_file, - invalid_keyfile) - - - - def test_generate_and_write_ed25519_keypair(self): - - # Test normal case. - temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - test_keypath = os.path.join(temporary_directory, 'ed25519_key') - - repo_lib.generate_and_write_ed25519_keypair(test_keypath, password='pw') - self.assertTrue(os.path.exists(test_keypath)) - self.assertTrue(os.path.exists(test_keypath + '.pub')) - - # Ensure the generated key files are importable. - imported_pubkey = \ - repo_lib.import_ed25519_publickey_from_file(test_keypath + '.pub') - self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_pubkey)) - - imported_privkey = \ - repo_lib.import_ed25519_privatekey_from_file(test_keypath, 'pw') - self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_privkey)) - - - # Test improperly formatted arguments. - self.assertRaises(securesystemslib.exceptions.FormatError, - repo_lib.generate_and_write_ed25519_keypair, - 3, password='pw') - self.assertRaises(securesystemslib.exceptions.FormatError, repo_lib.generate_and_write_rsa_keypair, - test_keypath, password=3) - - - - def test_import_ed25519_publickey_from_file(self): - # Test normal case. - # Generate ed25519 keys that can be imported. - temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - ed25519_keypath = os.path.join(temporary_directory, 'ed25519_key') - repo_lib.generate_and_write_ed25519_keypair(ed25519_keypath, password='pw') - - imported_ed25519_key = \ - repo_lib.import_ed25519_publickey_from_file(ed25519_keypath + '.pub') - self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_ed25519_key)) - - - # Test improperly formatted argument. - self.assertRaises(securesystemslib.exceptions.FormatError, - repo_lib.import_ed25519_publickey_from_file, 3) - - - # Test invalid argument. - # Non-existent key file. - nonexistent_keypath = os.path.join(temporary_directory, - 'nonexistent_keypath') - self.assertRaises(IOError, repo_lib.import_ed25519_publickey_from_file, - nonexistent_keypath) - - # Invalid key file argument. - invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile') - with open(invalid_keyfile, 'wb') as file_object: - file_object.write(b'bad keyfile') - - self.assertRaises(securesystemslib.exceptions.Error, repo_lib.import_ed25519_publickey_from_file, - invalid_keyfile) - - # Invalid public key imported (contains unexpected keytype.) - keytype = imported_ed25519_key['keytype'] - keyval = imported_ed25519_key['keyval'] - scheme = imported_ed25519_key['scheme'] - ed25519key_metadata_format = \ - securesystemslib.keys.format_keyval_to_metadata(keytype, scheme, - keyval, private=False) - - ed25519key_metadata_format['keytype'] = 'invalid_keytype' - with open(ed25519_keypath + '.pub', 'wb') as file_object: - file_object.write(json.dumps(ed25519key_metadata_format).encode('utf-8')) - - self.assertRaises(securesystemslib.exceptions.FormatError, - repo_lib.import_ed25519_publickey_from_file, - ed25519_keypath + '.pub') - - - def test_import_ed25519_privatekey_from_file(self): # Test normal case. # Generate ed25519 keys that can be imported. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) ed25519_keypath = os.path.join(temporary_directory, 'ed25519_key') - repo_lib.generate_and_write_ed25519_keypair(ed25519_keypath, password='pw') + securesystemslib.interface.generate_and_write_ed25519_keypair( + ed25519_keypath, password='pw') imported_ed25519_key = \ repo_lib.import_ed25519_privatekey_from_file(ed25519_keypath, 'pw') @@ -709,8 +555,8 @@ def test_sign_metadata(self): # Sign with a valid, but not a threshold, key. targets_public_keypath = os.path.join(keystore_path, 'targets_key.pub') - targets_public_key = \ - repo_lib.import_ed25519_publickey_from_file(targets_public_keypath) + targets_public_key = securesystemslib.interface.\ + import_ed25519_publickey_from_file(targets_public_keypath) # sign_metadata() expects the private key 'root_metadata' to be in # 'tuf.keydb'. Remove any public keys that may be loaded before diff --git a/tuf/repository_lib.py b/tuf/repository_lib.py index 22f12de7c2..371a9dff49 100755 --- a/tuf/repository_lib.py +++ b/tuf/repository_lib.py @@ -63,13 +63,6 @@ iso8601_logger = logging.getLogger('iso8601') iso8601_logger.disabled = True -# Recommended RSA key sizes: -# http://www.emc.com/emc-plus/rsa-labs/historical/twirl-and-rsa-key-size.htm#table1 -# According to the document above, revised May 6, 2003, RSA keys of -# size 3072 provide security through 2031 and beyond. 2048-bit keys -# are the recommended minimum and are good from the present through 2030. -DEFAULT_RSA_KEY_BITS = 3072 - # The extension of TUF metadata. METADATA_EXTENSION = '.json' @@ -752,42 +745,6 @@ def _log_warning_if_expires_soon(rolename, expires_iso8601_timestamp, -def generate_and_write_rsa_keypair(filepath, bits=DEFAULT_RSA_KEY_BITS, - password=None): - """ - - Generate an RSA key file, create an encrypted PEM string (using 'password' - as the pass phrase), and store it in 'filepath'. The public key portion of - the generated RSA key is stored in <'filepath'>.pub. - - - filepath: - The public and private key files are saved to .pub, , - respectively. - - bits: - The number of bits of the generated RSA key. - - password: - The password used to encrypt 'filepath'. - - - securesystemslib.exceptions.FormatError, if the arguments are improperly - formatted. - - - Writes key files to '' and '.pub'. - - - None. - """ - - securesystemslib.interface.generate_and_write_rsa_keypair( - filepath, bits, password) - - - - def import_rsa_privatekey_from_file(filepath, password=None): """ @@ -840,111 +797,6 @@ def import_rsa_privatekey_from_file(filepath, password=None): -def import_rsa_publickey_from_file(filepath): - """ - - Import the RSA key stored in 'filepath'. The key object returned is a TUF - key, specifically 'securesystemslib.RSAKEY_SCHEMA'. If the RSA PEM - in 'filepath' contains a private key, it is discarded. - - - filepath: - .pub file, an RSA PEM file. - - - securesystemslib.exceptions.FormatError, if 'filepath' is improperly formatted. - - securesystemslib.exceptions.Error, if a valid RSA key object cannot be - generated. This may be caused by an improperly formatted PEM file. - - - 'filepath' is read and its contents extracted. - - - An RSA key object conformant to 'securesystemslib.RSAKEY_SCHEMA'. - """ - - return securesystemslib.interface.import_rsa_publickey_from_file(filepath) - - - - - -def generate_and_write_ed25519_keypair(filepath, password=None): - """ - - Generate an Ed25519 key file, create an encrypted TUF key (using 'password' - as the pass phrase), and store it in 'filepath'. The public key portion of - the generated ED25519 key is stored in <'filepath'>.pub. Which cryptography - library performs the cryptographic decryption is determined by the string - set in 'settings.ED25519_CRYPTO_LIBRARY'. - - The Ed25519 private key is encrypted with AES-256 and CTR the mode of - operation. The password is strengthened with PBKDF2-HMAC-SHA256. - - - filepath: - The public and private key files are saved to .pub and - , respectively. - - password: - The password, or passphrase, to encrypt the private portion of the - generated ed25519 key. A symmetric encryption key is derived from - 'password', so it is not directly used. - - - securesystemslib.exceptions.FormatError, if the arguments are improperly - formatted. - - securesystemslib.exceptions.CryptoError, if 'filepath' cannot be encrypted. - - securesystemslib.exceptions.UnsupportedLibraryError, if 'filepath' cannot be - encrypted due to an invalid configuration setting (i.e., invalid - 'tuf.settings.py' setting). - - - Writes key files to '' and '.pub'. - - - None. - """ - - securesystemslib.interface.generate_and_write_ed25519_keypair( - filepath, password) - - - - - -def import_ed25519_publickey_from_file(filepath): - """ - - Load the ED25519 public key object (conformant to - 'securesystemslib.KEY_SCHEMA') stored in 'filepath'. Return - 'filepath' in securesystemslib.ED25519KEY_SCHEMA format. - - If the TUF key object in 'filepath' contains a private key, it is discarded. - - - filepath: - .pub file, a TUF public key file. - - - securesystemslib.exceptions.FormatError, if 'filepath' is improperly - formatted or is an unexpected key type. - - - The contents of 'filepath' is read and saved. - - - An ED25519 key object conformant to - 'securesystemslib.ED25519KEY_SCHEMA'. - """ - - return securesystemslib.interface.import_ed25519_publickey_from_file(filepath) - - - def import_ed25519_privatekey_from_file(filepath, password=None):