Exemple #1
0
    def setUpClass(self):
        """Create and change into temporary directory, generate key pair and dummy
    material, read key pair. """
        self.set_up_test_dir()

        self.step_name = "test_step"
        self.key_path = "test_key"
        generate_and_write_rsa_keypair(self.key_path)
        self.key = prompt_import_rsa_key_from_file(self.key_path)
        self.key_pub = prompt_import_rsa_key_from_file(self.key_path + ".pub")

        self.test_artifact = "test_artifact"
        open(self.test_artifact, "w").close()
Exemple #2
0
    def test_prompt_create_and_import_encrypted_rsa(self):
        """Create and import password encrypted RSA using prompt input. """
        key = "key6"
        password = "******"
        bits = 3072
        with patch("getpass.getpass", return_value=password):
            prompt_generate_and_write_rsa_keypair(key, bits)
            rsa_key = prompt_import_rsa_key_from_file(key)
            securesystemslib.formats.KEY_SCHEMA.check_match(rsa_key)
            self.assertTrue(rsa_key["keyval"].get("private"))

        with patch("getpass.getpass",
                   return_value="wrong-password"), self.assertRaises(
                       securesystemslib.exceptions.CryptoError):
            prompt_import_rsa_key_from_file(key)
Exemple #3
0
    def setUpClass(self):
        """Create and change into temporary directory,
    generate key pair, dummy artifact and base arguments. """

        self.working_dir = os.getcwd()

        self.test_dir = tempfile.mkdtemp()

        # Copy gpg keyring
        self.default_gpg_keyid = "8465a1e2e0fb2b40adb2478e18fb3f537e0c8a17"
        self.default_gpg_subkeyid = "c5a0abe6ec19d0d65f85e2c39be9df5131d924e9"
        self.non_default_gpg_keyid = "8288ef560ed3795f9df2c0db56193089b285da58"
        gpg_keyring_path = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "gpg_keyrings", "rsa")
        self.gnupg_home = os.path.join(self.test_dir, "rsa")
        shutil.copytree(gpg_keyring_path, self.gnupg_home)

        os.chdir(self.test_dir)

        self.key_path = "test_key"
        generate_and_write_rsa_keypair(self.key_path)
        self.key = prompt_import_rsa_key_from_file(self.key_path)

        self.test_step = "test_step"
        self.test_link = FILENAME_FORMAT.format(step_name=self.test_step,
                                                keyid=self.key["keyid"])
        self.test_artifact = "test_artifact"
        open(self.test_artifact, "w").close()
Exemple #4
0
    def setUpClass(self):
        """Create and change into temporary directory, generate two key pairs
    and dummy product. """
        self.set_up_test_dir()

        self.key_path = "test-key"
        self.key_path2 = "test-key2"
        generate_and_write_rsa_keypair(self.key_path)
        generate_and_write_rsa_keypair(self.key_path2)
        self.key = prompt_import_rsa_key_from_file(self.key_path)
        self.key2 = prompt_import_rsa_key_from_file(self.key_path2)

        self.step_name = "test-step"
        self.link_name = "{}.{:.8}.link".format(self.step_name,
                                                self.key["keyid"])
        self.link_name_unfinished = UNFINISHED_FILENAME_FORMAT.format(
            step_name=self.step_name, keyid=self.key["keyid"])

        self.test_product = "test_product"
        open(self.test_product, "w").close()
Exemple #5
0
  def setUpClass(self):
    """Create and change into temporary directory,
    generate key pair, dummy artifact and base arguments. """
    self.test_dir = tempfile.mkdtemp()
    os.chdir(self.test_dir)

    self.key_path = "test_key"
    generate_and_write_rsa_keypair(self.key_path)
    self.key = prompt_import_rsa_key_from_file(self.key_path)

    self.test_artifact = "test_artifact"
    open(self.test_artifact, "w").close()
Exemple #6
0
    def setUpClass(self):
        """Create and change into temporary directory, generate key pair and dummy
    material, read key pair. """
        self.set_up_test_dir()

        self.key_path = "test_key"
        generate_and_write_rsa_keypair(self.key_path)
        self.key = prompt_import_rsa_key_from_file(self.key_path)

        self.step_name = "test_step"
        self.link_name_unfinished = UNFINISHED_FILENAME_FORMAT.format(
            step_name=self.step_name, keyid=self.key["keyid"])

        self.test_material = "test_material"
        open(self.test_material, "w").close()
