Beispiel #1
0
    def setUpClass(self):
        """Creates and changes into temporary directory and prepares two layouts.
    The superlayout, which has one step and its sublayout, which is the usual
    demo layout (write code, package, inspect tar). """

        # Backup original cwd
        self.working_dir = os.getcwd()

        # Find demo files
        demo_files = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                  "demo_files")

        # Create and change into temporary directory
        self.test_dir = os.path.realpath(tempfile.mkdtemp())
        os.chdir(self.test_dir)

        # Copy demo files to temp dir
        for file in os.listdir(demo_files):
            shutil.copy(os.path.join(demo_files, file), self.test_dir)

        self.demo_layout = Metablock.load("demo.layout.template")
        self.code_link = Metablock.load("package.2f89b927.link")
        self.package_link = Metablock.load("write-code.776a00e2.link")
        self.demo_links = {
            "write-code": self.code_link,
            "package": self.package_link
        }
Beispiel #2
0
    def test_main_optional_args(self):
        """Test CLI command with optional arguments. """

        named_args = [
            "--step-name", self.test_step, "--key", self.rsa_key_path,
            "--materials", self.test_artifact, "--products",
            self.test_artifact, "--record-streams"
        ]
        positional_args = ["--", "python", "--version"]

        # Give wrong password whenever prompted.
        with mock.patch('in_toto.util.prompt_password', return_value='x'):

            # Test and assert recorded artifacts
            args1 = named_args + positional_args
            self.assert_cli_sys_exit(args1, 0)
            link_metadata = Metablock.load(self.test_link_rsa)
            self.assertTrue(self.test_artifact in list(
                link_metadata.signed.materials.keys()))
            self.assertTrue(self.test_artifact in list(
                link_metadata.signed.products.keys()))

            # Test and assert exlcuded artifacts
            args2 = named_args + ["--exclude", "*test*"] + positional_args
            self.assert_cli_sys_exit(args2, 0)
            link_metadata = Metablock.load(self.test_link_rsa)
            self.assertFalse(link_metadata.signed.materials)
            self.assertFalse(link_metadata.signed.products)

            # Test with base path
            args3 = named_args + ["--base-path", self.test_dir
                                  ] + positional_args
            self.assert_cli_sys_exit(args3, 0)
            link_metadata = Metablock.load(self.test_link_rsa)
            self.assertListEqual(list(link_metadata.signed.materials.keys()),
                                 [self.test_artifact])
            self.assertListEqual(list(link_metadata.signed.products.keys()),
                                 [self.test_artifact])

            # Test with bogus base path
            args4 = named_args + ["--base-path", "bogus/path"
                                  ] + positional_args
            self.assert_cli_sys_exit(args4, 1)

            # Test with lstrip path
            strip_prefix = self.test_artifact[:-1]
            args5 = named_args + ["--lstrip-paths", strip_prefix
                                  ] + positional_args
            self.assert_cli_sys_exit(args5, 0)
            link_metadata = Metablock.load(self.test_link_rsa)
            self.assertListEqual(list(link_metadata.signed.materials.keys()),
                                 [self.test_artifact[len(strip_prefix):]])
            self.assertListEqual(list(link_metadata.signed.products.keys()),
                                 [self.test_artifact[len(strip_prefix):]])
  def test_substitute(self):
    """Do a simple substitution on the expected_command field"""
    signed_layout = Metablock(signed=self.layout)
    signed_layout.sign(self.alice)

    # we will catch a LinkNotFound error because we don't have (and don't need)
    # the metadata.
    with self.assertRaises(in_toto.exceptions.LinkNotFoundError):
      in_toto_verify(signed_layout, self.alice_pub_dict,
          substitution_parameters={"EDITOR":"vim"})

    self.assertEqual(self.layout.steps[0].expected_command[0], "vim")
Beispiel #4
0
 def test_create_unfinished_metadata_with_expected_material(self):
     """Test record start creates metadata with expected material. """
     in_toto_record_start(self.step_name, [self.test_material], self.key)
     link = Metablock.load(self.link_name_unfinished)
     self.assertEquals(list(link.signed.materials.keys()),
                       [self.test_material])
     os.remove(self.link_name_unfinished)
