Ejemplo n.º 1
0
  def test_generate_and_write_rsa_keypair(self):

    # Test normal case.
    temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory)
    test_keypath = os.path.join(temporary_directory, 'rsa_key')
    test_keypath_unencrypted = os.path.join(temporary_directory,
        'rsa_key_unencrypted')

    interface.generate_and_write_rsa_keypair(test_keypath, password='******')
    self.assertTrue(os.path.exists(test_keypath))
    self.assertTrue(os.path.exists(test_keypath + '.pub'))

    # If an empty string is given for 'password', the private key file
    # is written to disk unencrypted.
    interface.generate_and_write_rsa_keypair(test_keypath_unencrypted,
        password='')
    self.assertTrue(os.path.exists(test_keypath_unencrypted))
    self.assertTrue(os.path.exists(test_keypath_unencrypted + '.pub'))

    # Ensure the generated key files are importable.
    scheme = 'rsassa-pss-sha256'
    imported_pubkey = \
      interface.import_rsa_publickey_from_file(test_keypath + '.pub')
    self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_pubkey))

    imported_privkey = interface.import_rsa_privatekey_from_file(test_keypath,
      'pw')
    self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_privkey))

    # Try to import the unencrypted key file.
    imported_privkey = interface.import_rsa_privatekey_from_file(test_keypath_unencrypted,
      '')
    self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_privkey))

    # Custom 'bits' argument.
    os.remove(test_keypath)
    os.remove(test_keypath + '.pub')
    interface.generate_and_write_rsa_keypair(test_keypath, bits=2048,
        password='******')
    self.assertTrue(os.path.exists(test_keypath))
    self.assertTrue(os.path.exists(test_keypath + '.pub'))


    # Test improperly formatted arguments.
    self.assertRaises(securesystemslib.exceptions.FormatError,
      interface.generate_and_write_rsa_keypair, 3, bits=2048, password='******')
    self.assertRaises(securesystemslib.exceptions.FormatError,
      interface.generate_and_write_rsa_keypair, test_keypath, bits='bad',
      password='******')
    self.assertRaises(securesystemslib.exceptions.FormatError,
      interface.generate_and_write_rsa_keypair, test_keypath, bits=2048,
      password=3)


    # Test invalid 'bits' argument.
    self.assertRaises(securesystemslib.exceptions.FormatError,
      interface.generate_and_write_rsa_keypair, test_keypath, bits=1024,
      password='******')
Ejemplo n.º 2
0
    def test_import_rsa_privatekey_from_file(self):
        # Test normal case.
        temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory)

        # Load one of the pre-generated key files from
        # 'securesystemslib/tests/repository_data'.  'password' unlocks the
        # pre-generated key files.
        key_filepath = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), 'data', 'keystore',
            'rsa_key')
        self.assertTrue(os.path.exists(key_filepath))

        imported_rsa_key = interface.import_rsa_privatekey_from_file(
            key_filepath, 'password')
        self.assertTrue(
            securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_rsa_key))

        # Test load encrypted key prompt for password
        with mock.patch('securesystemslib.interface.get_password',
                        return_value='password'):
            imported_rsa_key = interface.import_rsa_privatekey_from_file(
                key_filepath, prompt=True)
            self.assertTrue(
                securesystemslib.formats.RSAKEY_SCHEMA.matches(
                    imported_rsa_key))

        # Test improperly formatted 'filepath' argument.
        self.assertRaises(securesystemslib.exceptions.FormatError,
                          interface.import_rsa_privatekey_from_file, 3, 'pw')

        # Test improperly formatted 'password' argument.
        with self.assertRaises(securesystemslib.exceptions.FormatError):
            interface.import_rsa_privatekey_from_file(key_filepath, 123)

        # Test unallowed empty 'password'
        with self.assertRaises(ValueError):
            interface.import_rsa_privatekey_from_file(key_filepath, '')

        # Test unallowed passing 'prompt' and 'password'
        with self.assertRaises(ValueError):
            interface.import_rsa_privatekey_from_file(key_filepath,
                                                      password='******',
                                                      prompt=True)

        # Test invalid argument.
        # Non-existent key file.
        nonexistent_keypath = os.path.join(temporary_directory,
                                           'nonexistent_keypath')
        self.assertRaises(securesystemslib.exceptions.StorageError,
                          interface.import_rsa_privatekey_from_file,
                          nonexistent_keypath, 'pw')

        # Invalid key file argument.
        invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile')
        with open(invalid_keyfile, 'wb') as file_object:
            file_object.write(b'bad keyfile')
        self.assertRaises(securesystemslib.exceptions.CryptoError,
                          interface.import_rsa_privatekey_from_file,
                          invalid_keyfile, 'pw')
