示例#1
0
def verify_all_item_rules(items, links):
    """
  <Purpose>
    Iteratively verifies artifact rules of passed items (Steps or Inspections).

  <Arguments>
    items:
            A list containing Step or Inspection objects whose material
            and product rules will be verified.

    links:
            A dictionary containing link metadata per step or inspection, e.g.:
            {
              <link name> : <Metablock containing a Link object>,
              ...
            }

  <Exceptions>
    None.

  <Side Effects>
    None.

  """

    for item in items:

        link = links[item.name]
        log.info("Verifying material rules for '{}'...".format(item.name))
        verify_item_rules(item.name, "materials", item.expected_materials,
                          links)

        log.info("Verifying product rules for '{}'...".format(item.name))
        verify_item_rules(item.name, "products", item.expected_products, links)
示例#2
0
def run_all_inspections(layout):
    """
  <Purpose>
    Extracts all inspections from a passed Layout's inspect field and
    iteratively runs each command defined in the Inspection's `run` field using
    `runlib.in_toto_run`, which returns a Metablock object containing a Link
    object.

    If a link command returns non-zero the verification is aborted.

  <Arguments>
    layout:
            A Layout object which is used to extract the Inspections.

  <Exceptions>
    Calls function that raises BadReturnValueError if an inspection returned
    non-int or non-zero.

  <Returns>
    A dictionary of metadata about the executed inspections, e.g.:

    {
      <inspection name> : {
        <Metablock containing a Link object>,
        ...
      }, ...
    }

  """
    inspection_links_dict = {}
    for inspection in layout.inspect:
        log.info("Executing command for inspection '{}'...".format(
            inspection.name))

        # FIXME: We don't want to use the base path for runlib so we patch this
        # for now. This will not stay!
        base_path_backup = in_toto.settings.ARTIFACT_BASE_PATH
        in_toto.settings.ARTIFACT_BASE_PATH = None

        # FIXME: What should we record as material/product?
        # Is the current directory a sensible default? In general?
        # If so, we should probably make it a default in run_link
        # We could use artifact rule paths.
        material_list = product_list = ["."]
        link = in_toto.runlib.in_toto_run(inspection.name, material_list,
                                          product_list, inspection.run)

        _raise_on_bad_retval(link.signed.byproducts.get("return-value"),
                             inspection.run)

        inspection_links_dict[inspection.name] = link

        # Dump the inspection link file for auditing
        # Keep in mind that this pollutes the verifier's (client's) filesystem.
        filename = FILENAME_FORMAT_SHORT.format(step_name=inspection.name)
        link.dump(filename)

        in_toto.settings.ARTIFACT_BASE_PATH = base_path_backup

    return inspection_links_dict
示例#3
0
def in_toto_record_start(step_name, key, material_list):
    """
  <Purpose>
    Starts creating link metadata for a multi-part in-toto step. I.e.
    records passed materials, creates link meta data object from it, signs it
    with passed key and stores it to disk with UNFINISHED_FILENAME_FORMAT.

  <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
    material_list:
            List of file or directory paths that should be recorded as
            materials.

  <Exceptions>
    None.

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

  <Returns>
    None.

  """

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

    if material_list:
        log.info("Recording materials '{}'...".format(
            ", ".join(material_list)))
    materials_dict = record_artifacts_as_dict(material_list)

    log.info("Creating preliminary link metadata...")
    link = in_toto.models.link.Link(name=step_name,
                                    materials=materials_dict,
                                    products={},
                                    command=[],
                                    byproducts={},
                                    environment={"workdir": os.getcwd()})

    link_metadata = Metablock(signed=link)

    log.info("Signing link metadata with key '{:.8}...'...".format(
        key["keyid"]))
    link_metadata.sign(key)

    log.info(
        "Storing preliminary link metadata to '{}'...".format(unfinished_fn))
    link_metadata.dump(unfinished_fn)