Beispiel #5
0
  def test_compare_metadata_with_and_without_metadata_directory(self):
    """Test record stop with and without metadata directory,
     compare the expected product"""
    tmp_dir = os.path.realpath(tempfile.mkdtemp(dir=os.getcwd()))
    in_toto_record_start(self.step_name, [], self.key)
    in_toto_record_stop(self.step_name, [self.test_product], self.key,
        metadata_directory=tmp_dir)
    link_path = os.path.join(tmp_dir, self.link_name)
    link_with_md = Metablock.load(link_path)

    in_toto_record_start(self.step_name, [], self.key)
    in_toto_record_stop(self.step_name, [self.test_product], self.key)
    link_without_md = Metablock.load(self.link_name)
    self.assertEqual(link_with_md.signed, link_without_md.signed)
    os.remove(link_path)
    os.remove(self.link_name)
Beispiel #6
0
 def test_create_unfininished_metadata_verify_signature(self):
   """Test record start creates metadata with expected signature. """
   in_toto_record_start(
       self.step_name, [self.test_material], self.key)
   link = Metablock.load(self.link_name_unfinished)
   link.verify_signature(self.key)
   os.remove(self.link_name_unfinished)
Beispiel #7
0
  def test_in_toto_run_compare_with_and_without_metadata_directory(self):
    """Successfully run with and without metadata directory,
     compare the signed is equal"""
    tmp_dir = os.path.realpath(tempfile.mkdtemp(dir=os.getcwd()))
    in_toto_run(self.step_name, [self.test_artifact], [self.test_artifact],
        ["python", "--version"], True, self.key, metadata_directory=tmp_dir)
    file_path = os.path.join(tmp_dir, FILENAME_FORMAT.format(step_name=self.step_name,
        keyid=self.key["keyid"]))
    link_dump_with_md = Metablock.load(file_path)

    in_toto_run(self.step_name, [self.test_artifact], [self.test_artifact],
        ["python", "--version"], True, self.key)
    link_dump_without_md = Metablock.load(
        FILENAME_FORMAT.format(step_name=self.step_name, keyid=self.key["keyid"]))
    self.assertEqual(repr(link_dump_with_md.signed),
        repr(link_dump_without_md.signed))
Beispiel #8
0
 def test_create_metadata_with_expected_cwd(self):
     """Test record start/stop run, verify cwd. """
     in_toto_record_start(self.step_name, [], self.key)
     in_toto_record_stop(self.step_name, [self.test_product], self.key)
     link = Metablock.load(self.link_name)
     self.assertEquals(link.signed.environment["workdir"], os.getcwd())
     os.remove(self.link_name)
Beispiel #9
0
 def test_in_toto_run_compare_dumped_with_returned_link(self):
   """Successfully run, compare dumped link is equal to returned link. """
   link = in_toto_run(self.step_name, [self.test_artifact],
       [self.test_artifact], ["python", "--version"], True, self.key)
   link_dump = Metablock.load(
       FILENAME_FORMAT.format(step_name=self.step_name, keyid=self.key["keyid"]))
   self.assertEqual(repr(link), repr(link_dump))
Beispiel #10
0
 def test_create_metadata_verify_signature(self):
     """Test record start creates metadata with expected signature. """
     in_toto_record_start(self.step_name, [], self.key)
     in_toto_record_stop(self.step_name, [], self.key)
     link = Metablock.load(self.link_name)
     link.verify_signature(self.key)
     os.remove(self.link_name)
Beispiel #11
0
    def __verify_in_toto_metadata(self, target_relpath, in_toto_inspection_packet):
        # Make a temporary directory in a parent directory we control.
        tempdir = tempfile.mkdtemp(dir=REPOSITORIES_DIR)

        # Copy files over into temp dir.
        for rel_path in in_toto_inspection_packet:
            # Don't confuse Python with any leading path separator.
            rel_path = rel_path.lstrip('/')
            abs_path = os.path.join(self.__targets_dir, rel_path)
            shutil.copy(abs_path, tempdir)

        # Switch to the temp dir.
        os.chdir(tempdir)

        # Load the root layout and public keys.
        layout = Metablock.load('root.layout')
        pubkeys = glob.glob('*.pub')
        layout_key_dict = import_public_keys_from_files_as_dict(pubkeys)
        # Parameter substitution.
        params = substitute(target_relpath)

        try:
            verifylib.in_toto_verify(layout, layout_key_dict, substitution_parameters=params)
        except:
            logger.exception('in-toto failed to verify {}'.format(target_relpath))
            raise
        else:
            logger.info('in-toto verified {}'.format(target_relpath))
        finally:
            # Switch back to a parent directory we control, so that we can
            # safely delete temp dir.
            os.chdir(REPOSITORIES_DIR)
            # Delete temp dir.
            shutil.rmtree(tempdir)
