Skip to content

Commit f4819e4

Browse files
committed
Add interface.import_public_keys_from_file function
Add convenience function to import multiple public keys of different key types from file into an sslib dict format at once, and tests. Note: Uses the new pseudo-standard docstring style suggested in secure-systems-lab/code-style-guidelines#20. All new interface functions should use that style (existing docstrings will be converted in separate PRs).
1 parent 6e8e95c commit f4819e4

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

securesystemslib/interface.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
import securesystemslib.util
4848
import securesystemslib.keys
4949

50+
from securesystemslib import KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA
51+
5052
import six
5153

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

950952

951953

954+
def import_public_keys_from_file(filepaths, key_types=None):
955+
"""Import multiple public keys from files.
956+
957+
Arguments:
958+
filepaths: A list of paths to public key files.
959+
key_types (optional): A list of types of keys to be imported associated
960+
with filepaths by index. Must be one of KEY_TYPE_RSA, KEY_TYPE_ED25519 or
961+
KEY_TYPE_ECDSA. If not specified, all keys are assumed to be
962+
KEY_TYPE_RSA.
963+
964+
Raises:
965+
TypeError: filepaths or key_types (if passed) is not iterable.
966+
securesystemslib.exceptions.FormatError: key_types is passed and does not
967+
have the same length as filepaths or contains an unsupported key type.
968+
See import_ed25519_publickey_from_file, import_rsa_publickey_from_file and
969+
import_ecdsa_publickey_from_file for other exceptions.
970+
971+
Returns:
972+
A dict of public keys in KEYDICT_SCHEMA format.
973+
974+
"""
975+
if key_types is None:
976+
key_types = [securesystemslib.KEY_TYPE_RSA] * len(filepaths)
977+
978+
if len(key_types) != len(filepaths):
979+
raise securesystemslib.exceptions.FormatError(
980+
"Pass equal amount of 'filepaths' (got {}) and 'key_types (got {}), "
981+
"or no 'key_types' at all to default to '{}'.".format(
982+
len(filepaths), len(key_types), KEY_TYPE_RSA))
983+
984+
key_dict = {}
985+
for idx, filepath in enumerate(filepaths):
986+
if key_types[idx] == KEY_TYPE_ED25519:
987+
key = import_ed25519_publickey_from_file(filepath)
988+
989+
elif key_types[idx] == KEY_TYPE_RSA:
990+
key = import_rsa_publickey_from_file(filepath)
991+
992+
elif key_types[idx] == KEY_TYPE_ECDSA:
993+
key = import_ecdsa_publickey_from_file(filepath)
994+
995+
else:
996+
raise securesystemslib.exceptions.FormatError(
997+
"Unsupported key type '{}'. Must be '{}', '{}' or '{}'.".format(
998+
key_types[idx], KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA))
999+
1000+
key_dict[key["keyid"]] = key
1001+
1002+
return key_dict
1003+
1004+
9521005
if __name__ == '__main__':
9531006
# The interactive sessions of the documentation strings can
9541007
# be tested by running interface.py as a standalone module:

tests/test_interface.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,12 @@
4444

4545
import securesystemslib.exceptions
4646
import securesystemslib.formats
47+
import securesystemslib.exceptions
4748
import securesystemslib.hash
4849
import securesystemslib.interface as interface
4950

51+
from securesystemslib import KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA
52+
5053
import six
5154

5255

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

616619

620+
621+
def test_import_public_keys_from_file(self):
622+
"""Test import multiple public keys with different types. """
623+
temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory)
624+
path_rsa = os.path.join(temporary_directory, "rsa_key")
625+
path_ed25519 = os.path.join(temporary_directory, "ed25519_key")
626+
path_ecdsa = os.path.join(temporary_directory, "ecdsa_key")
627+
628+
interface.generate_and_write_rsa_keypair(path_rsa, password="pw")
629+
interface.generate_and_write_ed25519_keypair(path_ed25519, password="pw")
630+
interface.generate_and_write_ecdsa_keypair(path_ecdsa, password="pw")
631+
632+
# Successfully import key dict with one key per supported key type
633+
key_dict = interface.import_public_keys_from_file([
634+
path_rsa + ".pub",
635+
path_ed25519 + ".pub",
636+
path_ecdsa + ".pub"],
637+
[KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA])
638+
639+
securesystemslib.formats.ANY_PUBKEY_DICT_SCHEMA.check_match(key_dict)
640+
self.assertListEqual(
641+
sorted([key["keytype"] for key in key_dict.values()]),
642+
sorted([KEY_TYPE_RSA, KEY_TYPE_ED25519, KEY_TYPE_ECDSA])
643+
)
644+
645+
# Successfully import default rsa key
646+
key_dict = interface.import_public_keys_from_file([path_rsa + ".pub"])
647+
securesystemslib.formats.ANY_PUBKEY_DICT_SCHEMA.check_match(key_dict)
648+
securesystemslib.formats.RSAKEY_SCHEMA.check_match(
649+
list(key_dict.values()).pop())
650+
651+
# Bad default rsa key type for ed25519
652+
with self.assertRaises(securesystemslib.exceptions.Error):
653+
interface.import_public_keys_from_file([path_ed25519 + ".pub"])
654+
655+
# Bad ed25519 key type for rsa key
656+
with self.assertRaises(securesystemslib.exceptions.Error):
657+
interface.import_public_keys_from_file(
658+
[path_rsa + ".pub"], [KEY_TYPE_ED25519])
659+
660+
# Unsupported key type
661+
with self.assertRaises(securesystemslib.exceptions.FormatError):
662+
interface.import_public_keys_from_file(
663+
[path_ed25519 + ".pub"], ["KEY_TYPE_UNSUPPORTED"])
664+
665+
# Mismatching arguments lists lenghts
666+
with self.assertRaises(securesystemslib.exceptions.FormatError):
667+
interface.import_public_keys_from_file(
668+
[path_rsa + ".pub", path_ed25519 + ".pub"], [KEY_TYPE_ED25519])
669+
670+
671+
617672
# Run the test cases.
618673
if __name__ == '__main__':
619674
unittest.main()

0 commit comments

Comments
 (0)