Skip to content

Commit

Permalink
Merge pull request #232 from joshuagl/joshuagl/abstract-filesystem
Browse files Browse the repository at this point in the history
Implement filesystem abstraction
  • Loading branch information
SantiagoTorres authored May 7, 2020
2 parents 62936ca + c60ef22 commit 01a0c95
Show file tree
Hide file tree
Showing 8 changed files with 502 additions and 65 deletions.
4 changes: 4 additions & 0 deletions securesystemslib/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,7 @@ class InvalidConfigurationError(Error):
"""If a configuration object does not match the expected format."""
pass

class StorageError(Error):
"""Indicate an error occured during interaction with an abstracted storage
backend."""
pass
14 changes: 12 additions & 2 deletions securesystemslib/hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import securesystemslib.exceptions
import securesystemslib.formats
import securesystemslib.storage


DEFAULT_CHUNK_SIZE = 4096
Expand Down Expand Up @@ -314,7 +315,8 @@ def digest_fileobject(file_object, algorithm=DEFAULT_HASH_ALGORITHM,


def digest_filename(filename, algorithm=DEFAULT_HASH_ALGORITHM,
hash_library=DEFAULT_HASH_LIBRARY, normalize_line_endings=False):
hash_library=DEFAULT_HASH_LIBRARY, normalize_line_endings=False,
storage_backend=None):
"""
<Purpose>
Generate a digest object, update its hash using a file object
Expand All @@ -333,6 +335,11 @@ def digest_filename(filename, algorithm=DEFAULT_HASH_ALGORITHM,
normalize_line_endings:
Whether or not to normalize line endings for cross-platform support.
storage_backend:
An object which implements
securesystemslib.storage.StorageBackendInterface. When no object is
passed a FilesystemBackend will be instantiated and used.
<Exceptions>
securesystemslib.exceptions.FormatError, if the arguments are
improperly formatted.
Expand Down Expand Up @@ -361,8 +368,11 @@ def digest_filename(filename, algorithm=DEFAULT_HASH_ALGORITHM,

digest_object = None

if storage_backend is None:
storage_backend = securesystemslib.storage.FilesystemBackend()

# Open 'filename' in read+binary mode.
with open(filename, 'rb') as file_object:
with storage_backend.get(filename) as file_object:
# Create digest_object and update its hash data from file_object.
# digest_fileobject() raises:
# securesystemslib.exceptions.UnsupportedAlgorithmError
Expand Down
62 changes: 50 additions & 12 deletions securesystemslib/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

import securesystemslib.formats
import securesystemslib.settings
import securesystemslib.storage
import securesystemslib.util
import securesystemslib.keys

Expand All @@ -54,7 +55,7 @@
from colorama import Fore
TERM_RED = Fore.RED
TERM_RESET = Fore.RESET
except ImportError:
except ImportError: # pragma: no cover
logger.debug("Failed to find colorama module, terminal output won't be colored")
TERM_RED = ''
TERM_RESET = ''
Expand Down Expand Up @@ -242,7 +243,8 @@ def generate_and_write_rsa_keypair(filepath=None, bits=DEFAULT_RSA_KEY_BITS,


def import_rsa_privatekey_from_file(filepath, password=None,
scheme='rsassa-pss-sha256', prompt=False):
scheme='rsassa-pss-sha256', prompt=False,
storage_backend=None):
"""
<Purpose>
Import the PEM file in 'filepath' containing the private key.
Expand Down Expand Up @@ -272,6 +274,11 @@ def import_rsa_privatekey_from_file(filepath, password=None,
If True the user is prompted for a passphrase to decrypt 'filepath'.
Default is False.
storage_backend:
An object which implements
securesystemslib.storage.StorageBackendInterface. When no object is
passed a FilesystemBackend will be instantiated and used.
<Exceptions>
ValueError, if 'password' is passed and 'prompt' is True.
Expand Down Expand Up @@ -344,8 +351,11 @@ def import_rsa_privatekey_from_file(filepath, password=None,
logger.debug('No password was given. Attempting to import an'
' unencrypted file.')

if storage_backend is None:
storage_backend = securesystemslib.storage.FilesystemBackend()

# Read the contents of 'filepath' that should be a PEM formatted private key.
with open(filepath, 'rb') as file_object:
with storage_backend.get(filepath) as file_object:
pem_key = file_object.read().decode('utf-8')

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



def import_rsa_publickey_from_file(filepath, scheme='rsassa-pss-sha256'):
def import_rsa_publickey_from_file(filepath, scheme='rsassa-pss-sha256',
storage_backend=None):
"""
<Purpose>
Import the RSA key stored in 'filepath'. The key object returned is in the
Expand All @@ -374,6 +385,11 @@ def import_rsa_publickey_from_file(filepath, scheme='rsassa-pss-sha256'):
scheme:
The signature scheme used by the imported key.
storage_backend:
An object which implements
securesystemslib.storage.StorageBackendInterface. When no object is
passed a FilesystemBackend will be instantiated and used.
<Exceptions>
securesystemslib.exceptions.FormatError, if 'filepath' is improperly
formatted.
Expand All @@ -397,9 +413,12 @@ def import_rsa_publickey_from_file(filepath, scheme='rsassa-pss-sha256'):
# Is 'scheme' properly formatted?
securesystemslib.formats.RSA_SCHEME_SCHEMA.check_match(scheme)

if storage_backend is None:
storage_backend = securesystemslib.storage.FilesystemBackend()

# Read the contents of the key file that should be in PEM format and contains
# the public portion of the RSA key.
with open(filepath, 'rb') as file_object:
with storage_backend.get(filepath) as file_object:
rsa_pubkey_pem = file_object.read().decode('utf-8')

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



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

if storage_backend is None:
storage_backend = securesystemslib.storage.FilesystemBackend()

# If 'password' was passed check format and that it is not empty.
if password is not None:
securesystemslib.formats.PASSWORD_SCHEMA.check_match(password)
Expand Down Expand Up @@ -663,11 +691,12 @@ def import_ed25519_privatekey_from_file(filepath, password=None, prompt=False):
password = None

# Finally, regardless of password, try decrypting the key, if necessary.
# Otherwise, load it straight from the disk.
with open(filepath, 'rb') as file_object:
# Otherwise, load it straight from storage.
with storage_backend.get(filepath) as file_object:
json_str = file_object.read()
return securesystemslib.keys.\
import_ed25519key_from_private_json(json_str, password=password)

return securesystemslib.keys.\
import_ed25519key_from_private_json(json_str, password=password)



Expand Down Expand Up @@ -832,7 +861,8 @@ def import_ecdsa_publickey_from_file(filepath):



def import_ecdsa_privatekey_from_file(filepath, password=None):
def import_ecdsa_privatekey_from_file(filepath, password=None,
storage_backend=None):
"""
<Purpose>
Import the encrypted ECDSA key file in 'filepath', decrypt it, and return
Expand All @@ -850,6 +880,11 @@ def import_ecdsa_privatekey_from_file(filepath, password=None):
encrypted key file 'filepath' must be decrypted before the ECDSA key
object can be returned.
storage_backend:
An object which implements
securesystemslib.storage.StorageBackendInterface. When no object is
passed a FilesystemBackend will be instantiated and used.
<Exceptions>
securesystemslib.exceptions.FormatError, if the arguments are improperly
formatted or the imported key object contains an invalid key type (i.e.,
Expand Down Expand Up @@ -886,11 +921,14 @@ def import_ecdsa_privatekey_from_file(filepath, password=None):
# Does 'password' have the correct format?
securesystemslib.formats.PASSWORD_SCHEMA.check_match(password)

if storage_backend is None:
storage_backend = securesystemslib.storage.FilesystemBackend()

# Store the encrypted contents of 'filepath' prior to calling the decryption
# routine.
encrypted_key = None

with open(filepath, 'rb') as file_object:
with storage_backend.get(filepath) as file_object:
encrypted_key = file_object.read()

# Decrypt the loaded key file, calling the 'cryptography' library to generate
Expand Down
Loading

0 comments on commit 01a0c95

Please sign in to comment.