Beispiel #12
0
 def test_verify_failing_inspection_rules(self):
     """Test fail verification with failing inspection artifact rule. """
     layout = Metablock.load(self.layout_failing_inspection_rule_path)
     layout_key_dict = import_rsa_public_keys_from_files_as_dict(
         [self.alice_path])
     with self.assertRaises(RuleVerficationError):
         in_toto_verify(layout, layout_key_dict)
Beispiel #13
0
 def test_verify_failing_inspection_exits_non_zero(self):
     """Test fail verification with inspection returning non-zero. """
     layout = Metablock.load(self.layout_failing_inspection_retval)
     layout_key_dict = import_rsa_public_keys_from_files_as_dict(
         [self.alice_path])
     with self.assertRaises(BadReturnValueError):
         in_toto_verify(layout, layout_key_dict)
Beispiel #14
0
  def setUpClass(self):
    """Copy test gpg rsa keyring, gpg demo metadata files and demo final
    product to tmp test dir. """
    # Copy gpg demo metadata files
    demo_files = os.path.join(
        os.path.dirname(os.path.realpath(__file__)), "demo_files_gpg")

    # find where the scripts directory is located.
    scripts_directory = os.path.join(
        os.path.dirname(os.path.realpath(__file__)), "scripts")

    self.set_up_test_dir()
    self.set_up_gpg_keys()

    for fn in os.listdir(demo_files):
      shutil.copy(os.path.join(demo_files, fn), self.test_dir)

    # Change into test dir
    shutil.copytree(scripts_directory, 'scripts')

    # Sign layout template with gpg key
    layout_template = Metablock.load("demo.layout.template")

    self.layout_path = "gpg_signed.layout"
    layout_template.sign_gpg(self.gpg_key_0C8A17, self.gnupg_home)
    layout_template.dump(self.layout_path)
Beispiel #15
0
 def test_verify_failing_layout_expired(self):
     """Test fail verification with expired layout. """
     layout = Metablock.load(self.layout_expired_path)
     layout_key_dict = import_rsa_public_keys_from_files_as_dict(
         [self.alice_path, self.bob_path])
     with self.assertRaises(LayoutExpiredError):
         in_toto_verify(layout, layout_key_dict)
Beispiel #16
0
    def setUp(self):
        self.item_name = "item"
        self.sha256_1 = \
            "d65165279105ca6773180500688df4bdc69a2c7b771752f0a46ef120b7fd8ec3"
        self.sha256_2 = \
            "cfdaaf1ab2e4661952a9dec5e8fa3c360c1b06b1a073e8493a7c46d2af8c504b"

        self.links = {
            "item":
            Metablock(signed=Link(name="item",
                                  materials={
                                      "foo": {
                                          "sha256": self.sha256_1
                                      },
                                      "foobar": {
                                          "sha256": self.sha256_1
                                      },
                                      "bar": {
                                          "sha256": self.sha256_1
                                      }
                                  },
                                  products={
                                      "baz": {
                                          "sha256": self.sha256_1
                                      },
                                      "foo": {
                                          "sha256": self.sha256_1
                                      },
                                      "bar": {
                                          "sha256": self.sha256_2
                                      }
                                  }))
        }
Beispiel #17
0
 def test_verify_failing_bad_signature(self):
     """Test fail verification with bad layout signature. """
     layout = Metablock.load(self.layout_bad_sig)
     layout_key_dict = import_rsa_public_keys_from_files_as_dict(
         [self.alice_path])
     with self.assertRaises(SignatureVerificationError):
         in_toto_verify(layout, layout_key_dict)