Ejemplo n.º 3
0
  def test_import_rsa_privatekey_from_file(self):
    # Test normal case.
    temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory)

    # Load one of the pre-generated key files from
    # 'securesystemslib/tests/repository_data'.  'password' unlocks the
    # pre-generated key files.
    key_filepath = os.path.join('data', 'keystore', 'rsa_key')
    self.assertTrue(os.path.exists(key_filepath))

    imported_rsa_key = interface.import_rsa_privatekey_from_file(
        key_filepath, 'password')
    self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_rsa_key))


    # Test improperly formatted argument.
    self.assertRaises(securesystemslib.exceptions.FormatError,
        interface.import_rsa_privatekey_from_file, 3, 'pw')


    # Test invalid argument.
    # Non-existent key file.
    nonexistent_keypath = os.path.join(temporary_directory,
        'nonexistent_keypath')
    self.assertRaises(IOError, interface.import_rsa_privatekey_from_file,
        nonexistent_keypath, 'pw')

    # Invalid key file argument.
    invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile')
    with open(invalid_keyfile, 'wb') as file_object:
      file_object.write(b'bad keyfile')
    self.assertRaises(securesystemslib.exceptions.CryptoError,
        interface.import_rsa_privatekey_from_file, invalid_keyfile, 'pw')
Ejemplo n.º 4
0
 def _modify_repository_root(
     self,
     modification_func: Callable[[Metadata], None],
     bump_version: bool = False,
 ) -> None:
     """Apply 'modification_func' to root and persist it."""
     role_path = os.path.join(self.repository_directory, "metadata",
                              "root.json")
     root = Metadata.from_file(role_path)
     modification_func(root)
     if bump_version:
         root.signed.version += 1
     root_key_path = os.path.join(self.keystore_directory, "root_key")
     root_key_dict = import_rsa_privatekey_from_file(root_key_path,
                                                     password="******")
     signer = SSlibSigner(root_key_dict)
     root.sign(signer)
     root.to_file(
         os.path.join(self.repository_directory, "metadata", "root.json"))
     root.to_file(
         os.path.join(
             self.repository_directory,
             "metadata",
             f"{root.signed.version}.root.json",
         ))
Ejemplo n.º 5
0
    def setUpClass(self):
        # Create layout with one inspection
        self.layout = Layout.read({
            "_type":
            "layout",
            "inspect": [],
            "steps": [{
                "name": "run-command",
                "expected_command": ["{EDITOR}"],
            }]
        })

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

        self.set_up_test_dir()

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

        # load alice's key
        self.alice = import_rsa_privatekey_from_file("alice")
        self.alice_pub_dict = import_publickeys_from_file(["alice.pub"])
Ejemplo n.º 6
0
    def setUpClass(cls):
        cls.repo_dir = os.path.join(os.getcwd(), 'repository_data',
                                    'repository', 'metadata')
        cls.metadata = {}
        for md in [
                "root", "timestamp", "snapshot", "targets", "role1", "role2"
        ]:
            with open(os.path.join(cls.repo_dir, f"{md}.json"), "rb") as f:
                cls.metadata[md] = f.read()

        keystore_dir = os.path.join(os.getcwd(), 'repository_data', 'keystore')
        cls.keystore = {}
        root_key_dict = import_rsa_privatekey_from_file(os.path.join(
            keystore_dir, "root" + '_key'),
                                                        password="******")
        cls.keystore["root"] = SSlibSigner(root_key_dict)
        for role in ["delegation", "snapshot", "targets", "timestamp"]:
            key_dict = import_ed25519_privatekey_from_file(os.path.join(
                keystore_dir, role + '_key'),
                                                           password="******")
            cls.keystore[role] = SSlibSigner(key_dict)

        def hashes_length_modifier(timestamp: Timestamp) -> None:
            timestamp.meta["snapshot.json"].hashes = None
            timestamp.meta["snapshot.json"].length = None

        cls.metadata["timestamp"] = cls.modify_metadata(
            cls, "timestamp", hashes_length_modifier)
