Exemple #1
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'))
            }
Exemple #2
0
    def test_root_add_key_and_revoke_key(self) -> None:
        root_path = os.path.join(self.repo_dir, "metadata", "root.json")
        root = Metadata[Root].from_file(root_path)

        # Create a new key
        root_key2 = import_ed25519_publickey_from_file(
            os.path.join(self.keystore_dir, "root_key2.pub"))
        keyid = root_key2["keyid"]
        key_metadata = Key(
            keyid,
            root_key2["keytype"],
            root_key2["scheme"],
            root_key2["keyval"],
        )

        # Assert that root does not contain the new key
        self.assertNotIn(keyid, root.signed.roles[Root.type].keyids)
        self.assertNotIn(keyid, root.signed.keys)

        # Assert that add_key with old argument order will raise an error
        with self.assertRaises(ValueError):
            root.signed.add_key(Root.type, key_metadata)  # type: ignore

        # Add new root key
        root.signed.add_key(key_metadata, Root.type)

        # Assert that key is added
        self.assertIn(keyid, root.signed.roles[Root.type].keyids)
        self.assertIn(keyid, root.signed.keys)

        # Confirm that the newly added key does not break
        # the object serialization
        root.to_dict()

        # Try adding the same key again and assert its ignored.
        pre_add_keyid = root.signed.roles[Root.type].keyids.copy()
        root.signed.add_key(key_metadata, Root.type)
        self.assertEqual(pre_add_keyid, root.signed.roles[Root.type].keyids)

        # Add the same key to targets role as well
        root.signed.add_key(key_metadata, Targets.type)

        # Add the same key to a nonexistent role.
        with self.assertRaises(ValueError):
            root.signed.add_key(key_metadata, "nosuchrole")

        # Remove the key from root role (targets role still uses it)
        root.signed.revoke_key(keyid, Root.type)
        self.assertNotIn(keyid, root.signed.roles[Root.type].keyids)
        self.assertIn(keyid, root.signed.keys)

        # Remove the key from targets as well
        root.signed.revoke_key(keyid, Targets.type)
        self.assertNotIn(keyid, root.signed.roles[Targets.type].keyids)
        self.assertNotIn(keyid, root.signed.keys)

        with self.assertRaises(ValueError):
            root.signed.revoke_key("nosuchkey", Root.type)
        with self.assertRaises(ValueError):
            root.signed.revoke_key(keyid, "nosuchrole")
Exemple #3
0
    def test_metadata_root(self):
        root_path = os.path.join(
                self.repo_dir, 'metadata', 'root.json')
        root = Metadata.from_file(root_path)

        # Add a second key to root role
        root_key2 =  import_ed25519_publickey_from_file(
                    os.path.join(self.keystore_dir, 'root_key2.pub'))

        keyid = root_key2['keyid']
        key_metadata = format_keyval_to_metadata(
            root_key2['keytype'], root_key2['scheme'], root_key2['keyval'])

        # Assert that root does not contain the new key
        self.assertNotIn(keyid, root.signed.roles['root']['keyids'])
        self.assertNotIn(keyid, root.signed.keys)

        # Add new root key
        root.signed.add_key('root', keyid, key_metadata)

        # Assert that key is added
        self.assertIn(keyid, root.signed.roles['root']['keyids'])
        self.assertIn(keyid, root.signed.keys)

        # Remove the key
        root.signed.remove_key('root', keyid)

        # Assert that root does not contain the new key anymore
        self.assertNotIn(keyid, root.signed.roles['root']['keyids'])
        self.assertNotIn(keyid, root.signed.keys)
  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 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 #6