Beispiel #18
0
 def test_verify_failing_missing_key(self):
     """Test fail verification with missing layout key. """
     layout = Metablock.load(self.layout_double_signed_path)
     layout_key_dict = import_rsa_public_keys_from_files_as_dict(
         [self.bob_path])
     with self.assertRaises(SignatureVerificationError):
         in_toto_verify(layout, layout_key_dict)
Beispiel #19
0
    def setUpClass(self):
        """Copy test gpg rsa keyring, gpg demo metadata files and demo final
    product to tmp test dir. """

        self.working_dir = os.getcwd()
        self.test_dir = os.path.realpath(tempfile.mkdtemp())

        # Copy gpg keyring
        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)

        self.owner_gpg_keyid = "8465a1e2e0fb2b40adb2478e18fb3f537e0c8a17"

        # Copy gpg demo metadata files
        demo_files = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                  "demo_files_gpg")
        for file in os.listdir(demo_files):
            shutil.copy(os.path.join(demo_files, file), self.test_dir)

        # Change into test dir
        os.chdir(self.test_dir)

        # Sign layout template with gpg key
        layout_template = Metablock.load("demo.layout.template")

        self.layout_path = "gpg_signed.layout"
        layout_template.sign_gpg(self.owner_gpg_keyid, self.gnupg_home)
        layout_template.dump(self.layout_path)
Beispiel #20
0
    def test_create_grafeas_occurrence_from_in_toto_link(self):
        in_toto_link = Metablock.load("links/clone.776a00e2.link")
        grafeas_occurrence = \
            GrafeasInTotoOccurrence.from_link(in_toto_link, "clone",
                                              "clone-resource-uri")

        self.assertIsInstance(grafeas_occurrence, GrafeasInTotoOccurrence)
Beispiel #21
0
 def test_create_metadata_with_expected_product(self):
     """Test record stop records expected product. """
     in_toto_record_start(self.step_name, self.key, [])
     in_toto_record_stop(self.step_name, self.key, [self.test_product])
     link = Metablock.load(self.link_name)
     self.assertEquals(link.signed.products.keys(), [self.test_product])
     os.remove(self.link_name)
Beispiel #22
0
    def test_normalize_line_endings(self):
        """Test cross-platform line ending normalization. """
        paths = []
        try:
            # Create three artifacts with same content but different line endings
            for line_ending in [b"\n", b"\r", b"\r\n"]:
                fd, path = tempfile.mkstemp()
                paths.append(path)
                os.write(fd, b"hello" + line_ending + b"toto")
                os.close(fd)

            # Call in_toto_record start and stop and record artifacts as
            # materials and products with line ending normalization on
            in_toto_record_start(self.step_name,
                                 paths,
                                 self.key,
                                 normalize_line_endings=True)
            in_toto_record_stop(self.step_name,
                                paths,
                                self.key,
                                normalize_line_endings=True)
            link = Metablock.load(self.link_name).signed

            # Check that all three hashes in materials and products are equal
            for artifact_dict in [link.materials, link.products]:
                hash_dicts = list(artifact_dict.values())
                self.assertTrue(hash_dicts[1:] == hash_dicts[:-1])

        # Clean up
        finally:
            for path in paths:
                os.remove(path)
Beispiel #23
0
 def __load_root_layout(self, target_relpath):
     root_layout = Metablock.load(IN_TOTO_ROOT_LAYOUT)
     root_layout_pubkeys = glob.glob('*.pub')
     root_layout_pubkeys = import_public_keys_from_files_as_dict(
         root_layout_pubkeys)
     # Parameter substitution.
     root_layout_params = substitute(target_relpath)
     return root_layout, root_layout_pubkeys, root_layout_params
Beispiel #24
0
    def test_create_in_toto_link_from_grafeas_occurrence(self):
        grafeas_occurrence = \
            GrafeasInTotoOccurrence.load("occurrences/clone.776a00e2.occurrence")
        in_toto_link = grafeas_occurrence.to_link("clone")
        self.assertIsInstance(in_toto_link, Metablock)

        in_toto_link_original = Metablock.load("links/clone.776a00e2.link")
        self.assertEqual(in_toto_link, in_toto_link_original)