示例#4
0
def verify_all_steps_signatures(layout, chain_link_dict):
    """
  <Purpose>
    Extracts the Steps of a passed Layout and iteratively verifies the
    the signatures of the Link object(s) related to each Step by the name field.
    The public keys used for verification are also extracted from the Layout.

  <Arguments>
    layout:
            A Layout object whose Steps are extracted and verified.

    chain_link_dict:
            A dictionary containing link metadata per functionary per step,
            e.g.:
            {
              <link name> : {
                <functionary key id> : <Metablock containing a Link or Layout
                                        object>,
                ...
              }, ...
            }

  <Exceptions>
    Raises an exception if a needed key can not be found in the passed
    keys_dict or if a verification fails.
    TBA (see https://github.com/in-toto/in-toto/issues/6)

  """
    for step in layout.steps:
        # Find the according link for this step
        key_link_dict = chain_link_dict[step.name]

        for keyid, link in six.iteritems(key_link_dict):
            keys_dict = {}

            # Create the dictionary of keys for this step
            # Only one key with the matching keyid in the
            # filename is added to the dictionary which ensures
            # that the link has been signed by that key
            if keyid in step.pubkeys:
                keys_dict[keyid] = layout.keys[keyid]
            else:
                raise AuthorizationError(
                    "Unauthorized Key! '{0}'".format(keyid))

            log.info("Verifying signature(s) for '{0}'...".format(
                in_toto.models.link.FILENAME_FORMAT.format(step_name=step.name,
                                                           keyid=keyid)))

            # Verify link metadata file's signatures
            verify_link_signatures(link, keys_dict)
示例#5
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)
示例#6
0
def verify_all_steps_command_alignment(layout, chain_link_dict):
    """
  <Purpose>
    Iteratively checks if all expected commands as defined in the
    Steps of a Layout align with the actual commands as recorded in the Link
    metadata.

  <Arguments>
    layout:
            A Layout object to extract the expected commands from.

    chain_link_dict:
            A dictionary containing link metadata per functionary per step,
            e.g.:
            {
              <link name> : {
                <functionary key id> : <Metablock containing a Link object>,
                ...
              }, ...
            }

  <Exceptions>
    None.

  <Side Effects>
    None.

  """
    for step in layout.steps:
        # Find the according link for this step
        expected_command = step.expected_command
        key_link_dict = chain_link_dict[step.name]

        # FIXME: I think we could do this for one link per step only
        # providing that we verify command alignment AFTER threshold equality
        for keyid, link in six.iteritems(key_link_dict):
            log.info("Verifying command alignment for '{0}'...".format(
                in_toto.models.link.FILENAME_FORMAT.format(step_name=step.name,
                                                           keyid=keyid)))

            command = link.signed.command
            verify_command_alignment(command, expected_command)
示例#7
0
def in_toto_mock(name, link_cmd_args):
    """
  <Purpose>
    in_toto_run with defaults
     - Records materials and products in current directory
     - Does not sign resulting link file
     - Stores resulting link file under "<name>.link"

  <Arguments>
    name:
            A unique name to relate mock link metadata with a step or
            inspection defined in the layout.
    link_cmd_args:
            A list where the first element is a command and the remaining
            elements are arguments passed to that command.

  <Exceptions>
    None.

  <Side Effects>
    Writes newly created link metadata file to disk using the filename scheme
    from link.FILENAME_FORMAT_SHORT

  <Returns>
    Newly created Metablock object containing a Link object

  """
    link = in_toto_run(name, ["."], ["."],
                       link_cmd_args,
                       key=False,
                       record_streams=True)

    link_metadata = Metablock(signed=link)

    filename = FILENAME_FORMAT_SHORT.format(step_name=name)
    log.info("Storing unsigned link metadata to '{}.link'...".format(filename))
    link_metadata.dump(filename)
    return link_metadata
示例#8
0
def in_toto_verify(layout_path, layout_key_paths):
    """
  <Purpose>
    Loads the layout metadata as Metablock object (containg a Layout object)
    and the signature verification keys from the passed paths,
    calls   verifylib.in_toto_verify   and handles exceptions.

  <Arguments>
    layout_path:
            Path to layout metadata file that is being verified.

    layout_key_paths:
            List of paths to project owner public keys, used to verify the
            layout's signature.

  <Exceptions>
    SystemExit if any exception occurs

  <Side Effects>
    Calls sys.exit(1) if an exception is raised

  <Returns>
    None.

  """
    try:
        log.info("Verifying software supply chain...")

        log.info("Reading layout...")
        layout = Metablock.load(layout_path)

        log.info("Reading layout key(s)...")
        layout_key_dict = in_toto.util.import_rsa_public_keys_from_files_as_dict(
            layout_key_paths)

        verifylib.in_toto_verify(layout, layout_key_dict)
    except Exception as e:
        log.fail_verification("{0} - {1}".format(type(e).__name__, e))
        sys.exit(1)