Ejemplo n.º 7
0
def main():
    key_owner = interface.import_rsa_privatekey_from_file("./keys/owner")
    key_clone = interface.import_rsa_publickey_from_file("./keys/clone.pub")
    key_build = interface.import_rsa_publickey_from_file("./keys/build.pub")
    key_build_image = interface.import_rsa_publickey_from_file(
        "./keys/build-image.pub")

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

    metadata = Metablock(signed=layout)

    # Sign and dump layout to "root.layout"
    metadata.sign(key_owner)
    metadata.dump("root.layout")
Ejemplo n.º 8
0
  def setUpClass(self):
    """Creates and changes into temporary directory.
    Copies demo files to temp dir...
      - owner/functionary key pairs
      - *.link metadata files
      - layout template (not signed, no expiration date)
      - final product

    ...and dumps various layouts for different test scenarios
    """

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

    self.set_up_test_dir()

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

    shutil.copytree(scripts_directory, 'scripts')

    # Load layout template
    layout_template = Metablock.load("demo.layout.template")

    # Store layout paths to be used in tests
    self.layout_single_signed_path = "single-signed.layout"
    self.layout_double_signed_path = "double-signed.layout"

    # Import layout signing keys
    alice = import_rsa_privatekey_from_file("alice")
    bob = import_rsa_privatekey_from_file("bob")
    self.alice_path = "alice.pub"
    self.bob_path = "bob.pub"

    # dump a single signed layout
    layout_template.sign(alice)
    layout_template.dump(self.layout_single_signed_path)
    # dump a double signed layout
    layout_template.sign(bob)
    layout_template.dump(self.layout_double_signed_path)
Ejemplo n.º 9
0
  def setUpClass(self):
    """Create and change into temporary directory, generate two key pairs
    and dummy product. """
    self.set_up_test_dir()

    self.key_path = "test-key"
    self.key_path2 = "test-key2"
    generate_and_write_unencrypted_rsa_keypair(self.key_path)
    generate_and_write_unencrypted_rsa_keypair(self.key_path2)
    self.key = import_rsa_privatekey_from_file(self.key_path)
    self.key2 = import_rsa_privatekey_from_file(self.key_path2)

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

    self.test_product = "test_product"
    Path(self.test_product).touch()
Ejemplo n.º 10
0
    def setUpClass(self):
        """Create and change into temporary directory, generate key pair and dummy
    material, read key pair. """
        self.set_up_test_dir()

        self.step_name = "test_step"
        self.key_path = "test_key"
        generate_and_write_unencrypted_rsa_keypair(self.key_path)
        self.key = import_rsa_privatekey_from_file(self.key_path)
        self.key_pub = import_rsa_publickey_from_file(self.key_path + ".pub")

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

    self.key_path = "test_key"
    generate_and_write_unencrypted_rsa_keypair(self.key_path)
    self.key = import_rsa_privatekey_from_file(self.key_path)

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

    self.test_material = "test_material"
    Path(self.test_material).touch()
