Beispiel #1
0
def gen_key(parsed_arguments):

    if parsed_arguments.filename:
        parsed_arguments.filename = os.path.join(parsed_arguments.path,
                                                 KEYSTORE_DIR,
                                                 parsed_arguments.filename)

    keypath = None

    keygen_kwargs = {
        "password": parsed_arguments.pw,
        "filepath": parsed_arguments.filename,
        "prompt":
        (not parsed_arguments.pw)  # prompt if no default or passed pw
    }

    if parsed_arguments.key not in SUPPORTED_CLI_KEYTYPES:
        exceptions.Error('Invalid key type: ' + repr(parsed_arguments.key) +
                         '.  Supported'
                         ' key types: ' + repr(SUPPORTED_CLI_KEYTYPES))

    elif parsed_arguments.key == ECDSA_KEYTYPE:
        keypath = sslib_interface._generate_and_write_ecdsa_keypair(
            **keygen_kwargs)

    elif parsed_arguments.key == ED25519_KEYTYPE:
        keypath = sslib_interface._generate_and_write_ed25519_keypair(
            **keygen_kwargs)

    # RSA key..
    else:
        keypath = sslib_interface._generate_and_write_rsa_keypair(
            **keygen_kwargs)

    # If a filename is not given, the generated keypair is saved to the current
    # working directory.  By default, the keypair is written to <KEYID>.pub
    # and <KEYID> (private key).
    if not parsed_arguments.filename:
        privkey_repo_path = os.path.join(parsed_arguments.path, KEYSTORE_DIR,
                                         os.path.basename(keypath))
        pubkey_repo_path = os.path.join(parsed_arguments.path, KEYSTORE_DIR,
                                        os.path.basename(keypath + '.pub'))

        sslib_util.ensure_parent_dir(privkey_repo_path)
        sslib_util.ensure_parent_dir(pubkey_repo_path)

        # Move them from the CWD to the repo's keystore.
        shutil.move(keypath, privkey_repo_path)
        shutil.move(keypath + '.pub', pubkey_repo_path)
Beispiel #2
0
def add_target_to_repo(parsed_arguments,
                       target_path,
                       repo_targets_path,
                       repository,
                       custom=None):
    """
  (1) Copy 'target_path' to 'repo_targets_path'.
  (2) Add 'target_path' to Targets metadata of 'repository'.
  """

    if custom is None:
        custom = {}

    if not os.path.exists(target_path):
        logger.debug(repr(target_path) + ' does not exist.  Skipping.')

    else:
        sslib_util.ensure_parent_dir(
            os.path.join(repo_targets_path, target_path))
        shutil.copy(target_path, os.path.join(repo_targets_path, target_path))

        roleinfo = roledb.get_roleinfo(
            parsed_arguments.role, repository_name=repository._repository_name)

        # It is assumed we have a delegated role, and that the caller has made
        # sure to reject top-level roles specified with --role.
        if target_path not in roleinfo['paths']:
            logger.debug('Adding new target: ' + repr(target_path))
            roleinfo['paths'].update({target_path: custom})

        else:
            logger.debug('Replacing target: ' + repr(target_path))
            roleinfo['paths'].update({target_path: custom})

        roledb.update_roleinfo(parsed_arguments.role,
                               roleinfo,
                               mark_role_as_dirty=True,
                               repository_name=repository._repository_name)
Beispiel #3
0
    def write(self, write_partial=False):
        """
    <Purpose>
      Write all the JSON Metadata objects to their corresponding files.
      write() raises an exception if any of the role metadata to be written to
      disk is invalid, such as an insufficient threshold of signatures, missing
      private keys, etc.

    <Arguments>
      write_partial:
        A boolean indicating whether partial metadata should be written to
        disk.  Partial metadata may be written to allow multiple maintainters
        to independently sign and update role metadata.  write() raises an
        exception if a metadata role cannot be written due to not having enough
        signatures.

    <Exceptions>
      securesystemslib.exceptions.Error, if any of the project roles do not
      have a minimum threshold of signatures.

    <Side Effects>
      Creates metadata files in the project's metadata directory.

    <Returns>
      None.
    """

        # Does 'write_partial' have the correct format?
        # Ensure the arguments have the appropriate number of objects and object
        # types, and that all dict keys are properly named.
        # Raise 'securesystemslib.exceptions.FormatError' if any are improperly formatted.
        sslib_formats.BOOLEAN_SCHEMA.check_match(write_partial)

        # At this point the keydb and roledb stores must be fully
        # populated, otherwise write() throwns a 'tuf.Repository' exception if
        # any of the project roles are missing signatures, keys, etc.

        # Write the metadata files of all the delegated roles of the project.
        delegated_rolenames = roledb.get_delegated_rolenames(
            self.project_name, self.repository_name)

        for delegated_rolename in delegated_rolenames:
            delegated_filename = os.path.join(
                self.metadata_directory,
                delegated_rolename + METADATA_EXTENSION)

            # Ensure the parent directories of 'metadata_filepath' exist, otherwise an
            # IO exception is raised if 'metadata_filepath' is written to a
            # sub-directory.
            sslib_util.ensure_parent_dir(delegated_filename)

            _generate_and_write_metadata(delegated_rolename,
                                         delegated_filename,
                                         write_partial,
                                         self.targets_directory,
                                         prefix=self.prefix,
                                         repository_name=self.repository_name)

        # Generate the 'project_name' metadata file.
        targets_filename = self.project_name + METADATA_EXTENSION
        targets_filename = os.path.join(self.metadata_directory,
                                        targets_filename)
        junk, targets_filename = _generate_and_write_metadata(
            self.project_name,
            targets_filename,
            write_partial,
            self.targets_directory,
            prefix=self.prefix,
            repository_name=self.repository_name)

        # Save configuration information that is not stored in the project's
        # metadata
        _save_project_configuration(self.metadata_directory,
                                    self.targets_directory, self.keys,
                                    self.prefix, self.threshold,
                                    self.layout_type, self.project_name)
