Skip to content

Commit

Permalink
Merge pull request #278 from lukpueh/add-export-pubkeys
Browse files Browse the repository at this point in the history
Add interface.export_pubkeys convenience function
  • Loading branch information
lukpueh authored Sep 30, 2020
2 parents 21ab71c + f4819e4 commit 2ee9607
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
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)
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 @@ -935,6 +937,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()

0 comments on commit 2ee9607

Please sign in to comment.