Ejemplo n.º 12
0
    def setUpClass(cls) -> None:
        cls.repo_dir = os.path.join(utils.TESTS_DIR, "repository_data",
                                    "repository", "metadata")
        cls.metadata = {}
        for md in [
                Root.type,
                Timestamp.type,
                Snapshot.type,
                Targets.type,
                "role1",
                "role2",
        ]:
            with open(os.path.join(cls.repo_dir, f"{md}.json"), "rb") as f:
                cls.metadata[md] = f.read()

        keystore_dir = os.path.join(utils.TESTS_DIR, "repository_data",
                                    "keystore")
        cls.keystore = {}
        root_key_dict = import_rsa_privatekey_from_file(os.path.join(
            keystore_dir, Root.type + "_key"),
                                                        password="******")
        cls.keystore[Root.type] = SSlibSigner(root_key_dict)
        for role in [
                "delegation", Snapshot.type, Targets.type, Timestamp.type
        ]:
            key_dict = import_ed25519_privatekey_from_file(os.path.join(
                keystore_dir, role + "_key"),
                                                           password="******")
            cls.keystore[role] = SSlibSigner(key_dict)

        def hashes_length_modifier(timestamp: Timestamp) -> None:
            timestamp.snapshot_meta.hashes = None
            timestamp.snapshot_meta.length = None

        cls.metadata[Timestamp.type] = cls.modify_metadata(
            Timestamp.type, hashes_length_modifier)
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)
Ejemplo n.º 14
0
def main():
    # Load Niels's private key to later sign the layout
    key_niels = interface.import_rsa_privatekey_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 = interface.import_rsa_publickey_from_file(
        "../functionary_aimee/aimee.pub")
    key_noud = interface.import_rsa_publickey_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")
Ejemplo n.º 15
0
def update_link_metadata(checks, core_workflow=True):
    root = get_root()
    ensure_dir_exists(path_join(root, LINK_DIR))

    # Sign only what affects each wheel
    products = []
    for check in checks:
        products.append(path_join(check, 'datadog_checks'))
        products.append(path_join(check, 'setup.py'))

    if core_workflow:
        key_id = get_key_id(GPG_COMMAND)

        # Find this latest signed link metadata file on disk.
        # NOTE: in-toto currently uses the first 8 characters of the signing keyId.
        key_id_prefix = key_id[:8].lower()

        tag_link = f'{STEP_NAME}.{key_id_prefix}.link'
        # Normalize line endings, particularly on Windows, to deal with git.
        options = {'gpg_keyid': key_id, 'normalize_line_endings': True}
    else:
        signing_key_path = os.getenv('IN_TOTO_SIGNING_KEY_PATH', '')
        signing_key = import_rsa_privatekey_from_file(
            signing_key_path, os.getenv('IN_TOTO_SIGNING_KEY_PASSWORD'))

        # NOTE: in-toto currently uses the first 8 characters of the signing keyID,
        # the latter of which we assume is the key filename.
        key_id_prefix = signing_key['keyid'][:8].lower()

        tag_link = f'{STEP_NAME}.{key_id_prefix}.link'
        # Do not allow machine to normalize line endings, because it is not on Windows,
        # for which git might rewrite line endings in files.
        options = {'signing_key': signing_key, 'normalize_line_endings': False}

    # Final location of metadata file.
    metadata_file = path_join(LINK_DIR, tag_link)

    with chdir(root):
        # We should ignore products untracked and ignored by git.
        run_in_toto(products, **options)

        # Check whether each signed product is being tracked AND ignored by git.
        # NOTE: We have to check now *AFTER* signing the tag link file, so that
        # we can check against the actual complete list of products.
        with open(tag_link) as tag_json:
            tag = json.load(tag_json)
            products = tag['signed']['products']

        for product in products:
            # If NOT tracked...
            if not tracked_by_git(product):
                # First, delete the tag link off disk so as not to pollute.
                os.remove(tag_link)

                # AND NOT ignored, then it most likely means the developer
                # forgot to add the file to git.
                if not ignored_by_git(product):
                    raise NeitherTrackedNorIgnoredFileException(product)
                # AND ignored, then it most likely means that incorrectly
                # recorded with in-toto files ignored by git.
                else:
                    raise UntrackedButIgnoredFileException(product)

        # Move it to the expected location.
        shutil.move(tag_link, metadata_file)

    return (metadata_file, )