Exemple #7
0
def _sign_and_dump_metadata(metadata, args):
    """
  <Purpose>
    Internal method to sign link or layout metadata and dump it to disk.

  <Arguments>
    metadata:
            Metablock object (contains Link or Layout object)
    args:
            see argparser

  <Exceptions>
    SystemExit(0) if signing is successful
    SystemExit(2) if any exception occurs

  """

    try:
        if not args.append:
            metadata.signatures = []

        for key_path in args.key:
            key = util.prompt_import_rsa_key_from_file(key_path)
            metadata.sign(key)

            # Only relevant when signing Link metadata, where there is only one key
            keyid = key["keyid"]

        if args.output:
            out_path = args.output

        elif metadata._type == "link":
            out_path = FILENAME_FORMAT.format(step_name=metadata.signed.name,
                                              keyid=keyid)

        elif metadata._type == "layout":
            out_path = args.file

        log.info("Dumping {0} to '{1}'...".format(metadata._type, out_path))

        metadata.dump(out_path)
        sys.exit(0)

    except Exception as e:
        log.error("The following error occurred while signing: "
                  "{}".format(e))
        sys.exit(2)
Exemple #8
0
    def setUpClass(self):
        """Create and change into temporary directory,
    generate key pair, dummy artifact and base arguments. """

        self.working_dir = os.getcwd()

        self.test_dir = tempfile.mkdtemp()
        os.chdir(self.test_dir)

        self.key_path = "test_key"
        generate_and_write_rsa_keypair(self.key_path)
        self.key = prompt_import_rsa_key_from_file(self.key_path)

        self.test_step = "test_step"
        self.test_link = FILENAME_FORMAT.format(step_name=self.test_step,
                                                keyid=self.key["keyid"])
        self.test_artifact = "test_artifact"
        open(self.test_artifact, "w").close()
Exemple #9
0
def main():
    """Parse arguments, load key from disk (prompts for password if key is
  encrypted) and call in_toto_run. """

    parser = argparse.ArgumentParser(
        description="Executes link command and records metadata")
    # Whitespace padding to align with program name
    lpad = (len(parser.prog) + 1) * " "

    parser.usage = ("\n"
                    "%(prog)s  --step-name <unique step name>\n{0}"
                    " --key <functionary private key path>\n{0}"
                    "[--materials <filepath>[ <filepath> ...]]\n{0}"
                    "[--products <filepath>[ <filepath> ...]]\n{0}"
                    "[--record-streams]\n{0}"
                    "[--no-command]\n{0}"
                    "[--verbose] -- <cmd> [args]\n\n".format(lpad))

    in_toto_args = parser.add_argument_group("in-toto options")

    # FIXME: Do we limit the allowed characters for the name?
    in_toto_args.add_argument("-n",
                              "--step-name",
                              type=str,
                              required=True,
                              help="Unique name for link metadata")

    in_toto_args.add_argument(
        "-m",
        "--materials",
        type=str,
        required=False,
        nargs='+',
        help="Files to record before link command execution")

    in_toto_args.add_argument(
        "-p",
        "--products",
        type=str,
        required=False,
        nargs='+',
        help="Files to record after link command execution")

    in_toto_args.add_argument(
        "-k",
        "--key",
        type=str,
        required=True,
        help="Path to private key to sign link metadata (PEM)")

    in_toto_args.add_argument(
        "-b",
        "--record-streams",
        help="If set redirects stdout/stderr and stores to link metadata",
        dest="record_streams",
        default=False,
        action="store_true")

    in_toto_args.add_argument("-x",
                              "--no-command",
                              help="Set if step does not have a command",
                              dest="no_command",
                              default=False,
                              action="store_true")

    in_toto_args.add_argument("-v",
                              "--verbose",
                              dest="verbose",
                              help="Verbose execution.",
                              default=False,
                              action="store_true")

    # FIXME: This is not yet ideal.
    # What should we do with tokens like > or ; ?
    in_toto_args.add_argument(
        "link_cmd",
        nargs="*",
        help="Link command to be executed with options and arguments")

    args = parser.parse_args()

    # Turn on all the `log.info()` in the library
    if args.verbose:
        log.logging.getLogger().setLevel(log.logging.INFO)

    # Override defaults in settings.py with environment variables and RCfiles
    in_toto.user_settings.set_settings()

    # We load the key here because it might prompt the user for a password in
    # case the key is encrypted. Something that should not happen in the library.
    try:
        key = util.prompt_import_rsa_key_from_file(args.key)
    except Exception as e:
        log.error("in load key - {}".format(args.key))
        sys.exit(1)

    if args.no_command:
        in_toto_run(args.step_name, args.materials, args.products, [], key,
                    args.record_streams)
    else:
        if not args.link_cmd:
            parser.print_usage()
            parser.exit("For no command use --no-command option")
        in_toto_run(args.step_name, args.materials, args.products,
                    args.link_cmd, key, args.record_streams)
