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:
vladdd 2014-05-03 18:03:25 -04:00
parent c16b1fdd80
commit c9e3b6046e
4 changed files with 102 additions and 51 deletions

View file

@ -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',

View file

@ -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

View file

@ -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.')

View file

@ -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.
"""