Beispiel #25
0
def in_toto_record_stop(step_name, key, product_list):
    """
  <Purpose>
    Finishes creating link metadata for a multi-part in-toto step.
    Loads signing key and unfinished link metadata file from disk, verifies
    that the file was signed with the key, records products, updates unfinished
    Link object (products and signature), removes unfinished link file from and
    stores new link file to disk.

  <Arguments>
    step_name:
            A unique name to relate link metadata with a step defined in the
            layout.
    key:
            Private key to sign link metadata.
            Format is securesystemslib.formats.KEY_SCHEMA
    product_list:
            List of file or directory paths that should be recorded as products.

  <Exceptions>
    None.

  <Side Effects>
    Writes newly created link metadata file to disk using the filename scheme
    from link.FILENAME_FORMAT
    Removes unfinished link file link.UNFINISHED_FILENAME_FORMAT from disk

  <Returns>
    None.

  """
    fn = FILENAME_FORMAT.format(step_name=step_name, keyid=key["keyid"])
    unfinished_fn = UNFINISHED_FILENAME_FORMAT.format(step_name=step_name,
                                                      keyid=key["keyid"])
    log.info("Stop recording '{}'...".format(step_name))

    # Expects an a file with name UNFINISHED_FILENAME_FORMAT in the current dir
    log.info("Loading preliminary link metadata '{}'...".format(unfinished_fn))
    link_metadata = Metablock.load(unfinished_fn)

    # The file must have been signed by the same key
    log.info("Verifying preliminary link signature...")
    keydict = {key["keyid"]: key}
    link_metadata.verify_signatures(keydict)

    if product_list:
        log.info("Recording products '{}'...".format(", ".join(product_list)))
    link_metadata.signed.products = record_artifacts_as_dict(product_list)

    log.info("Updating signature with key '{:.8}...'...".format(key["keyid"]))
    link_metadata.signatures = []
    link_metadata.sign(key)

    log.info("Storing link metadata to '{}'...".format(fn))
    link_metadata.dump(fn)

    log.info("Removing unfinished link metadata '{}'...".format(unfinished_fn))
    os.remove(unfinished_fn)
Beispiel #26
0
def load_links_for_layout(layout):
    """
  <Purpose>
    Try to load all existing metadata files for each Step of the Layout
    from the current directory.

    For each step the metadata might consist of multiple (thresholds) Link
    or Layout (sub-layouts) files.

  <Arguments>
    layout:
          Layout object

  <Side Effects>
    Calls function to read files from disk

  <Returns>
    A dictionary carrying all the found metadata corresponding to the
    passed layout, e.g.:

    {
      <step name> : {
        <functionary key id> : <Metablock containing a Link or Layout object>,
        ...
      }, ...
    }


  """
    steps_metadata = {}

    # Iterate over all the steps in the layout
    for step in layout.steps:
        links_per_step = {}

        # We try to load a link for every authorized functionary, but don't fail
        # if the file does not exist (authorized != required)
        # FIXME: Should we really pass on IOError, or just skip inexistent links
        for keyid in step.pubkeys:
            filename = FILENAME_FORMAT.format(step_name=step.name, keyid=keyid)

            try:
                metadata = Metablock.load(filename)
                links_per_step[keyid] = metadata

            except IOError as e:
                pass

        # Check if the step has been performed by enough number of functionaries
        if len(links_per_step) < step.threshold:
            raise in_toto.exceptions.LinkNotFoundError(
                "Step '{0}' requires '{1}'"
                " link metadata file(s), found '{2}'.".format(
                    step.name, step.threshold, len(links_per_step)))

        steps_metadata[step.name] = links_per_step

    return steps_metadata
Beispiel #27
0
 def test_verify_failing_link_metadata_files(self):
     """Test fail verification with link metadata files not found. """
     os.rename("package.2f89b927.link", "package.link.bak")
     layout = Metablock.load(self.layout_single_signed_path)
     layout_key_dict = import_rsa_public_keys_from_files_as_dict(
         [self.alice_path])
     with self.assertRaises(in_toto.exceptions.LinkNotFoundError):
         in_toto_verify(layout, layout_key_dict)
     os.rename("package.link.bak", "package.2f89b927.link")