0
def import_public_keys_from_files_as_dict(filepaths, key_types=None):
  """
  <Purpose>
    Takes a list of filepaths to RSA public keys and returns them as a
    dictionary conformant with securesystemslib.formats.KEYDICT_SCHEMA.

  <Arguments>
    filepaths:
      List of paths to the public keys.

    key_types: (optional)
      List types of each of the keys being imported into the dict. If not
      specified, all keys are assumed to be RSA.

  <Exceptions>
    securesystemslib.exceptions.FormatError, if the arguments are
    don't have the same length.

    UnsupportedKeyTypeError, if the key_type specified is unsupported.

  <Side Effects>
    Each file in 'filepaths' is read and its contents extracted

  <Returns>
    A key dict object conformant with securesystemslib.formats.KEYDICT_SCHEMA
  """
  # are key_types needed?
  # we could figure it out using the key format

  if key_types is None:
    key_types = [KEY_TYPE_RSA] * len(filepaths)

  if len(key_types) != len(filepaths):
    raise securesystemslib.exceptions.FormatError(
        "number of key_types should match with the number"
        " of layout keys specified")

  key_dict = {}
  for idx, filepath in enumerate(filepaths):

    if key_types[idx] == KEY_TYPE_ED25519:
      key = import_ed25519_publickey_from_file(filepath)
    elif key_types[idx] == KEY_TYPE_RSA:
      key = import_rsa_key_from_file(filepath)
    else:  # pragma: no cover
      # This branch is never possible as argparse already checks valid keys
      # via the choices parameter.
      raise UnsupportedKeyTypeError('Unsupported keytype: ' + key_types[idx])

    securesystemslib.formats.PUBLIC_KEY_SCHEMA.check_match(key)
    keyid = key["keyid"]
    key_dict[keyid] = key
  return key_dict
    def test_import_ed25519_publickey_from_file(self):
        # Test normal case.
        # Generate ed25519 keys that can be imported.
        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_publickey_from_file(ed25519_keypath + '.pub')
        self.assertTrue(
            securesystemslib.formats.ED25519KEY_SCHEMA.matches(
                imported_ed25519_key))

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

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

        # 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_publickey_from_file,
                          invalid_keyfile)

        # Invalid public key imported (contains unexpected keytype.)
        keytype = imported_ed25519_key['keytype']
        keyval = imported_ed25519_key['keyval']
        scheme = imported_ed25519_key['scheme']

        ed25519key_metadata_format = \
          securesystemslib.keys.format_keyval_to_metadata(keytype, scheme,
          keyval, private=False)

        ed25519key_metadata_format['keytype'] = 'invalid_keytype'
        with open(ed25519_keypath + '.pub', 'wb') as file_object:
            file_object.write(
                json.dumps(ed25519key_metadata_format).encode('utf-8'))

        self.assertRaises(securesystemslib.exceptions.FormatError,
                          interface.import_ed25519_publickey_from_file,
                          ed25519_keypath + '.pub')
Exemple #8
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 #9
0
    def test_root_add_key_and_remove_key(self):
        root_path = os.path.join(
                self.repo_dir, 'metadata', 'root.json')
        root = Metadata[Root].from_file(root_path)

        # Create a new key
        root_key2 =  import_ed25519_publickey_from_file(
                    os.path.join(self.keystore_dir, 'root_key2.pub'))
        keyid = root_key2['keyid']
        key_metadata = Key(keyid, root_key2['keytype'], root_key2['scheme'],
            root_key2['keyval'])

        # Assert that root does not contain the new key
        self.assertNotIn(keyid, root.signed.roles['root'].keyids)
        self.assertNotIn(keyid, root.signed.keys)

        # Add new root key
        root.signed.add_key('root', key_metadata)

        # Assert that key is added
        self.assertIn(keyid, root.signed.roles['root'].keyids)
        self.assertIn(keyid, root.signed.keys)

        # Confirm that the newly added key does not break
        # the object serialization
        root.to_dict()

        # Try adding the same key again and assert its ignored.
        pre_add_keyid = root.signed.roles['root'].keyids.copy()
        root.signed.add_key('root', key_metadata)
        self.assertEqual(pre_add_keyid, root.signed.roles['root'].keyids)

        # Add the same key to targets role as well
        root.signed.add_key('targets', key_metadata)

        # Remove the key from root role (targets role still uses it)
        root.signed.remove_key('root', keyid)
        self.assertNotIn(keyid, root.signed.roles['root'].keyids)
        self.assertIn(keyid, root.signed.keys)

        # Remove the key from targets as well
        root.signed.remove_key('targets', keyid)
        self.assertNotIn(keyid, root.signed.roles['targets'].keyids)
        self.assertNotIn(keyid, root.signed.keys)

        with self.assertRaises(KeyError):
            root.signed.remove_key('root', 'nosuchkey')
    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 #12
0
def get_public_key():
    init_keys()
    return import_ed25519_publickey_from_file(KEY_NAME + '.pub')