Exemple #10
0
def main():
    """Parse arguments, load key from disk (prompts for password if key is
  encrypted) and call in_toto_run. """

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="""
Executes the passed command and records paths and hashes of 'materials' (i.e.
files before command execution) and 'products' (i.e. files after command
execution) and stores them together with other information (executed command,
return value, stdout, stderr, ...) to a link metadata file, which is signed
with the passed key.  Returns nonzero value on failure and zero otherwise.""")

    parser.usage = ("%(prog)s <named arguments> [optional arguments]"
                    " -- <command> [args]")

    parser.epilog = """
examples:
  Tag a git repo, storing files in CWD as products, signing the resulting link
  file with the private key loaded from 'key_file'.

      {prog} -n tag -p . -k key_file -- git tag v1.0


  Create tarball, storing files in 'project' directory as materials and the
  tarball as product, signing the link file with GPG key '...7E0C8A17'.

      {prog} -n package -m project -p project.tar.gz \\
             -g 8465A1E2E0FB2B40ADB2478E18FB3F537E0C8A17 \\
             -- tar czf project.tar.gz project

""".format(prog=parser.prog)

    named_args = parser.add_argument_group("required named arguments")

    # FIXME: Do we limit the allowed characters for the name?
    named_args.add_argument(
        "-n",
        "--step-name",
        type=str,
        required=True,
        metavar="<name>",
        help=("Name used to associate the resulting link metadata with the"
              " corresponding step defined in an in-toto layout."))

    parser.add_argument(
        "-m",
        "--materials",
        type=str,
        required=False,
        nargs='+',
        metavar="<path>",
        help=
        ("Paths to files or directories, whose paths and hashes are stored in the"
         " resulting link metadata before the command is executed. Symlinks are"
         " followed."))

    parser.add_argument(
        "-p",
        "--products",
        type=str,
        required=False,
        nargs='+',
        metavar="<path>",
        help=
        ("Paths to files or directories, whose paths and hashes are stored in the"
         " resulting link metadata after the command is executed. Symlinks are"
         " followed."))

    named_args.add_argument(
        "-k",
        "--key",
        type=str,
        metavar="<path>",
        help=
        ("Path to a PEM formatted private key file used to sign the resulting"
         " link metadata."
         " (passing one of '--key' or '--gpg' is required)"))

    named_args.add_argument(
        "-g",
        "--gpg",
        nargs="?",
        const=True,
        metavar="<id>",
        help=
        ("GPG keyid used to sign the resulting link metadata.  When '--gpg' is"
         " passed without keyid, the keyring's default GPG key is used."
         " (passing one of '--key' or '--gpg' is required)"))

    parser.add_argument(
        "--gpg-home",
        dest="gpg_home",
        type=str,
        metavar="<path>",
        help=
        ("Path to GPG keyring to load GPG key identified by '--gpg' option.  If"
         " '--gpg-home' is not passed, the default GPG keyring is used."))

    parser.add_argument(
        "-s",
        "--record-streams",
        dest="record_streams",
        default=False,
        action="store_true",
        help=
        ("If passed 'stdout' and 'stderr' of the executed command are redirected"
         " and stored in the resulting link metadata."))

    parser.add_argument(
        "-x",
        "--no-command",
        dest="no_command",
        default=False,
        action="store_true",
        help=
        ("Generate link metadata without executing a command, e.g. for a 'signed"
         " off by' step."))

    parser.add_argument(*EXCLUDE_ARGS, **EXCLUDE_KWARGS)
    parser.add_argument(*BASE_PATH_ARGS, **BASE_PATH_KWARGS)

    verbosity_args = parser.add_mutually_exclusive_group(required=False)
    verbosity_args.add_argument("-v",
                                "--verbose",
                                dest="verbose",
                                help="Verbose execution.",
                                action="store_true")

    verbosity_args.add_argument("-q",
                                "--quiet",
                                dest="quiet",
                                help="Suppress all output.",
                                action="store_true")

    # FIXME: This is not yet ideal.
    # What should we do with tokens like > or ; ?
    parser.add_argument(
        "link_cmd",
        nargs="*",
        metavar="<command>",
        help=(
            "Command to be executed with options and arguments, separated from"
            " 'in-toto-run' options by double dash '--'."))

    args = parser.parse_args()

    log.setLevelVerboseOrQuiet(args.verbose, args.quiet)

    # Override defaults in settings.py with environment variables and RCfiles
    in_toto.user_settings.set_settings()

    # Regular signing and GPG signing are mutually exclusive
    if (args.key == None) == (args.gpg == None):
        parser.print_usage()
        parser.error("Specify either `--key <key path>` or `--gpg [<keyid>]`")

    # If `--gpg` was set without argument it has the value `True` and
    # we will try to sign with the default key
    gpg_use_default = (args.gpg == True)

    # Otherwise we interpret it as actual keyid
    gpg_keyid = None
    if args.gpg != True:
        gpg_keyid = args.gpg

    # If no_command is specified run in_toto_run without executing a command
    if args.no_command:
        args.link_cmd = []

    elif not args.link_cmd:  # pragma: no branch
        parser.print_usage()
        parser.error("No command specified."
                     " Please specify (or use the --no-command option)")

    try:
        # We load the key here because it might prompt the user for a password in
        # case the key is encrypted. Something that should not happen in the lib.
        key = None
        if args.key:
            key = util.prompt_import_rsa_key_from_file(args.key)

        runlib.in_toto_run(args.step_name, args.materials, args.products,
                           args.link_cmd, args.record_streams, key, gpg_keyid,
                           gpg_use_default, args.gpg_home,
                           args.exclude_patterns, args.base_path)

    except Exception as e:
        log.error("(in-toto-run) {0}: {1}".format(type(e).__name__, e))
        sys.exit(1)

    sys.exit(0)
