def import_privatekey_from_file(keypath, password=None): # Note: should securesystemslib support this functionality (import any # privatekey type)? # If the caller does not provide a password argument, prompt for one. # Password confirmation is disabled here, which should ideally happen only # when creating encrypted key files. if password is None: # pragma: no cover # It is safe to specify the full path of 'filepath' in the prompt and not # worry about leaking sensitive information about the key's location. # However, care should be taken when including the full path in exceptions # and log files. password = sslib_interface.get_password( 'Enter a password for' ' the encrypted key (' + sslib_interface.TERM_RED + repr(keypath) + sslib_interface.TERM_RED + '): ', confirm=False) # Does 'password' have the correct format? sslib_formats.PASSWORD_SCHEMA.check_match(password) # Store the encrypted contents of 'filepath' prior to calling the decryption # routine. encrypted_key = None with open(keypath, 'rb') as file_object: encrypted_key = file_object.read().decode('utf-8') # Decrypt the loaded key file, calling the 'cryptography' library to generate # the derived encryption key from 'password'. Raise # 'securesystemslib.exceptions.CryptoError' if the decryption fails. try: key_object = sslib_keys.decrypt_key(encrypted_key, password) except sslib_exceptions.CryptoError: try: logger.debug( 'Decryption failed. Attempting to import a private PEM instead.' ) key_object = sslib_keys.import_rsakey_from_private_pem( encrypted_key, 'rsassa-pss-sha256', password) except sslib_exceptions.CryptoError as error: raise exceptions.Error( repr(keypath) + ' cannot be ' ' imported, possibly because an invalid key file is given or ' ' the decryption password is incorrect.') from error if key_object['keytype'] not in SUPPORTED_KEY_TYPES: raise exceptions.Error('Trying to import an unsupported key' ' type: ' + repr(key_object['keytype'] + '.' ' Supported key types: ' + repr(SUPPORTED_KEY_TYPES))) else: # Add "keyid_hash_algorithms" so that equal keys with different keyids can # be associated using supported keyid_hash_algorithms. key_object['keyid_hash_algorithms'] = sslib_settings.HASH_ALGORITHMS return key_object
def write_and_read_new_keys(trust_dir: str, functionary: str, threshold: Threshold) -> Keyring: # NOTE: By default, we expect to store keys in ~/.signy/private (like ~/.docker/private). keystore_dir = os.path.expanduser(os.path.join(trust_dir, 'private')) keypairs = [] for i in range(1, threshold.n + 1): print(f'Writing key {i}/{threshold.n} for the "{functionary}" functionary...') passphrase = get_password( prompt='Please enter a NON-EMPTY passphrase to ENCRYPT this key: ', confirm=True) keypath = write_keypair(keystore_dir, functionary, i, threshold.n, passphrase) keypair = read_keypair(functionary, keypath, i, threshold.n, passphrase) # Rename the private and public keys to match the keyid instead. # Why? So that we know how to find keys later on repository / disk. rename_keys_to_match_keyid(keystore_dir, keypair, functionary, i, threshold.n) keypairs.append(keypair) print() return Keyring(threshold, tuple(keypairs))
def create_and_set_keys(keystore_dir, repo_obj): for rolename in ('root', 'timestamp', 'snapshot', 'targets'): passphrase = os.environ.get('{}_PASSPHRASE'.format(rolename.upper())) if not passphrase: passphrase = get_password( prompt='Enter password for {}: '.format(rolename), confirm=True) private_keypath = str(get_private_keypath(keystore_dir, rolename)) public_keypath = get_public_keypath(private_keypath) generate_and_write_ecdsa_keypair(private_keypath, password=passphrase) private = import_ecdsa_privatekey_from_file(private_keypath, passphrase) public = import_ecdsa_publickey_from_file(public_keypath) role_obj = getattr(repo_obj, rolename) role_obj.load_signing_key(private) role_obj.add_verification_key(public)
def remove_targets(parsed_arguments): repository = repo_tool.load_repository( os.path.join(parsed_arguments.path, REPO_DIR)) # Remove target files from the Targets metadata (or the role specified in # --role) that match the glob patterns specified in --remove. remove_target_files_from_metadata(parsed_arguments, repository) # Examples of how the --pw command-line option is interpreted: # repo.py --init': parsed_arguments.pw = 'pw' # repo.py --init --pw my_password: parsed_arguments.pw = 'my_password' # repo.py --init --pw: The user is prompted for a password, as follows: if not parsed_arguments.pw: parsed_arguments.pw = sslib_interface.get_password( prompt='Enter a password for the top-level role keys: ', confirm=True) targets_private = import_privatekey_from_file( os.path.join(parsed_arguments.path, KEYSTORE_DIR, TARGETS_KEY_NAME), parsed_arguments.targets_pw) repository.targets.load_signing_key(targets_private) # Load the top-level keys for Snapshot and Timestamp to make a new release. # Automatically making a new release can be disabled via --no_release. if not parsed_arguments.no_release: snapshot_private = import_privatekey_from_file( os.path.join(parsed_arguments.path, KEYSTORE_DIR, SNAPSHOT_KEY_NAME), parsed_arguments.snapshot_pw) timestamp_private = import_privatekey_from_file( os.path.join(parsed_arguments.path, KEYSTORE_DIR, TIMESTAMP_KEY_NAME), parsed_arguments.timestamp_pw) repository.snapshot.load_signing_key(snapshot_private) repository.timestamp.load_signing_key(timestamp_private) consistent_snapshot = roledb.get_roleinfo( 'root', repository._repository_name)['consistent_snapshot'] repository.writeall(consistent_snapshot=consistent_snapshot) # Move staged metadata directory to "live" metadata directory. write_to_live_repo(parsed_arguments)