Skip to content
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

Implement filesystem abstraction #232

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
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