Ejemplo n.º 16
0
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")
Ejemplo n.º 17
0
  def test_generate_and_write_rsa_keypair(self):

    # Test normal case.
    temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory)
    test_keypath = os.path.join(temporary_directory, 'rsa_key')
    test_keypath_unencrypted = os.path.join(temporary_directory,
        'rsa_key_unencrypted')

    returned_path = interface.generate_and_write_rsa_keypair(test_keypath,
        password='******')
    self.assertTrue(os.path.exists(test_keypath))
    self.assertTrue(os.path.exists(test_keypath + '.pub'))
    self.assertEqual(returned_path, test_keypath)

    # If an empty string is given for 'password', the private key file
    # is written to disk unencrypted.
    interface.generate_and_write_rsa_keypair(test_keypath_unencrypted,
        password='')
    self.assertTrue(os.path.exists(test_keypath_unencrypted))
    self.assertTrue(os.path.exists(test_keypath_unencrypted + '.pub'))

    # Ensure the generated key files are importable.
    scheme = 'rsassa-pss-sha256'
    imported_pubkey = \
      interface.import_rsa_publickey_from_file(test_keypath + '.pub')
    self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_pubkey))

    imported_privkey = interface.import_rsa_privatekey_from_file(test_keypath,
      'pw')
    self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_privkey))

    # Try to import the unencrypted key file, by not passing a password
    imported_privkey = interface.import_rsa_privatekey_from_file(test_keypath_unencrypted)
    self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_privkey))

    # Try to import the unencrypted key file, by entering an empty password
    with mock.patch('securesystemslib.interface.get_password',
        return_value=''):
      imported_privkey = \
            interface.import_rsa_privatekey_from_file(test_keypath_unencrypted,
                                                      prompt=True)
      self.assertTrue(
          securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_privkey))

    # Fail importing unencrypted key passing a password
    with self.assertRaises(securesystemslib.exceptions.CryptoError):
      interface.import_rsa_privatekey_from_file(test_keypath_unencrypted, 'pw')

    # Fail importing encrypted key passing no password
    with self.assertRaises(securesystemslib.exceptions.CryptoError):
      interface.import_rsa_privatekey_from_file(test_keypath)

    # Custom 'bits' argument.
    os.remove(test_keypath)
    os.remove(test_keypath + '.pub')
    interface.generate_and_write_rsa_keypair(test_keypath, bits=2048,
        password='******')
    self.assertTrue(os.path.exists(test_keypath))
    self.assertTrue(os.path.exists(test_keypath + '.pub'))

    # Test for a default filepath.  If 'filepath' is not given, the key's
    # KEYID is used as the filename.  The key is saved to the current working
    # directory.
    default_keypath = interface.generate_and_write_rsa_keypair(password='******')
    self.assertTrue(os.path.exists(default_keypath))
    self.assertTrue(os.path.exists(default_keypath + '.pub'))

    written_key = interface.import_rsa_publickey_from_file(default_keypath + '.pub')
    self.assertEqual(written_key['keyid'], os.path.basename(default_keypath))

    os.remove(default_keypath)
    os.remove(default_keypath + '.pub')

    # Test improperly formatted arguments.
    self.assertRaises(securesystemslib.exceptions.FormatError,
      interface.generate_and_write_rsa_keypair, 3, bits=2048, password='******')
    self.assertRaises(securesystemslib.exceptions.FormatError,
      interface.generate_and_write_rsa_keypair, test_keypath, bits='bad',
      password='******')
    self.assertRaises(securesystemslib.exceptions.FormatError,
      interface.generate_and_write_rsa_keypair, test_keypath, bits=2048,
      password=3)


    # Test invalid 'bits' argument.
    self.assertRaises(securesystemslib.exceptions.FormatError,
      interface.generate_and_write_rsa_keypair, test_keypath, bits=1024,
      password='******')