Beispiel #4
0
def _generate_and_write_ecdsa_keypair(filepath=None,
                                      password=None,
                                      prompt=False):
    """Generates ecdsa key pair and writes custom JSON-formatted keys to disk.

  If a password is passed or entered on the prompt, the private key is
  encrypted using AES-256 in CTR mode, with the password strengthened in
  PBKDF2-HMAC-SHA256.

  NOTE: The custom key format includes 'ecdsa-sha2-nistp256' as signing scheme.

  Arguments:
    filepath (optional): The path to write the private key to. If not passed,
        the key is written to CWD using the keyid as filename. The public key
        is written to the same path as the private key using the suffix '.pub'.
    password (optional): An encryption password.
    prompt (optional): A boolean indicating if the user should be prompted
        for an encryption password. If the user enters an empty password, the
        key is not encrypted.

  Raises:
    UnsupportedLibraryError: pyca/cryptography is not available.
    FormatError: Arguments are malformed.
    ValueError: An empty string is passed as 'password', or both a 'password'
        is passed and 'prompt' is true.
    StorageError: Key files cannot be written.

  Side Effects:
    Prompts user for a password if 'prompt' is True.
    Writes key files to disk.

  Returns:
    The private key filepath.

  """
    ecdsa_key = keys.generate_ecdsa_key()

    # Use passed 'filepath' or keyid as file name
    if not filepath:
        filepath = os.path.join(os.getcwd(), ecdsa_key['keyid'])

    formats.PATH_SCHEMA.check_match(filepath)

    password = _get_key_file_encryption_password(password, prompt, filepath)

    # Create intermediate directories as required
    util.ensure_parent_dir(filepath)

    # Use custom JSON format for ecdsa keys on-disk
    keytype = ecdsa_key['keytype']
    keyval = ecdsa_key['keyval']
    scheme = ecdsa_key['scheme']
    ecdsakey_metadata_format = keys.format_keyval_to_metadata(keytype,
                                                              scheme,
                                                              keyval,
                                                              private=False)

    # Write public key to <filepath>.pub
    file_object = tempfile.TemporaryFile()
    file_object.write(json.dumps(ecdsakey_metadata_format).encode('utf-8'))
    util.persist_temp_file(file_object, filepath + '.pub')

    # Encrypt private key if we have a password, store as JSON string otherwise
    if password is not None:
        ecdsa_key = keys.encrypt_key(ecdsa_key, password)
    else:
        ecdsa_key = json.dumps(ecdsa_key)

    # Write private key to <filepath>
    file_object = tempfile.TemporaryFile()
    file_object.write(ecdsa_key.encode('utf-8'))
    util.persist_temp_file(file_object, filepath)

    return filepath
Beispiel #5
0
def _generate_and_write_rsa_keypair(filepath=None,
                                    bits=DEFAULT_RSA_KEY_BITS,
                                    password=None,
                                    prompt=False):
    """Generates RSA key pair and writes PEM-encoded keys to disk.

  If a password is passed or entered on the prompt, the private key is
  encrypted. According to the documentation of the used pyca/cryptography
  library, encryption is performed "using the best available encryption for a
  given key's backend", which "is a curated encryption choice and the algorithm
  may change over time."  The private key is written in PKCS#1 and the public
  key in X.509 SubjectPublicKeyInfo format.

  NOTE: A signing scheme can be assigned on key import (see import functions).

  Arguments:
    filepath (optional): The path to write the private key to. If not passed,
        the key is written to CWD using the keyid as filename. The public key
        is written to the same path as the private key using the suffix '.pub'.
    bits (optional): The number of bits of the generated RSA key.
    password (optional): An encryption password.
    prompt (optional): A boolean indicating if the user should be prompted
        for an encryption password. If the user enters an empty password, the
        key is not encrypted.

  Raises:
    UnsupportedLibraryError: pyca/cryptography is not available.
    FormatError: Arguments are malformed.
    ValueError: An empty string is passed as 'password', or both a 'password'
        is passed and 'prompt' is true.
    StorageError: Key files cannot be written.

  Side Effects:
    Prompts user for a password if 'prompt' is True.
    Writes key files to disk.

  Returns:
    The private key filepath.

  """
    formats.RSAKEYBITS_SCHEMA.check_match(bits)

    # Generate private RSA key and extract public and private both in PEM
    rsa_key = keys.generate_rsa_key(bits)
    public = rsa_key['keyval']['public']
    private = rsa_key['keyval']['private']

    # Use passed 'filepath' or keyid as file name
    if not filepath:
        filepath = os.path.join(os.getcwd(), rsa_key['keyid'])

    formats.PATH_SCHEMA.check_match(filepath)

    password = _get_key_file_encryption_password(password, prompt, filepath)

    # Encrypt the private key if a 'password' was passed or entered on the prompt
    if password is not None:
        private = keys.create_rsa_encrypted_pem(private, password)

    # Create intermediate directories as required
    util.ensure_parent_dir(filepath)

    # Write PEM-encoded public key to <filepath>.pub
    file_object = tempfile.TemporaryFile()
    file_object.write(public.encode('utf-8'))
    util.persist_temp_file(file_object, filepath + '.pub')

    # Write PEM-encoded private key to <filepath>
    file_object = tempfile.TemporaryFile()
    file_object.write(private.encode('utf-8'))
    util.persist_temp_file(file_object, filepath)

    return filepath