示例#9
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)
示例#10
0
def in_toto_run(name,
                material_list,
                product_list,
                link_cmd_args,
                key=False,
                record_streams=False):
    """
  <Purpose>
    Calls function to run command passed as link_cmd_args argument, storing
    its materials, by-products and return value, and products into a link
    metadata file. The link metadata file is signed with the passed key and
    stored to disk.

  <Arguments>
    name:
            A unique name to relate link metadata with a step or inspection
            defined in the layout.
    material_list:
            List of file or directory paths that should be recorded as
            materials.
    product_list:
            List of file or directory paths that should be recorded as
            products.
    link_cmd_args:
            A list where the first element is a command and the remaining
            elements are arguments passed to that command.
    key: (optional)
            Private key to sign link metadata.
            Format is securesystemslib.formats.KEY_SCHEMA
    record_streams: (optional)
            A bool that specifies whether to redirect standard output and
            and standard error to a temporary file which is returned to the
            caller (True) or not (False).

  <Exceptions>
    None.

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

  <Returns>
    Newly created Metablock object containing a Link object

  """

    log.info("Running '{}'...".format(name))

    # If a key is passed, it has to match the format
    if key:
        securesystemslib.formats.KEY_SCHEMA.check_match(key)
        #FIXME: Add private key format check to securesystemslib formats
        if not key["keyval"].get("private"):
            raise securesystemslib.exceptions.FormatError(
                "Signing key needs to be a private key.")

    if material_list:
        log.info("Recording materials '{}'...".format(
            ", ".join(material_list)))
    materials_dict = record_artifacts_as_dict(material_list)

    if link_cmd_args:
        log.info("Running command '{}'...".format(" ".join(link_cmd_args)))
        byproducts = execute_link(link_cmd_args, record_streams)
    else:
        byproducts = {}

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

    log.info("Creating link metadata...")
    link = in_toto.models.link.Link(name=name,
                                    materials=materials_dict,
                                    products=products_dict,
                                    command=link_cmd_args,
                                    byproducts=byproducts,
                                    environment={"workdir": os.getcwd()})

    link_metadata = Metablock(signed=link)

    if key:
        log.info("Signing link metadata with key '{:.8}...'...".format(
            key["keyid"]))
        link_metadata.sign(key)

        filename = FILENAME_FORMAT.format(step_name=name, keyid=key["keyid"])
        log.info("Storing link metadata to '{}'...".format(filename))
        link_metadata.dump(filename)

    return link_metadata
示例#11
0
def main():
    # Load Alice's private key to later sign the layout
    key_alice = import_rsa_key_from_file("alice")
    # Fetch and load Bob's and Carl's public keys
    # to specify that they are authorized to perform certain step in the layout
    key_bob = import_rsa_key_from_file("../functionary_bob/bob.pub")
    key_carl = import_rsa_key_from_file("../functionary_carl/carl.pub")

    layout = Layout.read({
        "_type":
        "layout",
        "keys": {
            key_bob["keyid"]: key_bob,
            key_carl["keyid"]: key_carl,
        },
        "steps": [{
            "name":
            "clone",
            "expected_materials": [],
            "expected_products": [["CREATE", "demo-project/foo.py"],
                                  ["DISALLOW", "*"]],
            "pubkeys": [key_bob["keyid"]],
            "expected_command":
            "git clone https://github.com/in-toto/demo-project.git",
            "threshold":
            1,
        }, {
            "name":
            "update-version",
            "expected_materials":
            [["MATCH", "demo-project/*", "WITH", "PRODUCTS", "FROM", "clone"],
             ["DISALLOW", "*"]],
            "expected_products": [["ALLOW", "demo-project/foo.py"],
                                  ["DISALLOW", "*"]],
            "pubkeys": [key_bob["keyid"]],
            "expected_command":
            "",
            "threshold":
            1,
        }, {
            "name":
            "package",
            "expected_materials": [
                [
                    "MATCH", "demo-project/*", "WITH", "PRODUCTS", "FROM",
                    "update-version"
                ],
                ["DISALLOW", "*"],
            ],
            "expected_products": [
                ["CREATE", "demo-project.tar.gz"],
                ["DISALLOW", "*"],
            ],
            "pubkeys": [key_carl["keyid"]],
            "expected_command":
            "tar --exclude '.git' -zcvf demo-project.tar.gz demo-project",
            "threshold":
            1,
        }],
        "inspect": [{
            "name":
            "untar",
            "expected_materials": [
                [
                    "MATCH", "demo-project.tar.gz", "WITH", "PRODUCTS", "FROM",
                    "package"
                ],
                # FIXME: If the routine running inspections would gather the
                # materials/products to record from the rules we wouldn't have to
                # ALLOW other files that we aren't interested in.
                ["ALLOW", ".keep"],
                ["ALLOW", "alice.pub"],
                ["ALLOW", "root.layout"],
                ["DISALLOW", "*"]
            ],
            "expected_products": [
                [
                    "MATCH", "demo-project/foo.py", "WITH", "PRODUCTS", "FROM",
                    "update-version"
                ],
                # FIXME: See expected_materials above
                ["ALLOW", "demo-project/.git/*"],
                ["ALLOW", "demo-project.tar.gz"],
                ["ALLOW", ".keep"],
                ["ALLOW", "alice.pub"],
                ["ALLOW", "root.layout"],
                ["DISALLOW", "*"]
            ],
            "run":
            "tar xzf demo-project.tar.gz",
        }],
    })

    metadata = Metablock(signed=layout)

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

    if in_toto.settings.VERBOSE:
        log.info("--Begin printing layout metadata--")
        log.info(Layout.display(layout))
        log.info("--End printing layout metadata--")