Ejemplo n.º 18
0
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")
Ejemplo n.º 19
0
    def test_rsa(self):
        """Test RSA key _generation and import interface functions. """

        # TEST: Generate default keys and import
        # Assert location and format
        fn_default = "default"
        fn_default_ret = _generate_and_write_rsa_keypair(filepath=fn_default)

        pub = import_rsa_publickey_from_file(fn_default + ".pub")
        priv = import_rsa_privatekey_from_file(fn_default)

        self.assertEqual(fn_default, fn_default_ret)
        self.assertTrue(RSAKEY_SCHEMA.matches(pub))
        self.assertTrue(PUBLIC_KEY_SCHEMA.matches(pub))
        self.assertTrue(RSAKEY_SCHEMA.matches(priv))
        # NOTE: There is no private key schema, at least check it has a value
        self.assertTrue(priv["keyval"]["private"])

        # TEST: Generate unencrypted keys with empty prompt
        # Assert importable without password
        fn_empty_prompt = "empty_prompt"
        with mock.patch("securesystemslib.interface.get_password",
                        return_value=""):
            _generate_and_write_rsa_keypair(filepath=fn_empty_prompt,
                                            prompt=True)
        import_rsa_privatekey_from_file(fn_empty_prompt)

        # TEST: Generate keys with auto-filename, i.e. keyid
        # Assert filename is keyid
        fn_keyid = _generate_and_write_rsa_keypair()
        pub = import_rsa_publickey_from_file(fn_keyid + ".pub")
        priv = import_rsa_privatekey_from_file(fn_keyid)
        self.assertTrue(
            os.path.basename(fn_keyid) == pub["keyid"] == priv["keyid"])

        # TEST: Generate keys with custom bits
        # Assert length
        bits = 4096
        fn_bits = "bits"
        _generate_and_write_rsa_keypair(filepath=fn_bits, bits=bits)

        priv = import_rsa_privatekey_from_file(fn_bits)
        # NOTE: Parse PEM with pyca/cryptography to get the key size property
        obj_bits = load_pem_private_key(
            priv["keyval"]["private"].encode("utf-8"),
            password=None,
            backend=default_backend())

        self.assertEqual(obj_bits.key_size, bits)

        # TEST: Generate two keypairs with encrypted private keys using ...
        pw = "pw"
        fn_encrypted = "encrypted"
        fn_prompt = "prompt"

        # ... a passed pw ...
        _generate_and_write_rsa_keypair(filepath=fn_encrypted, password=pw)
        with mock.patch("securesystemslib.interface.get_password",
                        return_value=pw):
            # ... and a prompted pw.
            _generate_and_write_rsa_keypair(filepath=fn_prompt, prompt=True)

            # Assert that both private keys are importable using the prompted pw ...
            import_rsa_privatekey_from_file(fn_prompt, prompt=True)
            import_rsa_privatekey_from_file(fn_encrypted, prompt=True)

        # ... and the passed pw.
        import_rsa_privatekey_from_file(fn_prompt, password=pw)
        import_rsa_privatekey_from_file(fn_encrypted, password=pw)

        # TEST: Import existing keys with encrypted private key (test regression)
        # Assert format
        pub = import_rsa_publickey_from_file(self.path_rsa + ".pub")
        priv = import_rsa_privatekey_from_file(self.path_rsa, "password")

        self.assertTrue(RSAKEY_SCHEMA.matches(pub))
        self.assertTrue(PUBLIC_KEY_SCHEMA.matches(pub))
        self.assertTrue(RSAKEY_SCHEMA.matches(priv))
        # NOTE: There is no private key schema, at least check it has a value
        self.assertTrue(priv["keyval"]["private"])

        # TEST: Generation errors
        for idx, (kwargs, err_msg) in enumerate([
                # Error on empty password
            ({
                "password": ""
            }, "encryption password must be 1 or more characters long"),
                # Error on 'password' and 'prompt=True'
            ({
                "password": pw,
                "prompt": True
            }, "passing 'password' and 'prompt=True' is not allowed")
        ]):

            with self.assertRaises(ValueError,
                                   msg="(row {})".format(idx)) as ctx:
                _generate_and_write_rsa_keypair(**kwargs)

            self.assertEqual(
                err_msg, str(ctx.exception),
                "expected: '{}' got: '{}' (row {})".format(
                    err_msg, ctx.exception, idx))

        # Error on bad argument format
        for idx, kwargs in enumerate([
            {
                "bits": 1024
            },  # Too low
            {
                "bits": "not-an-int"
            },
            {
                "filepath": 123456
            },  # Not a string
            {
                "password": 123456
            },  # Not a string
            {
                "prompt": "not-a-bool"
            }
        ]):
            with self.assertRaises(FormatError, msg="(row {})".format(idx)):
                _generate_and_write_rsa_keypair(**kwargs)

        # TEST: Import errors

        # Error public key import
        err_msg = "Invalid public pem"
        with self.assertRaises(Error) as ctx:
            import_rsa_publickey_from_file(fn_default)

        self.assertTrue(
            err_msg in str(ctx.exception),
            "expected: '{}' got: '{}'".format(err_msg, ctx.exception))

        # Error on private key import...
        for idx, (args, kwargs, err, err_msg) in enumerate([
                # Error on not a private key
            ([fn_default + ".pub"], {}, CryptoError,
             "Could not deserialize key data"),
                # Error on not encrypted
            ([fn_default], {
                "password": pw
            }, CryptoError,
             "Password was given but private key is not encrypted"),
                # Error on encrypted but no pw
            ([fn_encrypted], {}, CryptoError,
             "Password was not given but private key is encrypted"),
                # Error on encrypted but empty pw passed
            ([fn_encrypted], {
                "password": ""
            }, CryptoError,
             "Password was not given but private key is encrypted"),
                # Error on encrypted but bad pw passed
            ([fn_encrypted], {
                "password": "******"
            }, CryptoError, "Bad decrypt. Incorrect password?"),
                # Error on pw and prompt
            ([fn_default], {
                "password": pw,
                "prompt": True
            }, ValueError,
             "passing 'password' and 'prompt=True' is not allowed")
        ]):

            with self.assertRaises(err, msg="(row {})".format(idx)) as ctx:
                import_rsa_privatekey_from_file(*args, **kwargs)

            self.assertTrue(
                err_msg in str(ctx.exception),
                "expected: '{}' got: '{}' (row {})".format(
                    err_msg, ctx.exception, idx))

        # Error on encrypted but bad pw prompted
        err_msg = "Password was not given but private key is encrypted"
        with self.assertRaises(CryptoError) as ctx, mock.patch(
                "securesystemslib.interface.get_password",
                return_value="bad_pw"):
            import_rsa_privatekey_from_file(fn_encrypted)

        self.assertTrue(
            err_msg in str(ctx.exception),
            "expected: '{}' got: '{}'".format(err_msg, ctx.exception))

        # Error on bad argument format
        for idx, (args, kwargs) in enumerate([
            ([123456], {}),  # bad path
            ([fn_default], {
                "scheme": 123456
            }),  # bad scheme
            ([fn_default], {
                "scheme": "bad scheme"
            })  # bad scheme
        ]):
            with self.assertRaises(FormatError, msg="(row {})".format(idx)):
                import_rsa_publickey_from_file(*args, **kwargs)
            with self.assertRaises(FormatError, msg="(row {})".format(idx)):
                import_rsa_privatekey_from_file(*args, **kwargs)

        # bad password
        with self.assertRaises(FormatError):
            import_rsa_privatekey_from_file(fn_default, password=123456)

        # bad prompt
        with self.assertRaises(FormatError):
            import_rsa_privatekey_from_file(fn_default, prompt="not-a-bool")
