Exemple #1
0
def prompt_import_ed25519_privatekey_from_file(filepath):
  """Tries to load an Ed25519 private key without password. If a CryptoError
  occurs, prompts the user for a password and tries to load the key again.
  """
  password = None
  try:
    import_ed25519_privatekey_from_file(filepath, password)
  except securesystemslib.exceptions.CryptoError:
    password = prompt_password()
  return import_ed25519_privatekey_from_file(filepath, password)
Exemple #2
0
 def test_create_and_import_encrypted_ed25519_no_password(self):
     """Try import encrypted ed25519 key without or wrong pw, raises
 exception. """
     name = "key8"
     password = "******"
     generate_and_write_ed25519_keypair(name, password)
     with self.assertRaises(securesystemslib.exceptions.CryptoError):
         import_ed25519_privatekey_from_file(name)
     with self.assertRaises(securesystemslib.exceptions.CryptoError):
         import_ed25519_privatekey_from_file(name, "wrong-password")
Exemple #3
0
    def setUpClass(cls):
        # Create a temporary directory to store the repository, metadata, and
        # target files.  'temporary_directory' must be deleted in
        # TearDownClass() so that temporary files are always removed, even when
        # exceptions occur.
        cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd())

        test_repo_data = os.path.join(
                os.path.dirname(os.path.realpath(__file__)), 'repository_data')

        cls.repo_dir = os.path.join(cls.temporary_directory, 'repository')
        shutil.copytree(
                os.path.join(test_repo_data, 'repository'), cls.repo_dir)

        cls.keystore_dir = os.path.join(cls.temporary_directory, 'keystore')
        shutil.copytree(
                os.path.join(test_repo_data, 'keystore'), cls.keystore_dir)

        # Load keys into memory
        cls.keystore = {}
        for role in ['delegation', 'snapshot', 'targets', 'timestamp']:
            cls.keystore[role] = {
                'private': import_ed25519_privatekey_from_file(
                        os.path.join(cls.keystore_dir, role + '_key'),
                        password="******"),
                'public': import_ed25519_publickey_from_file(
                        os.path.join(cls.keystore_dir, role + '_key.pub'))
            }
  def test_generate_and_write_ed25519_keypair(self):

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

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

    # Ensure the generated key files are importable.
    imported_pubkey = \
      interface.import_ed25519_publickey_from_file(test_keypath + '.pub')
    self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_pubkey))

    imported_privkey = \
      interface.import_ed25519_privatekey_from_file(test_keypath, 'pw')
    self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_privkey))


    # Test improperly formatted arguments.
    self.assertRaises(securesystemslib.exceptions.FormatError,
        interface.generate_and_write_ed25519_keypair, 3, password='******')
    self.assertRaises(securesystemslib.exceptions.FormatError,
        interface.generate_and_write_rsa_keypair, test_keypath, password=3)
Exemple #5
0
    def setUpClass(cls) -> None:
        # Create a temporary directory to store the repository, metadata, and
        # target files.  'temporary_directory' must be deleted in
        # TearDownClass() so that temporary files are always removed, even when
        # exceptions occur.
        cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd())

        test_repo_data = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "repository_data")

        cls.repo_dir = os.path.join(cls.temporary_directory, "repository")
        shutil.copytree(os.path.join(test_repo_data, "repository"),
                        cls.repo_dir)

        cls.keystore_dir = os.path.join(cls.temporary_directory, "keystore")
        shutil.copytree(os.path.join(test_repo_data, "keystore"),
                        cls.keystore_dir)

        # Load keys into memory
        cls.keystore = {}
        for role in [
                "delegation", Snapshot.type, Targets.type, Timestamp.type
        ]:
            cls.keystore[role] = import_ed25519_privatekey_from_file(
                os.path.join(cls.keystore_dir, role + "_key"),
                password="******",
            )
    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)
  def test_import_ed25519_privatekey_from_file(self):
    # Test normal case.
    # Generate ed25519 keys that can be imported.
    scheme = 'ed25519'
    temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory)
    ed25519_keypath = os.path.join(temporary_directory, 'ed25519_key')
    interface.generate_and_write_ed25519_keypair(ed25519_keypath, password='******')

    imported_ed25519_key = \
      interface.import_ed25519_privatekey_from_file(ed25519_keypath, 'pw')
    self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_ed25519_key))


    # Test improperly formatted argument.
    self.assertRaises(securesystemslib.exceptions.FormatError,
        interface.import_ed25519_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_ed25519_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.Error,
      interface.import_ed25519_privatekey_from_file, invalid_keyfile, 'pw')

    # Invalid private key imported (contains unexpected keytype.)
    imported_ed25519_key['keytype'] = 'invalid_keytype'

    # Use 'pyca_crypto_keys.py' to bypass the key format validation performed
    # by 'keys.py'.
    salt, iterations, derived_key = \
      securesystemslib.pyca_crypto_keys._generate_derived_key('pw')

    # Store the derived key info in a dictionary, the object expected
    # by the non-public _encrypt() routine.
    derived_key_information = {'salt': salt, 'iterations': iterations,
        'derived_key': derived_key}

    # Convert the key object to json string format and encrypt it with the
    # derived key.
    encrypted_key = \
      securesystemslib.pyca_crypto_keys._encrypt(json.dumps(imported_ed25519_key),
          derived_key_information)

    with open(ed25519_keypath, 'wb') as file_object:
      file_object.write(encrypted_key.encode('utf-8'))

    self.assertRaises(securesystemslib.exceptions.FormatError,
        interface.import_ed25519_privatekey_from_file, ed25519_keypath, 'pw')
