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")
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)
def main(): key_owner = import_rsa_key_from_file("dependency_owner") key_developer = import_rsa_key_from_file("../developer/developer.pub") key_reviewer = import_rsa_key_from_file("../reviewer/reviewer.pub") layout = Layout.read({ "_type": "layout", "keys": { key_developer["keyid"]: key_developer, key_reviewer["keyid"]: key_reviewer, }, "steps": [{ "name": "dependency-develop", "expected_materials": [], "expected_products": [ ["CREATE", "../dependency/demo.py"], ["DISALLOW", "*"] ], "pubkeys": [key_developer["keyid"]], "expected_command": [], "threshold": 1, },{ "name": "dependency-code-review", "expected_materials": [ ["MATCH", "../dependency/demo.py", "WITH", "PRODUCTS", "FROM", "dependency-develop"], ["DISALLOW", "*"] ], "expected_products": [ ["ALLOW", "../dependency/demo.py"], ["DISALLOW", "*"] ], "pubkeys": [key_reviewer["keyid"]], "expected_command": [], "threshold": 1, }, ], "inspect": [], }) metadata = Metablock(signed=layout) # Sign and dump layout to "root.layout" metadata.sign(key_owner) metadata.dump("../metadata_dependency/root.layout")
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
def test_import_step_metadata_wrong_type(self): functionary_key = securesystemslib.keys.generate_rsa_key() name = "name" # Create and dump a link file with a wrong type link_name = in_toto.models.link.FILENAME_FORMAT.format( step_name=name, keyid=functionary_key["keyid"]) link_path = os.path.abspath(link_name) link = in_toto.models.link.Link(name=name) metadata = Metablock(signed=link) metadata.signed._type = "wrong-type" metadata.dump(link_path) # Add the single step to the test layout and try to read the failing link self.layout.steps.append( Step(name=name, pubkeys=[functionary_key["keyid"]])) with self.assertRaises(securesystemslib.exceptions.FormatError): in_toto.verifylib.load_links_for_layout(self.layout, ".") # Clean up os.remove(link_path)
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
def main(): # Load Niels's private key to later sign the layout key_niels = import_rsa_key_from_file("niels") # Fetch and load aimee's and noud's public keys # to specify that they are authorized to perform certain step in the layout key_aimee = import_rsa_key_from_file("../functionary_aimee/aimee.pub") key_noud = import_rsa_key_from_file("../functionary_noud/noud.pub") layout = Layout.read({ "_type": "layout", "keys": { key_aimee["keyid"]: key_aimee, key_noud["keyid"]: key_noud, }, "steps": [{ "name": "create", "expected_materials": [], "expected_products": [["CREATE", "app/Program.cs"], ["DISALLOW", "*"]], "pubkeys": [key_aimee["keyid"]], "expected_command": [ "dotnet", "new", "console", "-n", "app" ], "threshold": 1, },{ "name": "publish", "expected_materials": [ ["MATCH", "app/Program.cs", "WITH", "PRODUCTS", "FROM", "create"], ["DISALLOW", "*"], ], "expected_products": [["CREATE", "published/app"], ["CREATE", "published/app.*"], ["DISALLOW", "*"]], "pubkeys": [key_noud["keyid"]], "expected_command": [ "dotnet", "publish", "-o", "published", "app" ], "threshold": 1, },{ "name": "package", "expected_materials": [ ["MATCH", "published/*", "WITH", "PRODUCTS", "FROM", "publish"], ["DISALLOW", "*"], ], "expected_products": [ ["CREATE", "published.tar.gz"], ["DISALLOW", "*"], ], "pubkeys": [key_noud["keyid"]], "expected_command": [ "tar", "-zcvf", "published.tar.gz", "published", ], "threshold": 1, }], "inspect": [{ "name": "untar", "expected_materials": [ ["MATCH", "published.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", "niels.pub"], ["ALLOW", "root.layout"], ["DISALLOW", "*"] ], "expected_products": [ ["MATCH", "published/*", "WITH", "PRODUCTS", "FROM", "publish"], # FIXME: See expected_materials above ["ALLOW", "published/app.*"], ["ALLOW", "published.tar.gz"], ["ALLOW", ".keep"], ["ALLOW", "niels.pub"], ["ALLOW", "root.layout"], ["DISALLOW", "*"] ], "run": [ "tar", "xzf", "published.tar.gz", ] }], }) metadata = Metablock(signed=layout) # Sign and dump layout to "root.layout" metadata.sign(key_niels) metadata.dump("root.layout")
def in_toto_record_start(step_name, material_list, signing_key=None, gpg_keyid=None, gpg_use_default=False, gpg_home=None, exclude_patterns=None, base_path=None, record_environment=False, normalize_line_endings=False, lstrip_paths=None): """Generates preliminary link metadata. Records paths and hashes of materials in a preliminary link metadata file. The metadata is signed with the passed signing_key, a gpg key identified by its ID, or the default gpg key. If multiple key arguments are passed, only one key is used in above order of precedence. At least one key argument must be passed. The resulting link file is written to ``.STEP-NAME.KEYID-PREFIX.link-unfinished``. Use this function together with in_toto_record_stop as an alternative to in_toto_run, in order to provide evidence for supply chain steps that cannot be carried out by a single command. Arguments: step_name: A unique name to associate link metadata with a step. material_list: A list of artifact paths to be recorded as materials. Directories are traversed recursively. signing_key (optional): A key used to sign the resulting link metadata. The format is securesystemslib.formats.KEY_SCHEMA. gpg_keyid (optional): A keyid used to identify a local gpg key used to sign the resulting link metadata. gpg_use_default (optional): A boolean indicating if the default gpg key should be used to sign the resulting link metadata. gpg_home (optional): A path to the gpg home directory. If not set the default gpg home directory is used. exclude_patterns (optional): A list of filename patterns to exclude certain files from being recorded as artifacts. See Config docs for details. base_path (optional): A path relative to which artifacts are recorded. Default is the current working directory. record_environment (optional): A boolean indicating if information about the environment should be added in the resulting link metadata. normalize_line_endings (optional): A boolean indicating if line endings of artifacts should be normalized before hashing for cross-platform support. lstrip_paths (optional): A list of path prefixes used to left-strip artifact paths before storing them in the resulting link metadata. Raises: securesystemslib.exceptions.FormatError: Passed arguments are malformed. ValueError: None of signing_key, gpg_keyid or gpg_use_default=True is passed. securesystemslib.exceptions.StorageError: Cannot hash artifacts. PrefixError: Left-stripping artifact paths results in non-unique dict keys. securesystemslib.process.subprocess.TimeoutExpired: Link command times out. IOError, PermissionError: Cannot write link metadata. securesystemslib.exceptions.CryptoError, \ securesystemslib.exceptions.UnsupportedAlgorithmError: Signing errors. ValueError, OSError, securesystemslib.gpg.exceptions.CommandError, \ securesystemslib.gpg.exceptions.KeyNotFoundError: gpg signing errors. Side Effects: Reads artifact files from disk. Calls system gpg in a subprocess, if a gpg key argument is passed. Writes preliminary link metadata file to disk. """ LOG.info("Start recording '{}'...".format(step_name)) # Fail if there is no signing key arg at all if not signing_key and not gpg_keyid and not gpg_use_default: raise ValueError("Pass either a signing key, a gpg keyid or set" " gpg_use_default to True!") # Check key formats to fail early if signing_key: _check_match_signing_key(signing_key) if gpg_keyid: securesystemslib.formats.KEYID_SCHEMA.check_match(gpg_keyid) if exclude_patterns: securesystemslib.formats.NAMES_SCHEMA.check_match(exclude_patterns) if base_path: securesystemslib.formats.PATH_SCHEMA.check_match(base_path) if material_list: LOG.info("Recording materials '{}'...".format( ", ".join(material_list))) materials_dict = record_artifacts_as_dict( material_list, exclude_patterns=exclude_patterns, base_path=base_path, follow_symlink_dirs=True, normalize_line_endings=normalize_line_endings, lstrip_paths=lstrip_paths) LOG.info("Creating preliminary link metadata...") environment = {} if record_environment: environment['workdir'] = os.getcwd().replace('\\', '/') link = in_toto.models.link.Link(name=step_name, materials=materials_dict, products={}, command=[], byproducts={}, environment=environment) link_metadata = Metablock(signed=link) if signing_key: LOG.info("Signing link metadata using passed key...") signature = link_metadata.sign(signing_key) elif gpg_keyid: LOG.info("Signing link metadata using passed GPG keyid...") signature = link_metadata.sign_gpg(gpg_keyid, gpg_home=gpg_home) else: # (gpg_use_default) LOG.info("Signing link metadata using default GPG key ...") signature = link_metadata.sign_gpg(gpg_keyid=None, gpg_home=gpg_home) # We need the signature's keyid to write the link to keyid infix'ed filename signing_keyid = signature["keyid"] unfinished_fn = UNFINISHED_FILENAME_FORMAT.format(step_name=step_name, keyid=signing_keyid) LOG.info( "Storing preliminary link metadata to '{}'...".format(unfinished_fn)) link_metadata.dump(unfinished_fn)
def in_toto_run(name, material_list, product_list, link_cmd_args, record_streams=False, signing_key=None, gpg_keyid=None, gpg_use_default=False, gpg_home=None, exclude_patterns=None, base_path=None, compact_json=False, record_environment=False, normalize_line_endings=False, lstrip_paths=None, metadata_directory=None): """Performs a supply chain step or inspection generating link metadata. Executes link_cmd_args, recording paths and hashes of files before and after command execution (aka. artifacts) in a link metadata file. The metadata is signed with the passed signing_key, a gpg key identified by its ID, or the default gpg key. If multiple key arguments are passed, only one key is used in above order of precedence. The resulting link file is written to ``STEP-NAME.KEYID-PREFIX.link``. If no key argument is passed the link metadata is neither signed nor written to disk. Arguments: name: A unique name to associate link metadata with a step or inspection. material_list: A list of artifact paths to be recorded before command execution. Directories are traversed recursively. product_list: A list of artifact paths to be recorded after command execution. Directories are traversed recursively. link_cmd_args: A list where the first element is a command and the remaining elements are arguments passed to that command. record_streams (optional): A boolean indicating if standard output and standard error of the link command should be recorded in the link metadata in addition to being displayed while the command is executed. signing_key (optional): A key used to sign the resulting link metadata. The format is securesystemslib.formats.KEY_SCHEMA. gpg_keyid (optional): A keyid used to identify a local gpg key used to sign the resulting link metadata. gpg_use_default (optional): A boolean indicating if the default gpg key should be used to sign the resulting link metadata. gpg_home (optional): A path to the gpg home directory. If not set the default gpg home directory is used. exclude_patterns (optional): A list of filename patterns to exclude certain files from being recorded as artifacts. See Config docs for details. base_path (optional): A path relative to which artifacts are recorded. Default is the current working directory. compact_json (optional): A boolean indicating if the resulting link metadata should be written in the most compact JSON representation. record_environment (optional): A boolean indicating if information about the environment should be added in the resulting link metadata. normalize_line_endings (optional): A boolean indicating if line endings of artifacts should be normalized before hashing for cross-platform support. lstrip_paths (optional): A list of path prefixes used to left-strip artifact paths before storing them in the resulting link metadata. metadata_directory (optional): A directory path to write the resulting link metadata file to. Default destination is the current working directory. Raises: securesystemslib.exceptions.FormatError: Passed arguments are malformed. ValueError: Cannot change to base path directory. securesystemslib.exceptions.StorageError: Cannot hash artifacts. PrefixError: Left-stripping artifact paths results in non-unique dict keys. securesystemslib.process.subprocess.TimeoutExpired: Link command times out. IOError, FileNotFoundError, NotADirectoryError, PermissionError: Cannot write link metadata. securesystemslib.exceptions.CryptoError, \ securesystemslib.exceptions.UnsupportedAlgorithmError: Signing errors. ValueError, OSError, securesystemslib.gpg.exceptions.CommandError, \ securesystemslib.gpg.exceptions.KeyNotFoundError: gpg signing errors. Side Effects: Reads artifact files from disk. Runs link command in subprocess. Calls system gpg in a subprocess, if a gpg key argument is passed. Writes link metadata file to disk, if any key argument is passed. Returns: A Metablock object that contains the resulting link object. """ LOG.info("Running '{}'...".format(name)) # Check key formats to fail early if signing_key: _check_match_signing_key(signing_key) if gpg_keyid: securesystemslib.formats.KEYID_SCHEMA.check_match(gpg_keyid) if exclude_patterns: securesystemslib.formats.NAMES_SCHEMA.check_match(exclude_patterns) if base_path: securesystemslib.formats.PATH_SCHEMA.check_match(base_path) if metadata_directory: securesystemslib.formats.PATH_SCHEMA.check_match(metadata_directory) if material_list: LOG.info("Recording materials '{}'...".format( ", ".join(material_list))) materials_dict = record_artifacts_as_dict( material_list, exclude_patterns=exclude_patterns, base_path=base_path, follow_symlink_dirs=True, normalize_line_endings=normalize_line_endings, lstrip_paths=lstrip_paths) 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: securesystemslib.formats.PATHS_SCHEMA.check_match(product_list) LOG.info("Recording products '{}'...".format(", ".join(product_list))) products_dict = record_artifacts_as_dict( product_list, exclude_patterns=exclude_patterns, base_path=base_path, follow_symlink_dirs=True, normalize_line_endings=normalize_line_endings, lstrip_paths=lstrip_paths) LOG.info("Creating link metadata...") environment = {} if record_environment: environment['workdir'] = os.getcwd().replace('\\', '/') link = in_toto.models.link.Link(name=name, materials=materials_dict, products=products_dict, command=link_cmd_args, byproducts=byproducts, environment=environment) link_metadata = Metablock(signed=link, compact_json=compact_json) signature = None if signing_key: LOG.info("Signing link metadata using passed key...") signature = link_metadata.sign(signing_key) elif gpg_keyid: LOG.info("Signing link metadata using passed GPG keyid...") signature = link_metadata.sign_gpg(gpg_keyid, gpg_home=gpg_home) elif gpg_use_default: LOG.info("Signing link metadata using default GPG key ...") signature = link_metadata.sign_gpg(gpg_keyid=None, gpg_home=gpg_home) # We need the signature's keyid to write the link to keyid infix'ed filename if signature: signing_keyid = signature["keyid"] filename = FILENAME_FORMAT.format(step_name=name, keyid=signing_keyid) if metadata_directory is not None: filename = os.path.join(metadata_directory, filename) LOG.info("Storing link metadata to '{}'...".format(filename)) link_metadata.dump(filename) return link_metadata
#!/usr/bin/python from in_toto.models.layout import Layout, Step from in_toto.models.metadata import Metablock from in_toto.util import generate_and_write_rsa_keypair, import_rsa_key_from_file generate_and_write_rsa_keypair("build_key") build_key = import_rsa_key_from_file("build_key.pub") layout = Layout() build = Step(name="build") build.expected_materials.append(['ALLOW', 'package.json']) build.expected_materials.append(['ALLOW', 'index.js']) build.expected_command = ['npm', 'install'] layout.steps.append(build) layout.add_functionary_key(build_key) build.pubkeys.append(build_key['keyid']) generate_and_write_rsa_keypair("root_key") root_key = import_rsa_key_from_file("root_key") metablock = Metablock(signed=layout) metablock.sign(root_key) metablock.dump("root.layout")
def main(): # Load Alice's private key to later sign the layout key_alice = interface.import_rsa_privatekey_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 = interface.import_rsa_publickey_from_file( "../functionary_bob/bob.pub") key_carl = interface.import_rsa_publickey_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") print('Created demo in-toto layout as "root.layout".')
def in_toto_record_start(step_name, material_list, signing_key=None, gpg_keyid=None, gpg_use_default=False, gpg_home=None, exclude_patterns=None, base_path=None, record_environment=False, normalize_line_endings=False): """ <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 signing_key, gpg key identified by the passed gpg_keyid or the default gpg key and stores it to disk under UNFINISHED_FILENAME_FORMAT. One of signing_key, gpg_keyid or gpg_use_default has to be passed. <Arguments> step_name: A unique name to relate link metadata with a step defined in the layout. material_list: List of file or directory paths that should be recorded as materials. signing_key: (optional) If not None, link metadata is signed with this key. Format is securesystemslib.formats.KEY_SCHEMA gpg_keyid: (optional) If not None, link metadata is signed with a gpg key identified by the passed keyid. gpg_use_default: (optional) If True, link metadata is signed with default gpg key. gpg_home: (optional) Path to GPG keyring (if not set the default keyring is used). exclude_patterns: (optional) Artifacts matched by the pattern are excluded from the materials section in the resulting preliminary link. base_path: (optional) If passed, record materials relative to base_path. Default is current working directory. NOTE: The base_path part of the recorded materials is not included in the resulting preliminary link's material section. record_environment: (optional) if values such as workdir should be recorded on the environment dictionary (false by default) normalize_line_endings: (optional) If True, replaces windows and mac line endings with unix line endings before hashing materials, for cross-platform support. <Exceptions> ValueError if none of signing_key, gpg_keyid or gpg_use_default=True is passed. securesystemslib.FormatError if a signing_key is passed and does not match securesystemslib.formats.KEY_SCHEMA or a gpg_keyid is passed and does not match securesystemslib.formats.KEYID_SCHEMA or exclude_patterns are passed and don't match securesystemslib.formats.NAMES_SCHEMA, or base_path is passed and does not match securesystemslib.formats.PATH_SCHEMA or is not a directory. <Side Effects> Writes newly created link metadata file to disk using the filename scheme from link.UNFINISHED_FILENAME_FORMAT <Returns> None. """ log.info("Start recording '{}'...".format(step_name)) # Fail if there is no signing key arg at all if not signing_key and not gpg_keyid and not gpg_use_default: raise ValueError("Pass either a signing key, a gpg keyid or set" " gpg_use_default to True!") # Check key formats to fail early if signing_key: _check_match_signing_key(signing_key) if gpg_keyid: securesystemslib.formats.KEYID_SCHEMA.check_match(gpg_keyid) if exclude_patterns: securesystemslib.formats.NAMES_SCHEMA.check_match(exclude_patterns) if base_path: securesystemslib.formats.PATH_SCHEMA.check_match(base_path) if material_list: log.info("Recording materials '{}'...".format( ", ".join(material_list))) materials_dict = record_artifacts_as_dict( material_list, exclude_patterns=exclude_patterns, base_path=base_path, follow_symlink_dirs=True, normalize_line_endings=normalize_line_endings) log.info("Creating preliminary link metadata...") environment = {} if record_environment: environment['workdir'] = os.getcwd().replace('\\', '/') link = in_toto.models.link.Link(name=step_name, materials=materials_dict, products={}, command=[], byproducts={}, environment=environment) link_metadata = Metablock(signed=link) if signing_key: log.info("Signing link metadata using passed key...") signature = link_metadata.sign(signing_key) elif gpg_keyid: log.info("Signing link metadata using passed GPG keyid...") signature = link_metadata.sign_gpg(gpg_keyid, gpg_home=gpg_home) else: # (gpg_use_default) log.info("Signing link metadata using default GPG key ...") signature = link_metadata.sign_gpg(gpg_keyid=None, gpg_home=gpg_home) # We need the signature's keyid to write the link to keyid infix'ed filename signing_keyid = signature["keyid"] unfinished_fn = UNFINISHED_FILENAME_FORMAT.format(step_name=step_name, keyid=signing_keyid) log.info( "Storing preliminary link metadata to '{}'...".format(unfinished_fn)) link_metadata.dump(unfinished_fn)
def in_toto_run(name, material_list, product_list, link_cmd_args, record_streams=False, signing_key=None, gpg_keyid=None, gpg_use_default=False, gpg_home=None, exclude_patterns=None, base_path=None, compact_json=False, record_environment=False, normalize_line_endings=False): """ <Purpose> Calls functions in this module to run the command passed as link_cmd_args argument and to store materials, products, by-products and environment information into a link metadata file. The link metadata file is signed either with the passed signing_key, or a gpg key identified by the passed gpg_keyid or with the default gpg key if gpg_use_default is True. Even if multiple key parameters are passed, only one key is used for signing (in above order of precedence). The link file is dumped to `link.FILENAME_FORMAT` using the signing key's keyid. If no key parameter is passed the link is neither signed nor dumped. <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. 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). signing_key: (optional) If not None, link metadata is signed with this key. Format is securesystemslib.formats.KEY_SCHEMA gpg_keyid: (optional) If not None, link metadata is signed with a gpg key identified by the passed keyid. gpg_use_default: (optional) If True, link metadata is signed with default gpg key. gpg_home: (optional) Path to GPG keyring (if not set the default keyring is used). exclude_patterns: (optional) Artifacts matched by the pattern are excluded from the materials and products sections in the resulting link. base_path: (optional) If passed, record artifacts relative to base_path. Default is current working directory. NOTE: The base_path part of the recorded material is not included in the resulting preliminary link's material/product sections. compact_json: (optional) Whether or not to use the most compact json representation. record_environment: (optional) if values such as workdir should be recorded on the environment dictionary (false by default) normalize_line_endings: (optional) If True, replaces windows and mac line endings with unix line endings before hashing materials and products, for cross-platform support. <Exceptions> securesystemslib.FormatError if a signing_key is passed and does not match securesystemslib.formats.KEY_SCHEMA or a gpg_keyid is passed and does not match securesystemslib.formats.KEYID_SCHEMA or exclude_patterns are passed and don't match securesystemslib.formats.NAMES_SCHEMA, or base_path is passed and does not match securesystemslib.formats.PATH_SCHEMA or is not a directory. <Side Effects> If a key parameter is passed for signing, the newly created link metadata file is written to disk using the filename scheme: `link.FILENAME_FORMAT` <Returns> Newly created Metablock object containing a Link object """ log.info("Running '{}'...".format(name)) # Check key formats to fail early if signing_key: _check_match_signing_key(signing_key) if gpg_keyid: securesystemslib.formats.KEYID_SCHEMA.check_match(gpg_keyid) if exclude_patterns: securesystemslib.formats.NAMES_SCHEMA.check_match(exclude_patterns) if base_path: securesystemslib.formats.PATH_SCHEMA.check_match(base_path) if material_list: log.info("Recording materials '{}'...".format( ", ".join(material_list))) materials_dict = record_artifacts_as_dict( material_list, exclude_patterns=exclude_patterns, base_path=base_path, follow_symlink_dirs=True, normalize_line_endings=normalize_line_endings) 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: securesystemslib.formats.PATHS_SCHEMA.check_match(product_list) log.info("Recording products '{}'...".format(", ".join(product_list))) products_dict = record_artifacts_as_dict( product_list, exclude_patterns=exclude_patterns, base_path=base_path, follow_symlink_dirs=True, normalize_line_endings=normalize_line_endings) log.info("Creating link metadata...") environment = {} if record_environment: environment['workdir'] = os.getcwd().replace('\\', '/') link = in_toto.models.link.Link(name=name, materials=materials_dict, products=products_dict, command=link_cmd_args, byproducts=byproducts, environment=environment) link_metadata = Metablock(signed=link, compact_json=compact_json) signature = None if signing_key: log.info("Signing link metadata using passed key...") signature = link_metadata.sign(signing_key) elif gpg_keyid: log.info("Signing link metadata using passed GPG keyid...") signature = link_metadata.sign_gpg(gpg_keyid, gpg_home=gpg_home) elif gpg_use_default: log.info("Signing link metadata using default GPG key ...") signature = link_metadata.sign_gpg(gpg_keyid=None, gpg_home=gpg_home) # We need the signature's keyid to write the link to keyid infix'ed filename if signature: signing_keyid = signature["keyid"] filename = FILENAME_FORMAT.format(step_name=name, keyid=signing_keyid) log.info("Storing link metadata to '{}'...".format(filename)) link_metadata.dump(filename) return link_metadata
def main(): # Load Jerry's private key to later sign the layout key_jerry = interface.import_rsa_privatekey_from_file("jerry") # Fetch and load Bob's and Alice's public keys # to specify that they are authorized to perform certain step in the layout key_alice = interface.import_rsa_publickey_from_file( "../functionary_alice/alice.pub") key_bob = interface.import_rsa_publickey_from_file( "../functionary_bob/bob.pub") layout = Layout.read({ "_type": "layout", "keys": { key_bob["keyid"]: key_bob, key_alice["keyid"]: key_alice, }, "steps": [{ "name": "clone", "expected_materials": [], "expected_products": [["CREATE", "inclavare-containers/rbi/kernel/Dockerfile"], [ "CREATE", "inclavare-containers/rbi/kernel/build-docker-image.sh" ], ["CREATE", "inclavare-containers/rbi/kernel/build-kernel.sh"], ["CREATE", "inclavare-containers/rbi/kernel/check-integrity.sh"], [ "CREATE", "inclavare-containers/rbi/kernel/patch/build-kernel.sh" ], ["CREATE", "inclavare-containers/rbi/kernel/scripts/start.sh"], ["CREATE", "inclavare-containers/rbi/misc/check-integrity.sh"]], "pubkeys": [key_alice["keyid"]], "expected_command": [ "git", "clone", "https://github.com/alibaba/inclavare-containers.git" ], "threshold": 1, }, { "name": "build", "expected_materials": [[ "MATCH", "inclavare-containers/rbi/kernel/Dockerfile", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kernel/build-docker-image.sh", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kernel/build-kernel.sh", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kernel/check-integrity.sh", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kernel/patch/build-kernel.sh", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kernel/readme.md", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kernel/scripts/start.sh", "WITH", "PRODUCTS", "FROM", "clone" ]], "expected_products": [ ["CREATE", "inclavare-containers/rbi/result/kernel/vmlinux"], ], "pubkeys": [key_bob["keyid"]], "expected_command": [ "bash", "inclavare-containers/rbi/rbi.sh", "kernel", ], "threshold": 1, }], "inspect": [{ "name": "integrity", "expected_materials": [ [ "MATCH", "inclavare-containers/rbi/result/kernel/vmlinux", "WITH", "PRODUCTS", "FROM", "build" ], [ "MATCH", "inclavare-containers/rbi/misc/check-integrity.sh", "WITH", "PRODUCTS", "FROM", "clone" ], ["ALLOW", "jerry.pub"], ["ALLOW", "root.layout"], ["ALLOW", ".keep"] ], "expected_products": [ [ "CREATE", "inclavare-containers/rbi/result/kernel/.check_done" ], # FIXME: See expected_materials above ["ALLOW", "jerry.pub"], ["ALLOW", "root.layout"], ["ALLOW", ".keep"] ], "run": [ "bash", "inclavare-containers/rbi/misc/check-integrity.sh", "inclavare-containers/rbi/result/kernel/vmlinux", SHA256_VALUE ] }], }) metadata = Metablock(signed=layout) # Sign and dump layout to "root.layout" metadata.sign(key_jerry) metadata.dump("root.layout")
def main(): # Load Jerry's private key to later sign the layout key_jerry = interface.import_rsa_privatekey_from_file("jerry") # Fetch and load Bob's and Alice's public keys # to specify that they are authorized to perform certain step in the layout key_alice = interface.import_rsa_publickey_from_file( "../functionary_alice/alice.pub") key_bob = interface.import_rsa_publickey_from_file( "../functionary_bob/bob.pub") layout = Layout.read({ "_type": "layout", "keys": { key_bob["keyid"]: key_bob, key_alice["keyid"]: key_alice, }, "steps": [{ "name": "clone", "expected_materials": [], "expected_products": [["CREATE", "inclavare-containers/rbi/kata-agent/Dockerfile"], [ "CREATE", "inclavare-containers/rbi/kata-agent/kata-build-docker.sh" ], [ "CREATE", "inclavare-containers/rbi/kata-agent/kata-source-code.sh" ], ["CREATE", "inclavare-containers/rbi/kata-agent/kata-test.sh"], [ "CREATE", "inclavare-containers/rbi/kata-agent/patch/Cargo.lock" ], ["CREATE", "inclavare-containers/rbi/kata-agent/patch/Makefile"], ["CREATE", "inclavare-containers/rbi/kata-agent/patch/protocols"], [ "CREATE", "inclavare-containers/rbi/kata-agent/patch/protocols/Cargo.toml" ], ["CREATE", "inclavare-containers/rbi/kata-agent/readme.md"], ["CREATE", "inclavare-containers/rbi/kata-agent/readme_cn.md"], [ "CREATE", "inclavare-containers/rbi/kata-agent/scripts/start.sh" ], ["CREATE", "inclavare-containers/rbi/misc/check-integrity.sh"]], "pubkeys": [key_alice["keyid"]], "expected_command": [ "git", "clone", "https://github.com/alibaba/inclavare-containers.git" ], "threshold": 1, }, { "name": "build", "expected_materials": [ [ "MATCH", "inclavare-containers/rbi/kata-agent/Dockerfile", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/kata-build-docker.sh", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/kata-source-code.sh", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/kata-test.sh", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/patch/Cargo.lock", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/patch/Makefile", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/patch/protocols", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/patch/protocols/Cargo.toml", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/readme.md", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/readme_cn.md", "WITH", "PRODUCTS", "FROM", "clone" ], [ "MATCH", "inclavare-containers/rbi/kata-agent/scripts/start.sh", "WITH", "PRODUCTS", "FROM", "clone" ] ], "expected_products": [ [ "CREATE", "inclavare-containers/rbi/result/kata-agent/kata-agent" ], ], "pubkeys": [key_bob["keyid"]], "expected_command": [ "bash", "inclavare-containers/rbi/rbi.sh", "agent", ], "threshold": 1, }], "inspect": [{ "name": "integrity", "expected_materials": [ [ "MATCH", "inclavare-containers/rbi/result/kata-agent/kata-agent", "WITH", "PRODUCTS", "FROM", "build" ], [ "MATCH", "inclavare-containers/rbi/misc/check-integrity.sh", "WITH", "PRODUCTS", "FROM", "clone" ], # 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", "jerry.pub"], ["ALLOW", "root.layout"], ["ALLOW", ".keep"] ], "expected_products": [ [ "CREATE", "inclavare-containers/rbi/result/kata-agent/.check_done" ], # FIXME: See expected_materials above ["ALLOW", "jerry.pub"], ["ALLOW", "root.layout"], ["ALLOW", ".keep"] ], "run": [ "bash", "inclavare-containers/rbi/misc/check-integrity.sh", "inclavare-containers/rbi/result/kata-agent/kata-agent", SHA256_VALUE ] }], }) metadata = Metablock(signed=layout) # Sign and dump layout to "root.layout" metadata.sign(key_jerry) metadata.dump("root.layout")
def main(): key_owner = import_rsa_key_from_file("target_owner") key_dependency_owner = import_rsa_key_from_file("dependency_owner.pub") key_developer = import_rsa_key_from_file("../developer/developer.pub") key_reviewer = import_rsa_key_from_file("../reviewer/reviewer.pub") key_xray = import_rsa_key_from_file("../xray/xray.pub") layout = Layout.read({ "_type": "layout", "keys": { key_developer["keyid"]: key_developer, key_reviewer["keyid"]: key_reviewer, key_xray["keyid"]: key_xray, key_dependency_owner["keyid"]: key_dependency_owner, }, "steps": [ { "name": "target-develop", "expected_materials": [], "expected_products": [["CREATE", "../target/demo.py"], ["DISALLOW", "*"]], "pubkeys": [key_developer["keyid"]], "expected_command": [], "threshold": 1, }, { "name": "target-code-review", "expected_materials": [[ "MATCH", "../target/demo.py", "WITH", "PRODUCTS", "FROM", "target-develop" ], ["DISALLOW", "*"]], "expected_products": [["ALLOW", "../target/demo.py"], ["DISALLOW", "*"]], "pubkeys": [key_reviewer["keyid"]], "expected_command": [], "threshold": 1, }, { "name": "target-get-dependency", "expected_materials": [], "expected_products": [ ["ALLOW", "../dependency/demo.py"], ["DISALLOW", "*"], ], "pubkeys": [key_dependency_owner["keyid"]], "expected_command": [], "threshold": 1, }, { "name": "target-jfrog-xray", "expected_materials": [ [ "MATCH", "../target/demo.py", "WITH", "PRODUCTS", "FROM", "target-develop" ], ["DISALLOW", "*"], ], "expected_products": [ ["CREATE", "../reports/jfrog-xray-report.json"], ["DISALLOW", "*"], ], "pubkeys": [key_xray["keyid"]], "expected_command": ["python", "generate_report.py"], "threshold": 1, }, ], "inspect": [{ "name": "target-check-vulnerability-report", "expected_materials": [ [ "MATCH", "../reports/jfrog-xray-report.json", "WITH", "PRODUCTS", "FROM", "target-jfrog-xray" ], ], "expected_products": [], "run": ["python", "scripts/validate_jfrog_xray_report.py"], }], }) metadata = Metablock(signed=layout) # Sign and dump layout to "root.layout" metadata.sign(key_owner) metadata.dump("../metadata_target/root.layout")