Skip to content

Add interface.export_pubkeys convenience function #278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions securesystemslib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@
logger = logging.getLogger(__name__)
logger.setLevel(logging.WARNING)
logger.addHandler(logging.StreamHandler())


# Global constants
# TODO: Replace hard-coded key types with these constants (and add more)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would these constants be better suited to the keys module? Is it worth documenting how we're choosing where to put constants?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO __init__ is a good default place for global constants. If we want to module-scope them, we should coordinate with #270.

KEY_TYPE_RSA = "rsa"
KEY_TYPE_ED25519 = "ed25519"
KEY_TYPE_ECDSA = "ecdsa"
53 changes: 53 additions & 0 deletions securesystemslib/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
import securesystemslib.util
import securesystemslib.keys

from securesystemslib import KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA

import six

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -949,6 +951,57 @@ def import_ecdsa_privatekey_from_file(filepath, password=None,



def import_public_keys_from_file(filepaths, key_types=None):
"""Import multiple public keys from files.

Arguments:
filepaths: A list of paths to public key files.
key_types (optional): A list of types of keys to be imported associated
with filepaths by index. Must be one of KEY_TYPE_RSA, KEY_TYPE_ED25519 or
KEY_TYPE_ECDSA. If not specified, all keys are assumed to be
KEY_TYPE_RSA.

Raises:
TypeError: filepaths or key_types (if passed) is not iterable.
securesystemslib.exceptions.FormatError: key_types is passed and does not
have the same length as filepaths or contains an unsupported key type.
See import_ed25519_publickey_from_file, import_rsa_publickey_from_file and
import_ecdsa_publickey_from_file for other exceptions.

Returns:
A dict of public keys in KEYDICT_SCHEMA format.

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

if len(key_types) != len(filepaths):
raise securesystemslib.exceptions.FormatError(
"Pass equal amount of 'filepaths' (got {}) and 'key_types (got {}), "
"or no 'key_types' at all to default to '{}'.".format(
len(filepaths), len(key_types), KEY_TYPE_RSA))

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_publickey_from_file(filepath)

elif key_types[idx] == KEY_TYPE_ECDSA:
key = import_ecdsa_publickey_from_file(filepath)

else:
raise securesystemslib.exceptions.FormatError(
"Unsupported key type '{}'. Must be '{}', '{}' or '{}'.".format(
key_types[idx], KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA))

key_dict[key["keyid"]] = key

return key_dict


if __name__ == '__main__':
# The interactive sessions of the documentation strings can
# be tested by running interface.py as a standalone module:
Expand Down
55 changes: 55 additions & 0 deletions tests/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@

import securesystemslib.exceptions
import securesystemslib.formats
import securesystemslib.exceptions
import securesystemslib.hash
import securesystemslib.interface as interface

from securesystemslib import KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA

import six


Expand Down Expand Up @@ -614,6 +617,58 @@ def test_import_ecdsa_privatekey_from_file(self):
interface.import_ecdsa_privatekey_from_file, ecdsa_keypath, 'pw')



def test_import_public_keys_from_file(self):
"""Test import multiple public keys with different types. """
temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory)
path_rsa = os.path.join(temporary_directory, "rsa_key")
path_ed25519 = os.path.join(temporary_directory, "ed25519_key")
path_ecdsa = os.path.join(temporary_directory, "ecdsa_key")

interface.generate_and_write_rsa_keypair(path_rsa, password="pw")
interface.generate_and_write_ed25519_keypair(path_ed25519, password="pw")
interface.generate_and_write_ecdsa_keypair(path_ecdsa, password="pw")

# Successfully import key dict with one key per supported key type
key_dict = interface.import_public_keys_from_file([
path_rsa + ".pub",
path_ed25519 + ".pub",
path_ecdsa + ".pub"],
[KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA])

securesystemslib.formats.ANY_PUBKEY_DICT_SCHEMA.check_match(key_dict)
self.assertListEqual(
sorted([key["keytype"] for key in key_dict.values()]),
sorted([KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA])
)

# Successfully import default rsa key
key_dict = interface.import_public_keys_from_file([path_rsa + ".pub"])
securesystemslib.formats.ANY_PUBKEY_DICT_SCHEMA.check_match(key_dict)
securesystemslib.formats.RSAKEY_SCHEMA.check_match(
list(key_dict.values()).pop())

# Bad default rsa key type for ed25519
with self.assertRaises(securesystemslib.exceptions.Error):
interface.import_public_keys_from_file([path_ed25519 + ".pub"])

# Bad ed25519 key type for rsa key
with self.assertRaises(securesystemslib.exceptions.Error):
interface.import_public_keys_from_file(
[path_rsa + ".pub"], [KEY_TYPE_ED25519])

# Unsupported key type
with self.assertRaises(securesystemslib.exceptions.FormatError):
interface.import_public_keys_from_file(
[path_ed25519 + ".pub"], ["KEY_TYPE_UNSUPPORTED"])

# Mismatching arguments lists lenghts
with self.assertRaises(securesystemslib.exceptions.FormatError):
interface.import_public_keys_from_file(
[path_rsa + ".pub", path_ed25519 + ".pub"], [KEY_TYPE_ED25519])



# Run the test cases.
if __name__ == '__main__':
unittest.main()