Exemple #8
0
    def test_create_and_import_ed25519(self):
        """Create ed25519 key and import private and public key separately. """
        name = "key6"
        generate_and_write_ed25519_keypair(name)
        private_key = import_ed25519_privatekey_from_file(name)
        public_key = import_ed25519_publickey_from_file(name + ".pub")

        securesystemslib.formats.KEY_SCHEMA.check_match(private_key)
        self.assertTrue(private_key["keyval"].get("private"))
        self.assertTrue(
            securesystemslib.formats.PUBLIC_KEY_SCHEMA.matches(public_key))
Exemple #9
0
def read_keypair(functionary: str, keypath: Keypath, i: int = 1, n: int = 1, passphrase: Optional[str] = None) -> Keypair:
    assert isinstance(keypath, Keypath)

    private_keypath = keypath.private
    private_key_obj = import_ed25519_privatekey_from_file(keypath.private, password=passphrase)
    private_key = Key(private_keypath, private_key_obj)

    # and its corresponding public key.
    public_keypath = keypath.public
    public_key_obj = import_ed25519_publickey_from_file(keypath.public)
    public_key = Key(public_keypath, public_key_obj)

    return Keypair(private_key, public_key)
Exemple #10
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 layout for test scenario
    """
        # 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")

        scripts_directory = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "scripts")

        # 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)

        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_double_signed_path = "double-signed.layout"

        # Import layout signing keys
        alice = import_rsa_key_from_file("alice")
        danny = import_ed25519_privatekey_from_file("danny")
        self.alice_path = "alice.pub"
        self.danny_path = "danny.pub"

        # dump a double signed layout
        layout_template.sign(alice)
        layout_template.sign(danny)
        layout_template.dump(self.layout_double_signed_path)
Exemple #11
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 test_ed25519(self):
        """Test ed25519 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_ed25519_keypair(
            filepath=fn_default)

        pub = import_ed25519_publickey_from_file(fn_default + ".pub")
        priv = import_ed25519_privatekey_from_file(fn_default)

        self.assertEqual(fn_default, fn_default_ret)
        self.assertTrue(ED25519KEY_SCHEMA.matches(pub))
        self.assertTrue(PUBLIC_KEY_SCHEMA.matches(pub))
        self.assertTrue(ED25519KEY_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 with empty prompt password and without password
        fn_empty_prompt = "empty_prompt"
        with mock.patch("securesystemslib.interface.get_password",
                        return_value=""):
            _generate_and_write_ed25519_keypair(filepath=fn_empty_prompt)
            import_ed25519_privatekey_from_file(fn_empty_prompt, prompt=True)
        import_ed25519_privatekey_from_file(fn_empty_prompt)

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

        # TEST: Generate two keypairs with encrypted private keys using ...
        pw = "pw"
        fn_encrypted = "encrypted"
        fn_prompt = "prompt"
        # ... a passed pw ...
        _generate_and_write_ed25519_keypair(filepath=fn_encrypted, password=pw)
        with mock.patch("securesystemslib.interface.get_password",
                        return_value=pw):
            # ... and a prompted pw.
            _generate_and_write_ed25519_keypair(filepath=fn_prompt,
                                                prompt=True)

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

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

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

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

        # TEST: Unexpected behavior
        # FIXME: Should 'import_ed25519_publickey_from_file' be able to import a
        # a non-encrypted ed25519 private key? I think it should not, but it is:
        priv = import_ed25519_publickey_from_file(fn_default)
        self.assertTrue(ED25519KEY_SCHEMA.matches(priv))
        self.assertTrue(priv["keyval"]["private"])

        # FIXME: Should 'import_ed25519_privatekey_from_file' be able to import a
        # an ed25519 public key? I think it should not, but it is:
        pub = import_ed25519_privatekey_from_file(fn_default + ".pub")
        self.assertTrue(PUBLIC_KEY_SCHEMA.matches(pub))

        # 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_ed25519_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([
            {
                "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_ed25519_keypair(**kwargs)

        # TEST: Import errors
        # Error on public key import...
        for idx, (fn, err_msg) in enumerate([
                # Error on invalid json (custom key format)
            (fn_encrypted, "Cannot deserialize to a Python object"),
                # Error on invalid custom key format
            (self.path_no_key, "Missing key"),
                # Error on invalid key type
            (self.path_ecdsa + ".pub", "Invalid key type loaded")
        ]):
            with self.assertRaises(Error, msg="(row {})".format(idx)) as ctx:
                import_ed25519_publickey_from_file(fn)

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

        # Error on private key import...
        for idx, (args, kwargs, err, err_msg) in enumerate([
                # Error on not an ed25519 private key
            ([self.path_ecdsa], {}, CryptoError,
             "Malformed Ed25519 key JSON, possibly due to encryption, "
             "but no password provided?"),
                # Error on not encrypted
            ([fn_default], {
                "password": pw
            }, CryptoError, "Invalid encrypted file."),
                # Error on encrypted but no pw
            ([fn_encrypted], {}, CryptoError,
             "Malformed Ed25519 key JSON, possibly due to encryption, "
             "but no password provided?"),
                # Error on encrypted but empty pw
            ([fn_encrypted], {
                "password": ""
            }, CryptoError, "Decryption failed."),
                # Error on encrypted but bad pw passed
            ([fn_encrypted], {
                "password": "******"
            }, CryptoError, "Decryption failed."),
                # 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_ed25519_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 = ("Malformed Ed25519 key JSON, possibly due to encryption, "
                   "but no password provided?")
        with self.assertRaises(CryptoError) as ctx, mock.patch(
                "securesystemslib.interface.get_password",
                return_value="bad_pw"):
            import_ed25519_privatekey_from_file(fn_encrypted)

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

        # Error on bad path format
        with self.assertRaises(FormatError):
            import_ed25519_publickey_from_file(123456)
        with self.assertRaises(FormatError):
            import_ed25519_privatekey_from_file(123456)

        # Error on bad password format
        with self.assertRaises(FormatError):
            import_ed25519_privatekey_from_file(fn_default, password=123456)

        # Error on bad prompt format
        with self.assertRaises(FormatError):
            import_ed25519_privatekey_from_file(fn_default,
                                                prompt="not-a-bool")
  def test_generate_and_write_ed25519_keypair(self):

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

    returned_path = interface.generate_and_write_ed25519_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_ed25519_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.
    imported_pubkey = \
      interface.import_ed25519_publickey_from_file(test_keypath + '.pub')
    self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA\
                    .matches(imported_pubkey))

    imported_privkey = \
      interface.import_ed25519_privatekey_from_file(test_keypath, 'pw')
    self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA\
                    .matches(imported_privkey))

    # Fail importing encrypted key passing password and prompt
    with self.assertRaises(ValueError):
      interface.import_ed25519_privatekey_from_file(test_keypath,
                                                    password='******',
                                                    prompt=True)

    # Fail importing encrypted key passing an empty string for passwd 
    with self.assertRaises(ValueError):
      interface.import_ed25519_privatekey_from_file(test_keypath,
                                                    password='')

    # Try to import the unencrypted key file, by not passing a password
    imported_privkey = \
        interface.import_ed25519_privatekey_from_file(test_keypath_unencrypted)
    self.assertTrue(securesystemslib.formats.ED25519KEY_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_ed25519_privatekey_from_file(test_keypath_unencrypted,
                                                      prompt=True)
      self.assertTrue(
          securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_privkey))

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

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

    # 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_ed25519_keypair(password='******')
    self.assertTrue(os.path.exists(default_keypath))
    self.assertTrue(os.path.exists(default_keypath + '.pub'))

    written_key = interface.import_ed25519_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_ed25519_keypair, 3, password='******')
    self.assertRaises(securesystemslib.exceptions.FormatError,
        interface.generate_and_write_rsa_keypair, test_keypath, password=3)
Exemple #14
0
def get_private_key():
    init_keys()
    return import_ed25519_privatekey_from_file(KEY_NAME, password=PASSWORD)