Skip to content

Commit 01a0c95

Browse files
Merge pull request #232 from joshuagl/joshuagl/abstract-filesystem
Implement filesystem abstraction
2 parents 62936ca + c60ef22 commit 01a0c95

File tree

8 files changed

+502
-65
lines changed

8 files changed

+502
-65
lines changed

securesystemslib/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,7 @@ class InvalidConfigurationError(Error):
121121
"""If a configuration object does not match the expected format."""
122122
pass
123123

124+
class StorageError(Error):
125+
"""Indicate an error occured during interaction with an abstracted storage
126+
backend."""
127+
pass

securesystemslib/hash.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
import securesystemslib.exceptions
3838
import securesystemslib.formats
39+
import securesystemslib.storage
3940

4041

4142
DEFAULT_CHUNK_SIZE = 4096
@@ -314,7 +315,8 @@ def digest_fileobject(file_object, algorithm=DEFAULT_HASH_ALGORITHM,
314315

315316

316317
def digest_filename(filename, algorithm=DEFAULT_HASH_ALGORITHM,
317-
hash_library=DEFAULT_HASH_LIBRARY, normalize_line_endings=False):
318+
hash_library=DEFAULT_HASH_LIBRARY, normalize_line_endings=False,
319+
storage_backend=None):
318320
"""
319321
<Purpose>
320322
Generate a digest object, update its hash using a file object
@@ -333,6 +335,11 @@ def digest_filename(filename, algorithm=DEFAULT_HASH_ALGORITHM,
333335
normalize_line_endings:
334336
Whether or not to normalize line endings for cross-platform support.
335337
338+
storage_backend:
339+
An object which implements
340+
securesystemslib.storage.StorageBackendInterface. When no object is
341+
passed a FilesystemBackend will be instantiated and used.
342+
336343
<Exceptions>
337344
securesystemslib.exceptions.FormatError, if the arguments are
338345
improperly formatted.
@@ -361,8 +368,11 @@ def digest_filename(filename, algorithm=DEFAULT_HASH_ALGORITHM,
361368

362369
digest_object = None
363370

371+
if storage_backend is None:
372+
storage_backend = securesystemslib.storage.FilesystemBackend()
373+
364374
# Open 'filename' in read+binary mode.
365-
with open(filename, 'rb') as file_object:
375+
with storage_backend.get(filename) as file_object:
366376
# Create digest_object and update its hash data from file_object.
367377
# digest_fileobject() raises:
368378
# securesystemslib.exceptions.UnsupportedAlgorithmError

securesystemslib/interface.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
import securesystemslib.formats
4545
import securesystemslib.settings
46+
import securesystemslib.storage
4647
import securesystemslib.util
4748
import securesystemslib.keys
4849

@@ -54,7 +55,7 @@
5455
from colorama import Fore
5556
TERM_RED = Fore.RED
5657
TERM_RESET = Fore.RESET
57-
except ImportError:
58+
except ImportError: # pragma: no cover
5859
logger.debug("Failed to find colorama module, terminal output won't be colored")
5960
TERM_RED = ''
6061
TERM_RESET = ''
@@ -242,7 +243,8 @@ def generate_and_write_rsa_keypair(filepath=None, bits=DEFAULT_RSA_KEY_BITS,
242243

243244

244245
def import_rsa_privatekey_from_file(filepath, password=None,
245-
scheme='rsassa-pss-sha256', prompt=False):
246+
scheme='rsassa-pss-sha256', prompt=False,
247+
storage_backend=None):
246248
"""
247249
<Purpose>
248250
Import the PEM file in 'filepath' containing the private key.
@@ -272,6 +274,11 @@ def import_rsa_privatekey_from_file(filepath, password=None,
272274
If True the user is prompted for a passphrase to decrypt 'filepath'.
273275
Default is False.
274276
277+
storage_backend:
278+
An object which implements
279+
securesystemslib.storage.StorageBackendInterface. When no object is
280+
passed a FilesystemBackend will be instantiated and used.
281+
275282
<Exceptions>
276283
ValueError, if 'password' is passed and 'prompt' is True.
277284
@@ -344,8 +351,11 @@ def import_rsa_privatekey_from_file(filepath, password=None,
344351
logger.debug('No password was given. Attempting to import an'
345352
' unencrypted file.')
346353

354+
if storage_backend is None:
355+
storage_backend = securesystemslib.storage.FilesystemBackend()
356+
347357
# Read the contents of 'filepath' that should be a PEM formatted private key.
348-
with open(filepath, 'rb') as file_object:
358+
with storage_backend.get(filepath) as file_object:
349359
pem_key = file_object.read().decode('utf-8')
350360

351361
# Convert 'pem_key' to 'securesystemslib.formats.RSAKEY_SCHEMA' format.
@@ -360,7 +370,8 @@ def import_rsa_privatekey_from_file(filepath, password=None,
360370

361371

362372

363-
def import_rsa_publickey_from_file(filepath, scheme='rsassa-pss-sha256'):
373+
def import_rsa_publickey_from_file(filepath, scheme='rsassa-pss-sha256',
374+
storage_backend=None):
364375
"""
365376
<Purpose>
366377
Import the RSA key stored in 'filepath'. The key object returned is in the
@@ -374,6 +385,11 @@ def import_rsa_publickey_from_file(filepath, scheme='rsassa-pss-sha256'):
374385
scheme:
375386
The signature scheme used by the imported key.
376387
388+
storage_backend:
389+
An object which implements
390+
securesystemslib.storage.StorageBackendInterface. When no object is
391+
passed a FilesystemBackend will be instantiated and used.
392+
377393
<Exceptions>
378394
securesystemslib.exceptions.FormatError, if 'filepath' is improperly
379395
formatted.
@@ -397,9 +413,12 @@ def import_rsa_publickey_from_file(filepath, scheme='rsassa-pss-sha256'):
397413
# Is 'scheme' properly formatted?
398414
securesystemslib.formats.RSA_SCHEME_SCHEMA.check_match(scheme)
399415

416+
if storage_backend is None:
417+
storage_backend = securesystemslib.storage.FilesystemBackend()
418+
400419
# Read the contents of the key file that should be in PEM format and contains
401420
# the public portion of the RSA key.
402-
with open(filepath, 'rb') as file_object:
421+
with storage_backend.get(filepath) as file_object:
403422
rsa_pubkey_pem = file_object.read().decode('utf-8')
404423

405424
# Convert 'rsa_pubkey_pem' to 'securesystemslib.formats.RSAKEY_SCHEMA' format.
@@ -587,7 +606,8 @@ def import_ed25519_publickey_from_file(filepath):
587606

588607

589608

590-
def import_ed25519_privatekey_from_file(filepath, password=None, prompt=False):
609+
def import_ed25519_privatekey_from_file(filepath, password=None, prompt=False,
610+
storage_backend=None):
591611
"""
592612
<Purpose>
593613
Import the encrypted ed25519 key file in 'filepath', decrypt it, and return
@@ -610,6 +630,11 @@ def import_ed25519_privatekey_from_file(filepath, password=None, prompt=False):
610630
If True the user is prompted for a passphrase to decrypt 'filepath'.
611631
Default is False.
612632
633+
storage_backend:
634+
An object which implements
635+
securesystemslib.storage.StorageBackendInterface. When no object is
636+
passed a FilesystemBackend will be instantiated and used.
637+
613638
<Exceptions>
614639
securesystemslib.exceptions.FormatError, if the arguments are improperly
615640
formatted or the imported key object contains an invalid key type (i.e.,
@@ -634,6 +659,9 @@ def import_ed25519_privatekey_from_file(filepath, password=None, prompt=False):
634659
if password and prompt:
635660
raise ValueError("Passing 'password' and 'prompt' True is not allowed.")
636661

662+
if storage_backend is None:
663+
storage_backend = securesystemslib.storage.FilesystemBackend()
664+
637665
# If 'password' was passed check format and that it is not empty.
638666
if password is not None:
639667
securesystemslib.formats.PASSWORD_SCHEMA.check_match(password)
@@ -663,11 +691,12 @@ def import_ed25519_privatekey_from_file(filepath, password=None, prompt=False):
663691
password = None
664692

665693
# Finally, regardless of password, try decrypting the key, if necessary.
666-
# Otherwise, load it straight from the disk.
667-
with open(filepath, 'rb') as file_object:
694+
# Otherwise, load it straight from storage.
695+
with storage_backend.get(filepath) as file_object:
668696
json_str = file_object.read()
669-
return securesystemslib.keys.\
670-
import_ed25519key_from_private_json(json_str, password=password)
697+
698+
return securesystemslib.keys.\
699+
import_ed25519key_from_private_json(json_str, password=password)
671700

672701

673702

@@ -832,7 +861,8 @@ def import_ecdsa_publickey_from_file(filepath):
832861

833862

834863

835-
def import_ecdsa_privatekey_from_file(filepath, password=None):
864+
def import_ecdsa_privatekey_from_file(filepath, password=None,
865+
storage_backend=None):
836866
"""
837867
<Purpose>
838868
Import the encrypted ECDSA key file in 'filepath', decrypt it, and return
@@ -850,6 +880,11 @@ def import_ecdsa_privatekey_from_file(filepath, password=None):
850880
encrypted key file 'filepath' must be decrypted before the ECDSA key
851881
object can be returned.
852882
883+
storage_backend:
884+
An object which implements
885+
securesystemslib.storage.StorageBackendInterface. When no object is
886+
passed a FilesystemBackend will be instantiated and used.
887+
853888
<Exceptions>
854889
securesystemslib.exceptions.FormatError, if the arguments are improperly
855890
formatted or the imported key object contains an invalid key type (i.e.,
@@ -886,11 +921,14 @@ def import_ecdsa_privatekey_from_file(filepath, password=None):
886921
# Does 'password' have the correct format?
887922
securesystemslib.formats.PASSWORD_SCHEMA.check_match(password)
888923

924+
if storage_backend is None:
925+
storage_backend = securesystemslib.storage.FilesystemBackend()
926+
889927
# Store the encrypted contents of 'filepath' prior to calling the decryption
890928
# routine.
891929
encrypted_key = None
892930

893-
with open(filepath, 'rb') as file_object:
931+
with storage_backend.get(filepath) as file_object:
894932
encrypted_key = file_object.read()
895933

896934
# Decrypt the loaded key file, calling the 'cryptography' library to generate

0 commit comments

Comments
 (0)