示例#12
0
def verify_item_rules(source_name, source_type, rules, links):
    """
  <Purpose>
    Iteratively apply all passed material or product rules of one item (step or
    inspection) to enforce and authorize artifacts reported by the
    corresponding link and/or to guarantee that artifacts are linked together
    across links.
    In the beginning all artifacts are placed in a queue according to their
    type. If an artifact gets consumed by a rule it is removed from the queue,
    hence an artifact can only be consumed once.


  <Algorithm>
      1.  Create materials queue and products queue, and a generic artifacts
          queue based on the source_type (materials or products)

      2.  For each rule:
          1.  Apply rule on corresponding queue(s)
          2.  If rule verification passes, remove consumed items from the
              corresponding queue(s) and continue with next rule

  <Arguments>
    source_name:
            The name of the item (Step or Inspection) being verified
            (used for user logging).

    source_type:
            "materials" or "products" depending on whether the rules were in the
            "expected_materials" or "expected_products" field.

    rules:
            The list of rules (material or product rules) for the item
            being verified.

    links:
            A dictionary containing link metadata per step or inspection, e.g.:
            {
              <link name> : <Metablock containing a Link object>,
              ...
            }


  <Exceptions>
    FormatError
        if source_type is not "materials" or "products"
    RuleVerficationError
        if the artifacts queue is not empty after all rules were applied

  <Side Effects>
    None.

  """

    source_materials = links[source_name].signed.materials
    source_products = links[source_name].signed.products

    source_materials_queue = source_materials.keys()
    source_products_queue = source_products.keys()

    # Create generic source artifacts list and queue depending on the source type
    if source_type == "materials":
        source_artifacts = source_materials
        source_artifacts_queue = source_materials_queue

    elif source_type == "products":
        source_artifacts = source_products
        source_artifacts_queue = source_products_queue

    else:
        raise securesystemslib.exceptions.FormatError(
            "Argument 'source_type' of function 'verify_item_rules' has to be"
            " one of 'materials' or 'products.'\n"
            "Got:\n\t'{}'".format(source_type))

    # Apply (verify) all rule
    for rule in rules:

        log.info("Verifying '{}'...".format(" ".join(rule)))

        # Unpack rules for dispatching and rule format verification
        rule_data = in_toto.artifact_rules.unpack_rule(rule)
        rule_type = rule_data["type"]

        # MATCH, ALLOW, DISALLOW operate equally on either products or materials
        # depending on the source_type
        if rule_type == "match":
            source_artifacts_queue = verify_match_rule(rule,
                                                       source_artifacts_queue,
                                                       source_artifacts, links)

        elif rule_type == "allow":
            source_artifacts_queue = verify_allow_rule(rule,
                                                       source_artifacts_queue)

        elif rule_type == "disallow":
            verify_disallow_rule(rule, source_artifacts_queue)

        # CREATE, DELETE and MODIFY always operate either on products, on materials
        # or both, independently of the source_type ...
        elif rule_type == "create":
            source_products_queue = verify_create_rule(rule,
                                                       source_materials_queue,
                                                       source_products_queue)

            # The create rule only updates the products_queue, which in turn
            # only affects the generic artifacts queue if source_type is "products"
            if source_type == "products":
                source_artifacts_queue = source_products_queue

        elif rule_type == "delete":
            source_materials_queue = verify_delete_rule(
                rule, source_materials_queue, source_products_queue)

            # The delete rule only updates the materials_queue, which in turn
            # only affects the generic artifacts queue if source_type is "materials"
            if source_type == "materials":
                source_artifacts_queue = source_materials_queue

        elif rule_type == "modify":
            source_materials_queue, source_products_queue = verify_modify_rule(
                rule, source_materials_queue, source_products_queue,
                source_materials, source_products)

            # The modify rule updates materials_queue and products_queue. We have to
            # update the generic artifacts queue accordingly.
            if source_type == "materials":
                source_artifacts_queue = source_materials_queue
            elif source_type == "products":
                source_artifacts_queue = source_products_queue