Beispiel #28
0
 def test_in_toto_run_with_metadata_directory(self):
   """Successfully run with metadata directory,
    compare dumped link is equal to returned link"""
   tmp_dir = os.path.realpath(tempfile.mkdtemp(dir=os.getcwd()))
   link = in_toto_run(self.step_name, [self.test_artifact],
       [self.test_artifact], ["python", "--version"], True, self.key,
       metadata_directory=tmp_dir)
   file_path = os.path.join(tmp_dir, FILENAME_FORMAT.format(step_name=self.step_name,
       keyid=self.key["keyid"]))
   link_dump = Metablock.load(file_path)
   self.assertEqual(repr(link), repr(link_dump))
Beispiel #29
0
def get_summary_link(layout, reduced_chain_link_dict, name):
  """
  <Purpose>
    Merges the materials of the first step (as mentioned in the layout)
    and the products of the last step and returns a new link.
    This link reports the materials and products and summarizes the
    overall software supply chain.
    NOTE: The assumption is that the steps mentioned in the layout are
    to be performed sequentially. So, the first step mentioned in the
    layout denotes what comes into the supply chain and the last step
    denotes what goes out.

  <Arguments>
    layout:
            The layout specified by the project owner.

    reduced_chain_link_dict:
            A dictionary containing link metadata per step,
            e.g.:
            {
              <link name> : <Metablock containing a Link object>,
              ...
            }
    name:
            The name that the summary link will be associated with.

  <Exceptions>
    None.

  <Side Effects>
    None.

  <Returns>
    A Metablock object containing a Link which summarizes the materials and
    products of the overall software supply chain.

  """
  # Create empty link object
  summary_link = in_toto.models.link.Link()

  # Take first and last link in the order the corresponding
  # steps appear in the layout, if there are any.
  if len(layout.steps) > 0:
    first_step_link = reduced_chain_link_dict[layout.steps[0].name]
    last_step_link = reduced_chain_link_dict[layout.steps[-1].name]

    summary_link.materials = first_step_link.signed.materials
    summary_link.name = name

    summary_link.products = last_step_link.signed.products
    summary_link.byproducts = last_step_link.signed.byproducts
    summary_link.command = last_step_link.signed.command

  return Metablock(signed=summary_link)
Beispiel #30
0
def main():
    key_owner = interface.import_rsa_privatekey_from_file("./keys/owner")
    key_clone = interface.import_rsa_publickey_from_file("./keys/clone.pub")
    key_build = interface.import_rsa_publickey_from_file("./keys/build.pub")
    key_build_image = interface.import_rsa_publickey_from_file(
        "./keys/build-image.pub")

    layout = Layout.read({
        "_type":
        "layout",
        "keys": {
            key_clone["keyid"]: key_clone,
            key_build["keyid"]: key_build,
            key_build_image["keyid"]: key_build_image,
        },
        "steps": [{
            "name":
            "clone",
            "expected_materials": [["DISALLOW", "*"]],
            "expected_products": [["CREATE", "*"]],
            "pubkeys": [key_clone["keyid"]],
            "expected_command": [
                "git", "clone",
                "https://gitlab.com/boxboat/demos/intoto-spire/go-hello-world"
            ],
            "threshold":
            1,
        }, {
            "name":
            "build",
            "expected_materials":
            [["MATCH", "*", "WITH", "PRODUCTS", "FROM", "clone"],
             ["DISALLOW", "*"]],
            "expected_products": [["CREATE", "go-hello-world"],
                                  ["DISALLOW", "*"]],
            "pubkeys": [key_build["keyid"]],
            "expected_command": ["go", "build", "./..."],
            "threshold":
            1,
        }, {
            "name":
            "build-image",
            "expected_materials":
            [["MATCH", "*", "WITH", "PRODUCTS", "FROM", "clone"],
             ["DISALLOW", "*"]],
            "expected_products": [["CREATE", "image-id"],
                                  ["CREATE", "go-hello-world.tar"],
                                  ["DISALLOW", "*"]],
            "pubkeys": [key_build_image["keyid"]],
            "threshold":
            1,
        }],
        "inspect": []
    })

    metadata = Metablock(signed=layout)

    # Sign and dump layout to "root.layout"
    metadata.sign(key_owner)
    metadata.dump("root.layout")