mirror of
https://github.com/theupdateframework/python-tuf
synced 2026-05-24 10:08:28 +00:00
Address Issue #214.
Allow validation of specific libraries rather than checking all of them in check_crypto_libraries(). Log warning if the repository tool is imported but has not been properly installed: $ pip install tuf[tools]. Modify format string of console log messages.
This commit is contained in:
parent
c16b1fdd80
commit
c9e3b6046e
4 changed files with 102 additions and 51 deletions
|
|
@ -227,6 +227,11 @@
|
|||
# An ED25519 raw signature, which must be 64 bytes.
|
||||
ED25519SIGNATURE_SCHEMA = SCHEMA.LengthString(64)
|
||||
|
||||
# Required installation libraries expected by the repository tools and other
|
||||
# cryptography modules.
|
||||
REQUIRED_LIBRARIES_SCHEMA = SCHEMA.ListOf(SCHEMA.OneOf(
|
||||
[SCHEMA.String('general'), SCHEMA.String('ed25519'), SCHEMA.String('rsa')]))
|
||||
|
||||
# An ed25519 TUF key.
|
||||
ED25519KEY_SCHEMA = SCHEMA.Object(
|
||||
object_name = 'ED25519KEY_SCHEMA',
|
||||
|
|
|
|||
89
tuf/keys.py
89
tuf/keys.py
|
|
@ -197,9 +197,8 @@ def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS):
|
|||
tuf.formats.RSAKEYBITS_SCHEMA.check_match(bits)
|
||||
|
||||
# Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
|
||||
# 'tuf.conf', are unsupported or unavailable:
|
||||
# 'tuf.conf.RSA_CRYPTO_LIBRARY' and 'tuf.conf.ED25519_CRYPTO_LIBRARY'.
|
||||
_check_crypto_libraries()
|
||||
# 'tuf.conf', are unsupported or unavailable: 'tuf.conf.RSA_CRYPTO_LIBRARY'.
|
||||
check_crypto_libraries(['rsa'])
|
||||
|
||||
# Begin building the RSA key dictionary.
|
||||
rsakey_dict = {}
|
||||
|
|
@ -281,8 +280,8 @@ def generate_ed25519_key():
|
|||
|
||||
# Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified
|
||||
# in 'tuf.conf', are unsupported or unavailable:
|
||||
# 'tuf.conf.RSA_CRYPTO_LIBRARY' and 'tuf.conf.ED25519_CRYPTO_LIBRARY'.
|
||||
_check_crypto_libraries()
|
||||
# 'tuf.conf.ED25519_CRYPTO_LIBRARY'.
|
||||
check_crypto_libraries(['ed25519'])
|
||||
|
||||
# Begin building the ED25519 key dictionary.
|
||||
ed25519_key = {}
|
||||
|
|
@ -503,46 +502,78 @@ def _get_keyid(keytype, key_value):
|
|||
|
||||
|
||||
|
||||
def _check_crypto_libraries():
|
||||
""" Ensure all the crypto libraries specified in tuf.conf are available. """
|
||||
def check_crypto_libraries(required_libraries):
|
||||
"""
|
||||
<Purpose>
|
||||
Public function that ensures the cryptography libraries specified in
|
||||
'tuf.conf' are supported and available for each 'required_libraries'.
|
||||
|
||||
<Arguments>
|
||||
required_libraries:
|
||||
A list of library strings to validate. One, or multiple, strings from
|
||||
['rsa', 'ed25519', 'general'] can be specified.
|
||||
|
||||
<Exceptions>
|
||||
tuf.UnsupportedLibraryError, if the 'required_libraries' and the libraries
|
||||
specified in 'tuf.conf' are not supported or unavailable.
|
||||
|
||||
<Side Effects>
|
||||
Validates the libraries set in 'tuf.conf'.
|
||||
|
||||
<Returns>
|
||||
None.
|
||||
"""
|
||||
|
||||
# Does 'required_libraries' have the correct format?
|
||||
# This check will ensure 'required_libraries' has the appropriate number
|
||||
# of objects and object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.REQUIRED_LIBRARIES_SCHEMA.check_match(required_libraries)
|
||||
|
||||
# The checks below all raise 'tuf.UnsupportedLibraryError' if the RSA and
|
||||
# ED25519 crypto libraries specified in 'tuf.conf.py' are not supported or
|
||||
# unavailable. The appropriate error message is added to the exception.
|
||||
# The funcions of this module that depend on user-installed crypto libraries
|
||||
# should call this private function to ensure the called routine does not fail
|
||||
# with unpredictable exceptions in the event of a missing library.
|
||||
# The supported and available lists checked are populated when 'tuf.keys.py'
|
||||
# is imported.
|
||||
if _RSA_CRYPTO_LIBRARY not in _SUPPORTED_RSA_CRYPTO_LIBRARIES:
|
||||
# The checks below all raise 'tuf.UnsupportedLibraryError' if the general,
|
||||
# RSA, and ED25519 crypto libraries specified in 'tuf.conf.py' are not
|
||||
# supported or unavailable. The appropriate error message is added to the
|
||||
# exception. The funcions of this module that depend on user-installed
|
||||
# crypto libraries should call this private function to ensure the called
|
||||
# routine does not fail with unpredictable exceptions in the event of a
|
||||
# missing library. The supported and available lists checked are populated
|
||||
# when 'tuf.keys.py' is imported.
|
||||
|
||||
if 'rsa' in required_libraries and _RSA_CRYPTO_LIBRARY not in \
|
||||
_SUPPORTED_RSA_CRYPTO_LIBRARIES:
|
||||
message = 'The '+repr(_RSA_CRYPTO_LIBRARY)+' crypto library specified'+ \
|
||||
' in "tuf.conf.RSA_CRYPTO_LIBRARY" is not supported.\n'+ \
|
||||
'Supported crypto libraries: '+repr(_SUPPORTED_RSA_CRYPTO_LIBRARIES)+'.'
|
||||
raise tuf.UnsupportedLibraryError(message)
|
||||
|
||||
if _ED25519_CRYPTO_LIBRARY not in _SUPPORTED_ED25519_CRYPTO_LIBRARIES:
|
||||
if 'ed25519' in required_libraries and _ED25519_CRYPTO_LIBRARY not in \
|
||||
_SUPPORTED_ED25519_CRYPTO_LIBRARIES:
|
||||
message = 'The '+repr(_ED25519_CRYPTO_LIBRARY)+' crypto library specified'+\
|
||||
' in "tuf.conf.ED25519_CRYPTO_LIBRARY" is not supported.\n'+ \
|
||||
'Supported crypto libraries: '+repr(_SUPPORTED_ED25519_CRYPTO_LIBRARIES)+'.'
|
||||
raise tuf.UnsupportedLibraryError(message)
|
||||
|
||||
if _GENERAL_CRYPTO_LIBRARY not in _SUPPORTED_GENERAL_CRYPTO_LIBRARIES:
|
||||
if 'general' in required_libraries and _GENERAL_CRYPTO_LIBRARY not in \
|
||||
_SUPPORTED_GENERAL_CRYPTO_LIBRARIES:
|
||||
message = 'The '+repr(_GENERAL_CRYPTO_LIBRARY)+' crypto library specified'+\
|
||||
' in "tuf.conf.GENERAL_CRYPTO_LIBRARY" is not supported.\n'+ \
|
||||
'Supported crypto libraries: '+repr(_SUPPORTED_GENERAL_CRYPTO_LIBRARIES)+'.'
|
||||
raise tuf.UnsupportedLibraryError(message)
|
||||
|
||||
if _RSA_CRYPTO_LIBRARY not in _available_crypto_libraries:
|
||||
if 'rsa' in required_libraries and _RSA_CRYPTO_LIBRARY not in \
|
||||
_available_crypto_libraries:
|
||||
message = 'The '+repr(_RSA_CRYPTO_LIBRARY)+' crypto library specified'+ \
|
||||
' in "tuf.conf.RSA_CRYPTO_LIBRARY" could not be imported.'
|
||||
raise tuf.UnsupportedLibraryError(message)
|
||||
|
||||
if _ED25519_CRYPTO_LIBRARY not in _available_crypto_libraries:
|
||||
if 'ed25519' in required_libraries and _ED25519_CRYPTO_LIBRARY not in \
|
||||
_available_crypto_libraries:
|
||||
message = 'The '+repr(_ED25519_CRYPTO_LIBRARY)+' crypto library specified'+\
|
||||
' in "tuf.conf.ED25519_CRYPTO_LIBRARY" could not be imported.'
|
||||
raise tuf.UnsupportedLibraryError(message)
|
||||
|
||||
if _GENERAL_CRYPTO_LIBRARY not in _available_crypto_libraries:
|
||||
if 'general' in required_libraries and _GENERAL_CRYPTO_LIBRARY not in \
|
||||
_available_crypto_libraries:
|
||||
message = 'The '+repr(_GENERAL_CRYPTO_LIBRARY)+' crypto library specified'+\
|
||||
' in "tuf.conf.GENERAL_CRYPTO_LIBRARY" could not be imported.'
|
||||
raise tuf.UnsupportedLibraryError(message)
|
||||
|
|
@ -628,8 +659,8 @@ def create_signature(key_dict, data):
|
|||
|
||||
# Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified
|
||||
# in 'tuf.conf', are unsupported or unavailable:
|
||||
# 'tuf.conf.RSA_CRYPTO_LIBRARY' and 'tuf.conf.ED25519_CRYPTO_LIBRARY'.
|
||||
_check_crypto_libraries()
|
||||
# 'tuf.conf.RSA_CRYPTO_LIBRARY' or 'tuf.conf.ED25519_CRYPTO_LIBRARY'.
|
||||
check_crypto_libraries([key_dict['keytype']])
|
||||
|
||||
# Signing the 'data' object requires a private key.
|
||||
# The 'RSASSA-PSS' (i.e., PyCrypto module) and 'ed25519' (i.e., PyNaCl and the
|
||||
|
|
@ -885,8 +916,8 @@ def import_rsakey_from_encrypted_pem(encrypted_pem, password):
|
|||
|
||||
# Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
|
||||
# 'tuf.conf', are unsupported or unavailable:
|
||||
# 'tuf.conf.RSA_CRYPTO_LIBRARY' and 'tuf.conf.ED25519_CRYPTO_LIBRARY'.
|
||||
_check_crypto_libraries()
|
||||
# 'tuf.conf.RSA_CRYPTO_LIBRARY' and 'tuf.conf.GENERAL_CRYPTO_LIBRARY'.
|
||||
check_crypto_libraries(['rsa', 'general'])
|
||||
|
||||
# Begin building the RSA key dictionary.
|
||||
rsakey_dict = {}
|
||||
|
|
@ -1068,7 +1099,7 @@ def encrypt_key(key_object, password):
|
|||
# Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
|
||||
# 'tuf.conf', are unsupported or unavailable:
|
||||
# 'tuf.conf.GENERAL_CRYPTO_LIBRARY'.
|
||||
_check_crypto_libraries()
|
||||
check_crypto_libraries(['general'])
|
||||
|
||||
# Encrypted string of 'key_object'. The encrypted string may be safely saved
|
||||
# to a file and stored offline.
|
||||
|
|
@ -1164,7 +1195,7 @@ def decrypt_key(encrypted_key, passphrase):
|
|||
# Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
|
||||
# 'tuf.conf', are unsupported or unavailable:
|
||||
# 'tuf.conf.GENERAL_CRYPTO_LIBRARY'.
|
||||
_check_crypto_libraries()
|
||||
check_crypto_libraries(['general'])
|
||||
|
||||
# Store and return the decrypted key object.
|
||||
key_object = None
|
||||
|
|
@ -1247,8 +1278,8 @@ def create_rsa_encrypted_pem(private_key, passphrase):
|
|||
|
||||
# Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
|
||||
# 'tuf.conf', are unsupported or unavailable:
|
||||
# 'tuf.conf.RSA_CRYPTO_LIBRARY'.
|
||||
_check_crypto_libraries()
|
||||
# 'tuf.conf.RSA_CRYPTO_LIBRARY' and 'tuf.conf.GENERAL_CRYPTO_LIBRARY'.
|
||||
check_crypto_libraries(['rsa', 'general'])
|
||||
|
||||
encrypted_pem = None
|
||||
|
||||
|
|
|
|||
|
|
@ -258,6 +258,7 @@ def set_console_log_level(log_level=_DEFAULT_CONSOLE_LOG_LEVEL):
|
|||
|
||||
if console_handler is not None:
|
||||
console_handler.setLevel(log_level)
|
||||
|
||||
else:
|
||||
message = 'The console handler has not been set with add_console_handler().'
|
||||
raise tuf.Error(message)
|
||||
|
|
@ -298,14 +299,18 @@ def add_console_handler(log_level=_DEFAULT_CONSOLE_LOG_LEVEL):
|
|||
# Set the console handler for the logger. The built-in console handler will
|
||||
# log messages to 'sys.stderr' and capture 'log_level' messages.
|
||||
console_handler = logging.StreamHandler()
|
||||
|
||||
# Get our filter for the console handler.
|
||||
console_filter = ConsoleFilter()
|
||||
console_format_string = '%(message)s'
|
||||
formatter = logging.Formatter(console_format_string)
|
||||
|
||||
console_handler.setLevel(log_level)
|
||||
console_handler.setFormatter(formatter)
|
||||
console_handler.addFilter(console_filter)
|
||||
logger.addHandler(console_handler)
|
||||
logger.debug('Added a console handler.')
|
||||
|
||||
else:
|
||||
logger.warn('We already have a console handler.')
|
||||
|
||||
|
|
@ -339,5 +344,6 @@ def remove_console_handler():
|
|||
logger.removeHandler(console_handler)
|
||||
console_handler = None
|
||||
logger.debug('Removed a console handler.')
|
||||
|
||||
else:
|
||||
logger.warn('We do not have a console handler.')
|
||||
|
|
|
|||
|
|
@ -117,6 +117,15 @@
|
|||
TIMESTAMP_EXPIRES_WARN_SECONDS = 86400
|
||||
|
||||
|
||||
try:
|
||||
tuf.keys.check_crypto_libraries(['rsa', 'ed25519', 'general'])
|
||||
|
||||
except tuf.UnsupportedLibraryError as e:
|
||||
message = 'Warning: The repository and developer tools require additional' + \
|
||||
' libraries and can be installed as follows:\n $ pip install tuf[tools]'
|
||||
logger.warn(message)
|
||||
|
||||
|
||||
class Repository(object):
|
||||
"""
|
||||
<Purpose>
|
||||
|
|
@ -430,23 +439,23 @@ def status(self):
|
|||
except tuf.UnsignedMetadataError, e:
|
||||
insufficient_signatures.append(delegated_role)
|
||||
|
||||
# Print the verification results of the delegated roles and return
|
||||
# Log the verification results of the delegated roles and return
|
||||
# immediately after each invalid case.
|
||||
if len(insufficient_keys):
|
||||
message = \
|
||||
'Delegated roles with insufficient keys:\n'+repr(insufficient_keys)
|
||||
print(message)
|
||||
logger.info(message)
|
||||
return
|
||||
|
||||
if len(insufficient_signatures):
|
||||
message = \
|
||||
'Delegated roles with insufficient signatures:\n'+\
|
||||
repr(insufficient_signatures)
|
||||
print(message)
|
||||
logger.info(message)
|
||||
return
|
||||
|
||||
# Verify the top-level roles and print the results.
|
||||
_print_status_of_top_level_roles(targets_directory, metadata_directory)
|
||||
# Verify the top-level roles and log the results.
|
||||
_log_status_of_top_level_roles(targets_directory, metadata_directory)
|
||||
|
||||
finally:
|
||||
shutil.rmtree(temp_repository_directory, ignore_errors=True)
|
||||
|
|
@ -2579,13 +2588,13 @@ def _generate_and_write_metadata(rolename, metadata_filename, write_partial,
|
|||
|
||||
|
||||
|
||||
def _print_status_of_top_level_roles(targets_directory, metadata_directory):
|
||||
def _log_status_of_top_level_roles(targets_directory, metadata_directory):
|
||||
"""
|
||||
Non-public function that prints whether any of the top-level roles contain an
|
||||
Non-public function that logs whether any of the top-level roles contain an
|
||||
invalid number of public and private keys, or an insufficient threshold of
|
||||
signatures. Considering that the top-level metadata have to be verified in
|
||||
the expected root -> targets -> snapshot -> timestamp order, this function
|
||||
prints the error message and returns as soon as a required metadata file is
|
||||
logs the error message and returns as soon as a required metadata file is
|
||||
found to be invalid. It is assumed here that the delegated roles have been
|
||||
written and verified. Example output:
|
||||
|
||||
|
|
@ -2614,7 +2623,7 @@ def _print_status_of_top_level_roles(targets_directory, metadata_directory):
|
|||
_check_role_keys(rolename)
|
||||
|
||||
except tuf.InsufficientKeysError, e:
|
||||
print(str(e))
|
||||
logger.info(str(e))
|
||||
return
|
||||
|
||||
# Do the top-level roles contain a valid threshold of signatures? Top-level
|
||||
|
|
@ -2624,13 +2633,13 @@ def _print_status_of_top_level_roles(targets_directory, metadata_directory):
|
|||
signable, root_filename = \
|
||||
_generate_and_write_metadata('root', root_filename, False,
|
||||
targets_directory, metadata_directory)
|
||||
_print_status('root', signable)
|
||||
_log_status('root', signable)
|
||||
|
||||
# 'tuf.UnsignedMetadataError' raised if metadata contains an invalid threshold
|
||||
# of signatures. Print the valid/threshold message, where valid < threshold.
|
||||
# of signatures. log the valid/threshold message, where valid < threshold.
|
||||
except tuf.UnsignedMetadataError, e:
|
||||
signable = e[1]
|
||||
_print_status('root', signable)
|
||||
_log_status('root', signable)
|
||||
return
|
||||
|
||||
# Verify the metadata of the Targets role.
|
||||
|
|
@ -2638,11 +2647,11 @@ def _print_status_of_top_level_roles(targets_directory, metadata_directory):
|
|||
signable, targets_filename = \
|
||||
_generate_and_write_metadata('targets', targets_filename, False,
|
||||
targets_directory, metadata_directory)
|
||||
_print_status('targets', signable)
|
||||
_log_status('targets', signable)
|
||||
|
||||
except tuf.UnsignedMetadataError, e:
|
||||
signable = e[1]
|
||||
_print_status('targets', signable)
|
||||
_log_status('targets', signable)
|
||||
return
|
||||
|
||||
# Verify the metadata of the snapshot role.
|
||||
|
|
@ -2652,11 +2661,11 @@ def _print_status_of_top_level_roles(targets_directory, metadata_directory):
|
|||
_generate_and_write_metadata('snapshot', snapshot_filename, False,
|
||||
targets_directory, metadata_directory,
|
||||
False, filenames)
|
||||
_print_status('snapshot', signable)
|
||||
_log_status('snapshot', signable)
|
||||
|
||||
except tuf.UnsignedMetadataError, e:
|
||||
signable = e[1]
|
||||
_print_status('snapshot', signable)
|
||||
_log_status('snapshot', signable)
|
||||
return
|
||||
|
||||
# Verify the metadata of the Timestamp role.
|
||||
|
|
@ -2666,19 +2675,19 @@ def _print_status_of_top_level_roles(targets_directory, metadata_directory):
|
|||
_generate_and_write_metadata('timestamp', snapshot_filename, False,
|
||||
targets_directory, metadata_directory,
|
||||
False, filenames)
|
||||
_print_status('timestamp', signable)
|
||||
_log_status('timestamp', signable)
|
||||
|
||||
except tuf.UnsignedMetadataError, e:
|
||||
signable = e[1]
|
||||
_print_status('timestamp', signable)
|
||||
_log_status('timestamp', signable)
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
def _print_status(rolename, signable):
|
||||
def _log_status(rolename, signable):
|
||||
"""
|
||||
Non-public function prints the number of (good/threshold) signatures of
|
||||
Non-public function logs the number of (good/threshold) signatures of
|
||||
'rolename'.
|
||||
"""
|
||||
|
||||
|
|
@ -2686,7 +2695,7 @@ def _print_status(rolename, signable):
|
|||
|
||||
message = repr(rolename)+' role contains '+ repr(len(status['good_sigs']))+\
|
||||
' / '+repr(status['threshold'])+' signatures.'
|
||||
print(message)
|
||||
logger.info(message)
|
||||
|
||||
|
||||
|
||||
|
|
@ -2694,7 +2703,7 @@ def _print_status(rolename, signable):
|
|||
|
||||
def _prompt(message, result_type=str):
|
||||
"""
|
||||
Non-public function that prompts the user for input by printing 'message',
|
||||
Non-public function that prompts the user for input by loging 'message',
|
||||
converting the input to 'result_type', and returning the value to the
|
||||
caller.
|
||||
"""
|
||||
|
|
|
|||
Loading…
Reference in a new issue