示例#13
0
def in_toto_verify(layout, layout_key_dict):
    """
  <Purpose>
    Does entire in-toto supply chain verification of a final product
    by performing the following actions:

        1.  Verify layout signature(s)

        2.  Verify layout expiration

        3.  Load link metadata for every Step defined in the layout
            NOTE: link files are expected to have the corresponding step
            and the functionary, who carried out the step, encoded in their
            filename.

        4.  Verify functionary signature for every Link

        5.  Verify sublayouts
            NOTE: Replaces the layout object in the chain_link_dict with an
            unsigned summary link (the actual links of the sublayouts are
            verified). The summary link is used just like a regular link
            to verify command alignments, thresholds and inspections below.

        6.  Verify alignment of defined (Step) and reported (Link) commands
            NOTE: Won't raise exception on mismatch

        7.  Verify threshold constraints

        8.  Verify rules defined in each Step's expected_materials and
            expected_products field
            NOTE: At this point no Inspection link metadata is available,
            hence (MATCH) rules cannot reference materials or products of
            Inspections.
            Verifying Steps' artifact rules before executing Inspections
            guarantees that Inspection commands don't run on compromised
            target files, which would be a surface for attacks.

        9.  Execute Inspection commands
            NOTE: Inspections, similar to Steps executed with 'in-toto-run',
            will record materials before and products after command execution.
            For now it records everything in the current working directory.

        10. Verify rules defined in each Inspection's expected_materials and
            expected_products field

  <Arguments>
    layout:
            Layout object that is being verified.

    layout_key_dict:
            Dictionary of project owner public keys, used to verify the
            layout's signature.

  <Exceptions>
    None.

  <Side Effects>
    Read link metadata files from disk

  <Returns>
    A link which summarizes the materials and products of the overall
    software supply chain (used by super-layout verification if any)
  """

    log.info("Verifying layout signatures...")
    verify_layout_signatures(layout, layout_key_dict)

    # For the rest of the verification we only care about the layout payload
    # (Layout) that carries all the information and not about the layout
    # container (Metablock) that also carries the signatures
    layout = layout.signed

    log.info("Verifying layout expiration...")
    verify_layout_expiration(layout)

    log.info("Reading link metadata files...")
    chain_link_dict = load_links_for_layout(layout)

    log.info("Verifying link metadata signatures...")
    verify_all_steps_signatures(layout, chain_link_dict)

    log.info("Verifying sublayouts...")
    chain_link_dict = verify_sublayouts(layout, chain_link_dict)

    log.info("Verifying alignment of reported commands...")
    verify_all_steps_command_alignment(layout, chain_link_dict)

    log.info("Verifying threshold constraints...")
    verify_threshold_constraints(layout, chain_link_dict)
    reduced_chain_link_dict = reduce_chain_links(chain_link_dict)

    log.info("Verifying Step rules...")
    verify_all_item_rules(layout.steps, reduced_chain_link_dict)

    log.info("Executing Inspection commands...")
    inspection_link_dict = run_all_inspections(layout)

    log.info("Verifying Inspection rules...")
    # Artifact rules for inspections can reference links that correspond to
    # Steps or Inspections, hence the concatenation of both collections of links
    combined_links = reduced_chain_link_dict.copy()
    combined_links.update(inspection_link_dict)
    verify_all_item_rules(layout.inspect, combined_links)

    # We made it this far without exception that means, verification passed
    log.pass_verification("The software product passed all verification.")

    # Return a link file which summarizes the entire software supply chain
    # This is mostly relevant if the currently verified supply chain is embedded
    # in another supply chain
    return get_summary_link(layout, reduced_chain_link_dict)