Ejemplo n.º 20
0
    def setUp(self):
        """Imports keys, generates layout and layout_key_dict for the clone,
    update-version, and package step."""
        self.key_alice = interface.import_rsa_privatekey_from_file(
            "keys/alice")
        # Fetch and load Bob's and Carl's public keys
        # to specify that they are authorized to perform certain step in the layout
        self.key_bob = interface.import_rsa_publickey_from_file("keys/bob.pub")
        self.key_carl = interface.import_rsa_publickey_from_file(
            "keys/carl.pub")

        # Create layout
        self.layout = Layout.read({
            "_type":
            "layout",
            "keys": {
                self.key_bob["keyid"]: self.key_bob,
                self.key_carl["keyid"]: self.key_carl,
            },
            "steps": [{
                "name":
                "clone",
                "expected_materials": [],
                "expected_products": [["CREATE", "demo-project/foo.py"],
                                      ["DISALLOW", "*"]],
                "pubkeys": [self.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": [self.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": [self.key_carl["keyid"]],
                "expected_command": [
                    "tar",
                    "--exclude",
                    ".git",
                    "-zcvf",
                    "demo-project.tar.gz",
                    "demo-project",
                ],
                "threshold":
                1,
            }],
            "inspect": [],
        })

        self.metadata = Metablock(signed=self.layout)
        self.metadata.sign(self.key_alice)

        # Create the public key dict
        self.layout_key_dict = {}
        self.layout_key_dict.update(
            interface.import_publickeys_from_file(["keys/alice.pub"]))