def test_get_inspection_by_name(self): """Test getting inspection by name. """ names = ["a", "b", "c"] layout = Layout(inspect=[Inspection(name=name) for name in names]) self.assertEqual(layout.get_inspection_by_name("b").name, "b") with self.assertRaises(securesystemslib.exceptions.FormatError): layout.get_inspection_by_name(1)
def test_get_step_by_name(self): """Test getting step by name. """ names = ["a", "b", "c"] layout = Layout(steps=[Step(name=name) for name in names]) self.assertEqual(layout.get_step_by_name("b").name, "b") with self.assertRaises(securesystemslib.exceptions.FormatError): layout.get_step_by_name(None)
def setUp(self): self.layout = Layout.read({ "_type": "layout", "inspect": [{ "name": "do-the-thing", "expected_materials": [ ["MATCH", "{SOURCE_THING}", "WITH", "MATERIALS", "FROM", "{SOURCE_STEP}"] ], "expected_products": [ ["CREATE", "{NEW_THING}"] ] }], "steps": [{ "name": "artifacts", "expected_command": [], "expected_materials": [ ["MATCH", "{SOURCE_THING}", "WITH", "MATERIALS", "FROM", "{SOURCE_STEP}"] ], "expected_products": [ ["CREATE", "{NEW_THING}"] ] }] })
def setUpClass(self): # Create layout with one inspection self.layout = Layout.read({ "_type": "layout", "inspect": [], "steps": [{ "name": "run-command", "expected_command": ["{EDITOR}"], }] }) super(Test_SubstituteOnVerify, self).setUpClass() # Find demo files demo_files = os.path.join(os.path.dirname(os.path.realpath(__file__)), "demo_files") # Copy demo files to temp dir for file in os.listdir(demo_files): shutil.copy(os.path.join(demo_files, file), self.test_dir) # load alice's key self.alice = import_rsa_key_from_file("alice") self.alice_pub_dict = import_public_keys_from_files_as_dict( ["alice.pub"])
def setUpClass(self): # Create layout with one inspection self.layout = Layout.read({ "_type": "layout", "inspect": [], "steps": [{ "name": "run-command", "expected_command": ["{EDITOR}"], }] }) # 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) # load alice's key self.alice = import_rsa_key_from_file("alice") self.alice_pub_dict = import_public_keys_from_files_as_dict( ["alice.pub"])
def load(path): """Loads the JSON string representation of in-toto metadata from disk. Arguments: path: The path to read the file from. Raises: IOError: The file cannot be written. securesystemslib.exceptions.FormatError: Metadata format is invalid. Returns: A Metablock object whose signable attribute is either a Link or a Layout object. """ with open(path, "r") as fp: data = json.load(fp) signatures = data.get("signatures", []) signed_data = data.get("signed", {}) signed_type = signed_data.get("_type") if signed_type == "link": signed = Link.read(signed_data) elif signed_type == "layout": signed = Layout.read(signed_data) else: raise securesystemslib.exceptions.FormatError( "Invalid Metadata format") return Metablock(signatures=signatures, signed=signed)
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 test_validate_signatures(self): """Test validate Metablock's 'signatures' property. """ # An empty signature list is okay metablock = Metablock(signed=Layout()) metablock._validate_signatures() # Fail with signatures property not a list metablock.signatures = "not-a-signatures-list" with self.assertRaises(FormatError): metablock._validate_signatures() # Fail with invalid signature metablock.signatures = [] metablock.signatures.append("not-a-signature") with self.assertRaises(FormatError): metablock._validate_signatures() # Load signed demo link demo_link_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), "..", "demo_files", "write-code.776a00e2.link") metablock = Metablock.load(demo_link_path) # Verify that there is a signature and that it is valid self.assertTrue(len(metablock.signatures) > 0) metablock._validate_signatures()
def setUp(self): # Create layout with one inspection self.layout = Layout.read({ "_type": "layout", "inspect": [], "steps": [{ "name": "run-command", "expected_command": ["{EDITOR}"], }]})
def test_inspection_fail_with_non_zero_retval(self): """Test fail run inspections with non-zero return value. """ layout = Layout.read({ "_type": "layout", "steps": [], "inspect": [{ "name": "non-zero-inspection", "run": "expr 1 / 0", }] }) with self.assertRaises(BadReturnValueError): run_all_inspections(layout)
def test_validate_signed(self): """Test validate Metablock's 'signed' property. """ # Valid Layout Metablock metablock = Metablock(signed=Layout()) metablock._validate_signed() # Valid Link Metablock Metablock(signed=Link()) metablock._validate_signed() # Fail instantiation with empty or invalid signed property # Metablock is validated on instantiation with self.assertRaises(FormatError): Metablock() with self.assertRaises(FormatError): Metablock(signed="not-a-layout-or-link") # Fail with invalid signed property metablock = Metablock(signed=Layout()) metablock.signed._type = "bogus type" with self.assertRaises(FormatError): metablock._validate_signed()
def test_remove_inspection_by_name(self): """Test removing inspection by name. """ names = ["a", "b", "c"] layout = Layout(inspect=[Inspection(name=name) for name in names]) self.assertEqual(len(layout.inspect), 3) self.assertTrue("b" in layout.get_inspection_name_list()) # Items are only removed if they exist for _ in range(2): layout.remove_inspection_by_name("b") self.assertEqual(len(layout.inspect), 2) self.assertTrue("b" not in layout.get_inspection_name_list()) with self.assertRaises(securesystemslib.exceptions.FormatError): layout.remove_inspection_by_name(False)
def test_remove_step_by_name(self): """Test removing step by name. """ names = ["a", "b", "c"] layout = Layout(steps=[Step(name=name) for name in names]) self.assertEqual(len(layout.steps), 3) self.assertTrue("b" in layout.get_step_name_list()) # Items are only removed if they exist for _ in range(2): layout.remove_step_by_name("b") self.assertEqual(len(layout.steps), 2) self.assertTrue("b" not in layout.get_step_name_list()) with self.assertRaises(securesystemslib.exceptions.FormatError): layout.get_step_by_name([])
def setUp(self): """ Create layout with dummy inspection """ # Create layout with one inspection self.layout = Layout.read({ "_type": "layout", "steps": [], "inspect": [{ "name": "run-command", "run": ["{COMMAND}"], }] })
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 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) # Import sub layout signing (private) and verifying (public) keys alice = import_rsa_key_from_file("alice") alice_pub = import_rsa_key_from_file("alice.pub") # Copy, sign and dump sub layout as link from template layout_template = Metablock.load("demo.layout.template") sub_layout = copy.deepcopy(layout_template) sub_layout_name = "sub_layout" sub_layout_path = FILENAME_FORMAT.format(step_name=sub_layout_name, keyid=alice_pub["keyid"]) sub_layout.sign(alice) sub_layout.dump(sub_layout_path) # Create super layout that has only one step, the sublayout self.super_layout = Layout() self.super_layout.keys[alice_pub["keyid"]] = alice_pub sub_layout_step = Step(name=sub_layout_name, pubkeys=[alice_pub["keyid"]]) self.super_layout.steps.append(sub_layout_step) # Load the super layout links (i.e. the sublayout) self.super_layout_links = load_links_for_layout(self.super_layout)
def setUpClass(self): """ Create layout with dummy inpsection. Create and change into temp test directory with dummy artifact.""" # Create layout with one inspection self.layout = Layout.read({ "_type": "layout", "steps": [], "inspect": [{ "name": "touch-bar", "run": "touch bar", }] }) # Create directory where the verification will take place self.working_dir = os.getcwd() self.test_dir = os.path.realpath(tempfile.mkdtemp()) os.chdir(self.test_dir) open("foo", "w").write("foo")
def load(path): """ <Purpose> Loads the JSON string representation of signed metadata from disk and creates a Metablock object. The `signed` attribute of the Metablock object is assigned a Link or Layout object, depending on the `_type` field in the loaded metadata file. <Arguments> path: The path to write the file to. <Side Effects> Reading metadata file from disk <Returns> None. """ with open(path, "r") as fp: data = json.load(fp) signatures = data.get("signatures", []) signed_data = data.get("signed", {}) signed_type = signed_data.get("_type") if signed_type == "link": signed = Link.read(signed_data) elif signed_type == "layout": signed = Layout.read(signed_data) else: raise securesystemslib.exceptions.FormatError( "Invalid Metadata format") return Metablock(signatures=signatures, signed=signed)
#!/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")
#!/usr/bin/env python from in_toto.models.layout import Layout, Step, Inspection import in_toto.util import json import datetime if __name__: # Get keys santiago_key = in_toto.util.import_rsa_key_from_file("santiago.pub") justin_key = in_toto.util.import_rsa_key_from_file("justin") with open("root.layout") as fp: info = json.load(fp) l = Layout.read(info) l.sign(justin_key) l.dump()
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 test_get_step_name_list(self): """Test getting list of step names. """ names = ["a", "b", "c"] layout = Layout(steps=[Step(name=name) for name in names]) self.assertListEqual(layout.get_step_name_list(), names)
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 setUp(self): """Populate a base layout that we can use.""" self.layout = Layout() self.layout.expires = '2016-11-18T16:44:55Z'
def test_set_relative_expiration(self): """Test adding expiration date relative from today. """ layout = Layout() with self.assertRaises(securesystemslib.exceptions.FormatError): layout.set_relative_expiration(days=None, months=0, years=0) with self.assertRaises(securesystemslib.exceptions.FormatError): layout.set_relative_expiration(days=0, months="", years=0) with self.assertRaises(securesystemslib.exceptions.FormatError): layout.set_relative_expiration(days=0, months=0, years=[]) layout.set_relative_expiration(days=1) layout._validate_expires() layout.set_relative_expiration(months=2) layout._validate_expires() layout.set_relative_expiration(years=3) layout._validate_expires() layout.set_relative_expiration(days=3, months=2, years=1) layout._validate_expires() # It's possible to add an expiration date in the past layout.set_relative_expiration(days=-3, months=-2, years=-1) layout._validate_expires()
class TestLayoutValidator(unittest.TestCase): """Test in_toto.models.layout.Layout validators. """ def setUp(self): """Populate a base layout that we can use.""" self.layout = Layout() self.layout.expires = '2016-11-18T16:44:55Z' def test_wrong_type(self): """Test that the type field is validated properly.""" with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout._type = "wrong" self.layout._validate_type() self.layout.validate() self.layout._type = "layout" self.layout._validate_type() def test_validate_readme_field(self): """Tests the readme field data type validator. """ self.layout.readme = 1 with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout._validate_readme() self.layout.readme = "This is a test supply chain" self.layout._validate_readme() def test_wrong_expires(self): """Test the expires field is properly populated.""" self.layout.expires = '' with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout._validate_expires() with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() self.layout.expires = '-1' with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout._validate_expires() with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() # notice the wrong month self.layout.expires = '2016-13-18T16:44:55Z' with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout._validate_expires() with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() self.layout.expires = '2016-11-18T16:44:55Z' self.layout._validate_expires() self.layout.validate() def test_wrong_key_dictionary(self): """Test that the keys dictionary is properly populated.""" rsa_key_one = securesystemslib.keys.generate_rsa_key() rsa_key_two = securesystemslib.keys.generate_rsa_key() # FIXME: attr.ib reutilizes the default dictionary, so future constructor # are not empty... self.layout.keys = {"kek": rsa_key_one} with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout._validate_keys() with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() self.layout.keys = {} self.layout.keys[rsa_key_two['keyid']] = "kek" with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout._validate_keys() with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() self.layout.keys = {} del rsa_key_one["keyval"]["private"] del rsa_key_two["keyval"]["private"] self.layout.keys[rsa_key_one['keyid']] = rsa_key_one self.layout.keys[rsa_key_two['keyid']] = rsa_key_two self.layout._validate_keys() self.layout.validate() def test_wrong_steps_list(self): """Check that the validate method checks the steps' correctness.""" self.layout.steps = "not-a-step" with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() self.layout.steps = ["not-a-step"] with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() test_step = Step(name="this-is-a-step") with self.assertRaises(securesystemslib.exceptions.FormatError): test_step.expected_materials = ['this is a malformed step'] self.layout.steps = [test_step] self.layout.validate() test_step = Step(name="this-is-a-step") test_step.expected_materials = [["CREATE", "foo"]] test_step.threshold = 1 self.layout.steps = [test_step] self.layout.validate() def test_wrong_inspect_list(self): """Check that the validate method checks the inspections' correctness.""" self.layout.inspect = "not-an-inspection" with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() self.layout.inspect = ["not-an-inspection"] with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() test_inspection = Inspection(name="this-is-a-step") test_inspection.expected_materials = [ 'this is a malformed artifact rule' ] self.layout.inspect = [test_inspection] with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() test_inspection = Inspection(name="this-is-a-step") test_inspection.expected_materials = [["CREATE", "foo"]] self.layout.inspect = [test_inspection] self.layout.validate() def test_repeated_step_names(self): """Check that only unique names exist in the steps and inspect lists""" self.layout.steps = [Step(name="name"), Step(name="name")] with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() self.layout.steps = [Step(name="name")] self.layout.inspect = [Inspection(name="name")] with self.assertRaises(securesystemslib.exceptions.FormatError): self.layout.validate() self.layout.step = [Step(name="name"), Step(name="othername")] self.layout.inspect = [Inspection(name="thirdname")] self.layout.validate() 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 test_wrong_pubkeys(self): """Check validate pubkeys fails with wrong keys.""" # Pubkeys must be lists ... tmp_step = Step() tmp_step.pubkeys = "abcd" with self.assertRaises(securesystemslib.exceptions.FormatError): tmp_step.validate() # ... of keyids (hex schema) tmp_step.pubkeys = ["abcdefg"] with self.assertRaises(securesystemslib.exceptions.FormatError): tmp_step.validate()
def test_functionary_keys(self): """Test adding and listing functionary keys (securesystemslib and gpg). """ layout = Layout() self.assertEqual(len(layout.get_functionary_key_id_list()), 0) layout.add_functionary_keys_from_paths( [self.pubkey_path1, self.pubkey_path2]) layout.add_functionary_keys_from_gpg_keyids( [self.gpg_keyid1, self.gpg_keyid2], gpg_home=self.gnupg_home) layout._validate_keys() self.assertEqual(len(layout.get_functionary_key_id_list()), 4) # Must be a valid key object with self.assertRaises(securesystemslib.exceptions.FormatError): layout.add_functionary_key("abcd") # Must be pubkey and not private key with self.assertRaises(securesystemslib.exceptions.Error): layout.add_functionary_key_from_path(self.key_path) # Must be a valid path with self.assertRaises(securesystemslib.exceptions.FormatError): layout.add_functionary_key_from_path(123) # Must be a valid keyid with self.assertRaises(securesystemslib.exceptions.FormatError): layout.add_functionary_key_from_gpg_keyid("abcdefg") # Must be a list of paths with self.assertRaises(securesystemslib.exceptions.FormatError): layout.add_functionary_keys_from_paths("abcd") with self.assertRaises(securesystemslib.exceptions.FormatError): layout.add_functionary_keys_from_paths([1]) # Must be a list of keyids with self.assertRaises(securesystemslib.exceptions.FormatError): layout.add_functionary_keys_from_gpg_keyids(None) with self.assertRaises(securesystemslib.exceptions.FormatError): layout.add_functionary_keys_from_gpg_keyids(["abcdefg"])
#!/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 layout = Layout() build = Step(name="build") build.expected_materials.append(['ALLOW', 'src/*']) build.expected_products.append(['CREATE', 'foo']) analyze = Step(name="analyze") analyze.expected_materials.append( ['MATCH', 'foo', 'WITH', 'PRODUCTS', 'FROM', 'build']) layout.steps.append(build) layout.steps.append(analyze) 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 = DbomMetablock(signed=layout) # Sign and dump layout to "root.layout" metadata.sign(key_alice) metadata.save_owner_key(key_alice, assetID) metadata.dump("root.layout") metadata.save_layout("root.layout", assetID)
def test_get_inspection_name_list(self): """Test getting list of inspection names. """ names = ["a", "b", "c"] layout = Layout(inspect=[Inspection(name=name) for name in names]) self.assertListEqual(layout.get_inspection_name_list(), names)