示例#14
0
def verify_sublayouts(layout, chain_link_dict):
    """
  <Purpose>
    Checks if any step has been delegated by the functionary, recurses into
    the delegation and replaces the layout object in the chain_link_dict
    by an equivalent link object.

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

    chain_link_dict:
            A dictionary containing link metadata per functionary per step,
            e.g.:
            {
              <link name> : {
                <functionary key id> : <Metablock containing a Link or Layout
                                          object>,
                ...
              }, ...
            }

  <Exceptions>
    raises an Exception if verification of the delegated step fails.

  <Side Effects>
    None.

  <Returns>
    The passed dictionary containing link metadata per functionary per step,
    with layouts replaced with summary links.
    e.g.:
    {
      <link name> : {
        <functionary key id> : <Metablock containing a Link object>,
        ...
      }, ...
    }

  """

    for step_name, key_link_dict in six.iteritems(chain_link_dict):

        for keyid, link in six.iteritems(key_link_dict):

            if link._type == "layout":
                log.info("Verifying sublayout {}...".format(step_name))
                layout_key_dict = {}

                # Retrieve the entire key object for the keyid
                # corresponding to the link
                layout_key_dict = {keyid: layout.keys.get(keyid)}

                # Make a recursive call to in_toto_verify with the
                # layout and the extracted key object
                summary_link = in_toto_verify(link, layout_key_dict)

                # Replace the layout object in the passed chain_link_dict
                # with the link file returned by in-toto-verify
                key_link_dict[keyid] = summary_link

    return chain_link_dict
示例#15
0
def verify_threshold_constraints(layout, chain_link_dict):
    """
  <Purpose>
    Verifies that each step of a layout meets its signature threshold, i.e.:
    For each step there are at least `step.threshold` corresponding links,
    signed by different functionaries.

    Furthermore, verifies that all links corresponding to a given step report
    the same materials and products.

  <Arguments>
    layout:
            The layout whose step thresholds are being verified

    chain_link_dict:
            A dictionary containing link metadata per functionary per step,
            e.g.:
            {
              <link name> : {
                <functionary key id> : <Metablock containing a Link object>,
                ...
              }, ...
            }

  <Exceptions>
    raises an Exception if threshold is not verified.
    ThresholdVerificationError if the step is not performed by enough
    functionaries or if the materials and products for a step are not same
    for all functionaries.

  <Side Effects>
    None.

  """

    # We are only interested in links that are related to steps defined in the
    # Layout, so iterate over layout.steps
    for step in layout.steps:
        # Skip steps that don't require multiple functionaries
        if step.threshold <= 1:
            log.info("Skipping threshold verification for step '{0}' with"
                     " threshold '{1}'...".format(step.name, step.threshold))
            continue

        log.info("Verifying threshold for step '{0}' with"
                 " threshold '{1}'...".format(step.name, step.threshold))
        # Extract the key_link_dict for this step from the passed chain_link_dict
        key_link_dict = chain_link_dict[step.name]

        # Check if we have at least <threshold> links for this step
        if len(key_link_dict) < step.threshold:
            raise ThresholdVerificationError(
                "Step '{0}' not performed"
                " by enough functionaries!".format(step.name))

        # Take a reference link (e.g. the first in the step_link_dict)
        reference_keyid = key_link_dict.keys()[0]
        reference_link = key_link_dict[reference_key]

        # Iterate over all links to compare their properties with a reference_link
        for keyid, link in six.iteritems(key_link_dict):

            # compare their properties
            if (reference_link.signed.materials != link.signed.materials
                    or reference_link.signed.products != link.signed.products):
                raise ThresholdVerificationError(
                    "Links '{0}' and '{1}' have different"
                    " artifacts!".format(
                        in_toto.models.link.FILENAME_FORMAT.format(
                            step_name=step.name, keyid=reference_keyid),
                        in_toto.models.link.FILENAME_FORMAT.format(
                            step_name=step.name, keyid=keyid)))