Exemple #11
0
def _sign_and_dump_metadata(metadata, args):
    """
  <Purpose>
    Internal method to sign link or layout metadata and dump it to disk.

  <Arguments>
    metadata:
            Metablock object (contains Link or Layout object)
    args:
            see argparser

  <Exceptions>
    SystemExit(0) if signing is successful
    SystemExit(2) if any exception occurs

  """

    try:
        if not args.append:
            metadata.signatures = []

        signature = None
        # If the cli tool was called with `--gpg [KEYID ...]` `args.gpg` is
        # a list (not None) and we will try to sign with gpg.
        # If `--gpg-home` was not set, args.gpg_home is None and the signer tries
        # to use the default gpg keyring.
        if args.gpg != None:
            # If `--gpg` was passed without argument we sign with the default key
            # Excluded so that coverage does not vary in different test environments
            if len(args.gpg) == 0:  # pragma: no cover
                signature = metadata.sign_gpg(gpg_keyid=None,
                                              gpg_home=args.gpg_home)

            # Otherwise we sign with each passed keyid
            for keyid in args.gpg:
                securesystemslib.formats.KEYID_SCHEMA.check_match(keyid)
                signature = metadata.sign_gpg(gpg_keyid=keyid,
                                              gpg_home=args.gpg_home)

        # Alternatively we iterate over passed private key paths `--key KEYPATH ...`
        # load the corresponding key from disk and sign with it
        elif args.key != None:  # pragma: no branch
            for key_path in args.key:
                key = util.prompt_import_rsa_key_from_file(key_path)
                signature = metadata.sign(key)

        # If `--output` was specified we store the signed link or layout metadata
        # to that location no matter what
        if args.output:
            out_path = args.output

        # Otherwise, in case of links, we build the filename using the link/step
        # name and the keyid of the created signature (there is only one for links)
        elif metadata.type_ == "link":
            in_toto.formats.ANY_SIGNATURE_SCHEMA.check_match(signature)
            keyid = signature["keyid"]
            out_path = FILENAME_FORMAT.format(step_name=metadata.signed.name,
                                              keyid=keyid)

        # In case of layouts we just override the input file.
        elif metadata.type_ == "layout":  # pragma: no branch
            out_path = args.file

        log.info("Dumping {0} to '{1}'...".format(metadata.type_, out_path))

        metadata.dump(out_path)
        sys.exit(0)

    except Exception as e:
        log.error("The following error occurred while signing: "
                  "{}".format(e))
        sys.exit(2)