mirror of
https://github.com/theupdateframework/python-tuf
synced 2026-05-24 10:08:28 +00:00
Continue updating the comments and docstrings of libtuf.py
This commit is contained in:
parent
21e9291ba7
commit
8d34b7aff8
1 changed files with 122 additions and 87 deletions
209
tuf/libtuf.py
209
tuf/libtuf.py
|
|
@ -824,6 +824,7 @@ def expiration(self):
|
|||
def expiration(self, expiration_datetime_utc):
|
||||
"""
|
||||
<Purpose>
|
||||
TODO: return 'input_datetime_utc' in ISO 8601 format.
|
||||
|
||||
>>>
|
||||
>>>
|
||||
|
|
@ -1043,10 +1044,11 @@ class Targets(Metadata):
|
|||
def __init__(self, targets_directory, rolename, roleinfo=None):
|
||||
|
||||
# Do the arguments have the correct format?
|
||||
# Raise 'tuf.FormatError' if any are improperly formatted.
|
||||
# This check ensures arguments have the appropriate number of objects and
|
||||
# object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.PATH_SCHEMA.check_match(targets_directory)
|
||||
tuf.formats.ROLENAME_SCHEMA.check_match(rolename)
|
||||
|
||||
if roleinfo is not None:
|
||||
tuf.formats.ROLEDB_SCHEMA.check_match(roleinfo)
|
||||
|
||||
|
|
@ -2073,6 +2075,16 @@ def generate_and_write_rsa_keypair(filepath, bits=DEFAULT_RSA_KEY_BITS,
|
|||
password=None):
|
||||
"""
|
||||
<Purpose>
|
||||
Generate an RSA key file, create an encrypted PEM string (using 'password'
|
||||
as the pass phrase), and store it in 'filepath'. The public key portion
|
||||
of the generated RSA key is stored in <'filepath'>.pub.
|
||||
|
||||
Which cryptography library performs the cryptographic decryption is
|
||||
determined by the string set in 'tuf.conf.RSA_CRYPTO_LIBRARY'. PyCrypto
|
||||
currently supported.
|
||||
|
||||
The PEM private key is encrypted with 3DES and CBC the mode of operation.
|
||||
The pass phrase is strengthened with PBKDF1-MD5.
|
||||
|
||||
<Arguments>
|
||||
filepath:
|
||||
|
|
@ -2083,7 +2095,8 @@ def generate_and_write_rsa_keypair(filepath, bits=DEFAULT_RSA_KEY_BITS,
|
|||
The number of bits of the generated RSA key.
|
||||
|
||||
password:
|
||||
|
||||
The passphrase to encrypt 'filepath'.
|
||||
|
||||
<Exceptions>
|
||||
tuf.FormatError, if the arguments are improperly formatted.
|
||||
|
||||
|
|
@ -2094,11 +2107,11 @@ def generate_and_write_rsa_keypair(filepath, bits=DEFAULT_RSA_KEY_BITS,
|
|||
None.
|
||||
"""
|
||||
|
||||
# Does 'filepath' have the correct format?
|
||||
# Raise 'tuf.FormatError' if there is a mismatch.
|
||||
# Do the arguments have the correct format?
|
||||
# This check ensures arguments have the appropriate number of objects and
|
||||
# object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.PATH_SCHEMA.check_match(filepath)
|
||||
|
||||
# Does 'bits' have the correct format?
|
||||
tuf.formats.RSAKEYBITS_SCHEMA.check_match(bits)
|
||||
|
||||
# If the caller does not provide a password argument, prompt for one.
|
||||
|
|
@ -2108,20 +2121,24 @@ def generate_and_write_rsa_keypair(filepath, bits=DEFAULT_RSA_KEY_BITS,
|
|||
|
||||
# Does 'password' have the correct format?
|
||||
tuf.formats.PASSWORD_SCHEMA.check_match(password)
|
||||
|
||||
|
||||
# Generate public and private RSA keys, encrypt the private portion, and
|
||||
# store them in PEM format.
|
||||
rsa_key = tuf.keys.generate_rsa_key(bits)
|
||||
public = rsa_key['keyval']['public']
|
||||
private = rsa_key['keyval']['private']
|
||||
encrypted_pem = tuf.keys.create_rsa_encrypted_pem(private, password)
|
||||
|
||||
# Write public key (i.e., 'public', which is in PEM format) to
|
||||
# '<filepath>.pub'.
|
||||
# '<filepath>.pub'. If the parent directory of filepath does not exist,
|
||||
# create it, and all parent directories if necessary.
|
||||
tuf.util.ensure_parent_dir(filepath)
|
||||
|
||||
with open(filepath+'.pub', 'w') as file_object:
|
||||
file_object.write(public)
|
||||
|
||||
# Write the private key in encrypted PEM format to '<filepath>'.
|
||||
# Unlike the public key file, the private key does not have a file extension.
|
||||
with open(filepath, 'w') as file_object:
|
||||
file_object.write(encrypted_pem)
|
||||
|
||||
|
|
@ -2132,23 +2149,38 @@ def generate_and_write_rsa_keypair(filepath, bits=DEFAULT_RSA_KEY_BITS,
|
|||
def import_rsa_privatekey_from_file(filepath, password=None):
|
||||
"""
|
||||
<Purpose>
|
||||
Import the encrypted PEM file in 'filepath', decrypt it, and return the key
|
||||
object in 'tuf.formats.RSAKEY_SCHEMA' format.
|
||||
|
||||
Which cryptography library performs the cryptographic decryption is
|
||||
determined by the string set in 'tuf.conf.RSA_CRYPTO_LIBRARY'. PyCrypto
|
||||
currently supported.
|
||||
|
||||
The PEM private key is encrypted with 3DES and CBC the mode of operation.
|
||||
The pass phrase is strengthened with PBKDF1-MD5.
|
||||
|
||||
<Arguments>
|
||||
filepath:
|
||||
<filepath> file, an RSA encrypted PEM file.
|
||||
<filepath> file, an RSA encrypted PEM file. Unlike the public RSA PEM
|
||||
key file, 'filepath' does not have an extension.
|
||||
|
||||
password:
|
||||
The passphrase to decrypt 'filepath'.
|
||||
|
||||
<Exceptions>
|
||||
tuf.FormatError, if 'filepath' is improperly formatted.
|
||||
|
||||
<Side Effects>
|
||||
The contents of 'filepath' is read, decrypted, and the key stored.
|
||||
|
||||
<Returns>
|
||||
An RSA key object, conformant to 'tuf.formats.RSAKEY_SCHEMA'.
|
||||
"""
|
||||
|
||||
# Does 'filepath' have the correct format?
|
||||
# Raise 'tuf.FormatError' if there is a mismatch.
|
||||
# This check ensures arguments have the appropriate number of objects and
|
||||
# object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.PATH_SCHEMA.check_match(filepath)
|
||||
|
||||
# If the caller does not provide a password argument, prompt for one.
|
||||
|
|
@ -2161,9 +2193,11 @@ def import_rsa_privatekey_from_file(filepath, password=None):
|
|||
|
||||
encrypted_pem = None
|
||||
|
||||
# Read the contents of 'filepath' that should be an encrypted PEM.
|
||||
with open(filepath, 'rb') as file_object:
|
||||
encrypted_pem = file_object.read()
|
||||
|
||||
# Convert 'rsa_pubkey_pem' in 'tuf.formats.RSAKEY_SCHEMA' format.
|
||||
rsa_key = tuf.keys.import_rsakey_from_encrypted_pem(encrypted_pem, password)
|
||||
|
||||
return rsa_key
|
||||
|
|
@ -2175,8 +2209,14 @@ def import_rsa_privatekey_from_file(filepath, password=None):
|
|||
def import_rsa_publickey_from_file(filepath):
|
||||
"""
|
||||
<Purpose>
|
||||
If the RSA PEM in 'filepath' contains a private key, it is discarded.
|
||||
|
||||
Import the RSA key stored in 'filepath'. The key object returned is a TUF
|
||||
key, specifically 'tuf.formats.RSAKEY_SCHEMA'. If the RSA PEM in 'filepath'
|
||||
contains a private key, it is discarded.
|
||||
|
||||
Which cryptography library performs the cryptographic decryption is
|
||||
determined by the string set in 'tuf.conf.RSA_CRYPTO_LIBRARY'. PyCrypto
|
||||
currently supported.
|
||||
|
||||
<Arguments>
|
||||
filepath:
|
||||
<filepath>.pub file, an RSA PEM file.
|
||||
|
|
@ -2185,18 +2225,24 @@ def import_rsa_publickey_from_file(filepath):
|
|||
tuf.FormatError, if 'filepath' is improperly formatted.
|
||||
|
||||
<Side Effects>
|
||||
|
||||
'filepath' is read and its contents extracted.
|
||||
|
||||
<Returns>
|
||||
An RSA key object conformant to 'tuf.formats.RSAKEY_SCHEMA'.
|
||||
"""
|
||||
|
||||
# Does 'filepath' have the correct format?
|
||||
# Raise 'tuf.FormatError' if there is a mismatch.
|
||||
# This check ensures arguments have the appropriate number of objects and
|
||||
# object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.PATH_SCHEMA.check_match(filepath)
|
||||
|
||||
# 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, 'r+b') as file_object:
|
||||
rsa_pubkey_pem = file_object.read()
|
||||
|
||||
# Convert 'rsa_pubkey_pem' in 'tuf.formats.RSAKEY_SCHEMA' format.
|
||||
rsakey_dict = tuf.keys.format_rsakey_from_pem(rsa_pubkey_pem)
|
||||
|
||||
return rsakey_dict
|
||||
|
|
@ -2205,44 +2251,11 @@ def import_rsa_publickey_from_file(filepath):
|
|||
|
||||
|
||||
|
||||
def expiration_datetime_utc(input_datetime_utc):
|
||||
"""
|
||||
<Purpose>
|
||||
TODO: return 'input_datetime_utc' in ISO 8601 format.
|
||||
|
||||
<Arguments>
|
||||
input_datetime_utc:
|
||||
|
||||
<Exceptions>
|
||||
tuf.FormatError, if 'input_datetime_utc' is invalid.
|
||||
|
||||
<Side Effects>
|
||||
None.
|
||||
|
||||
<Returns>
|
||||
"""
|
||||
if not tuf.formats.DATETIME_SCHEMA.matches(input_datetime_utc):
|
||||
message = 'The datetime argument must be in "YYYY-MM-DD HH:MM:SS" format.'
|
||||
raise tuf.FormatError(message)
|
||||
try:
|
||||
unix_timestamp = tuf.formats.parse_time(input_datetime_utc+' UTC')
|
||||
except (tuf.FormatError, ValueError), e:
|
||||
raise tuf.FormatError('Invalid date entered.')
|
||||
|
||||
if unix_timestamp < time.time():
|
||||
message = 'The expiration date must occur after the current date.'
|
||||
raise tuf.FormatError(message)
|
||||
|
||||
return input_datetime_utc+' UTC'
|
||||
|
||||
|
||||
|
||||
|
||||
def get_metadata_filenames(metadata_directory=None):
|
||||
"""
|
||||
<Purpose>
|
||||
Return a dictionary containing the filenames of the top-level roles.
|
||||
If 'metadata_directory' is set to 'metadata', the dictionary
|
||||
If 'metadata_directory' were set to 'metadata', the dictionary
|
||||
returned would contain:
|
||||
|
||||
filenames = {'root': 'metadata/root.txt',
|
||||
|
|
@ -2272,10 +2285,15 @@ def get_metadata_filenames(metadata_directory=None):
|
|||
metadata_directory = '.'
|
||||
|
||||
# Does 'metadata_directory' have the correct format?
|
||||
# Raise 'tuf.FormatError' if there is a mismatch.
|
||||
# This check ensures arguments have the appropriate number of objects and
|
||||
# object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.PATH_SCHEMA.check_match(metadata_directory)
|
||||
|
||||
# Store the filepaths of the top-level roles, including the
|
||||
# 'metadata_directory' for each one.
|
||||
filenames = {}
|
||||
|
||||
filenames[ROOT_FILENAME] = \
|
||||
os.path.join(metadata_directory, ROOT_FILENAME)
|
||||
|
||||
|
|
@ -2297,7 +2315,7 @@ def get_metadata_filenames(metadata_directory=None):
|
|||
def get_metadata_file_info(filename):
|
||||
"""
|
||||
<Purpose>
|
||||
Retrieve the file information for 'filename'. The object returned
|
||||
Retrieve the file information of 'filename'. The object returned
|
||||
conforms to 'tuf.formats.FILEINFO_SCHEMA'. The information
|
||||
generated for 'filename' is stored in metadata files like 'targets.txt'.
|
||||
The fileinfo object returned has the form:
|
||||
|
|
@ -2307,7 +2325,7 @@ def get_metadata_file_info(filename):
|
|||
|
||||
<Arguments>
|
||||
filename:
|
||||
The metadata file whose file information is needed.
|
||||
The metadata file whose file information is needed. It must exist.
|
||||
|
||||
<Exceptions>
|
||||
tuf.FormatError, if 'filename' is improperly formatted.
|
||||
|
|
@ -2320,12 +2338,14 @@ def get_metadata_file_info(filename):
|
|||
|
||||
<Returns>
|
||||
A dictionary conformant to 'tuf.formats.FILEINFO_SCHEMA'. This
|
||||
dictionary contains the length, hashes, and custom data about
|
||||
the 'filename' metadata file.
|
||||
dictionary contains the length, hashes, and custom data about the
|
||||
'filename' metadata file.
|
||||
"""
|
||||
|
||||
# Does 'filename' have the correct format?
|
||||
# Raise 'tuf.FormatError' if there is a mismatch.
|
||||
# This check ensures arguments have the appropriate number of objects and
|
||||
# object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.PATH_SCHEMA.check_match(filename)
|
||||
|
||||
if not os.path.isfile(filename):
|
||||
|
|
@ -2350,7 +2370,7 @@ def generate_root_metadata(version, expiration_date):
|
|||
"""
|
||||
<Purpose>
|
||||
Create the root metadata. 'tuf.roledb.py' and 'tuf.keydb.py' are read and
|
||||
the information returned by these modules are used to generate the root
|
||||
the information returned by these modules is used to generate the root
|
||||
metadata object.
|
||||
|
||||
<Arguments>
|
||||
|
|
@ -2360,6 +2380,8 @@ def generate_root_metadata(version, expiration_date):
|
|||
trusted.
|
||||
|
||||
expiration_date:
|
||||
The expiration date, in UTC, of the metadata file. Conformant to
|
||||
'tuf.formats.TIME_SCHEMA'.
|
||||
|
||||
<Exceptions>
|
||||
tuf.FormatError, if the generated root metadata object could not
|
||||
|
|
@ -2376,23 +2398,29 @@ def generate_root_metadata(version, expiration_date):
|
|||
"""
|
||||
|
||||
# Do the arguments have the correct format?
|
||||
# Raise 'tuf.FormatError' if any of the arguments are improperly formatted.
|
||||
# This check ensures arguments have the appropriate number of objects and
|
||||
# object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.METADATAVERSION_SCHEMA.check_match(version)
|
||||
tuf.formats.TIME_SCHEMA.check_match(expiration_date)
|
||||
|
||||
# The role and key dictionaries to be saved in the root metadata object.
|
||||
# Conformant to 'ROLEDICT_SCHEMA' and 'KEYDICT_SCHEMA', respectively.
|
||||
roledict = {}
|
||||
keydict = {}
|
||||
|
||||
# Extract the role, threshold, and keyid information from the config.
|
||||
# The necessary role metadata is generated from this information.
|
||||
# Extract the role, threshold, and keyid information of the top-level roles,
|
||||
# which Root stores in its metadata. The necessary role metadata is generated
|
||||
# from this information.
|
||||
for rolename in ['root', 'targets', 'release', 'timestamp']:
|
||||
|
||||
# If a top-level role is missing from 'tuf.roledb.py', raise an exception.
|
||||
if not tuf.roledb.role_exists(rolename):
|
||||
raise tuf.Error(repr(rolename)+' not in "tuf.roledb".')
|
||||
|
||||
|
||||
# Keep track of the keys loaded so that duplicates is avoided.
|
||||
keyids = []
|
||||
|
||||
# Generate keys for the keyids listed by the role being processed.
|
||||
for keyid in tuf.roledb.get_role_keyids(rolename):
|
||||
key = tuf.keydb.get_key(keyid)
|
||||
|
|
@ -2443,41 +2471,39 @@ def generate_targets_metadata(targets_directory, target_files, version,
|
|||
expiration_date, delegations=None):
|
||||
"""
|
||||
<Purpose>
|
||||
Generate the targets metadata object. The targets must exist at the same
|
||||
path they should on the repo. 'target_files' is a list of targets. We're
|
||||
not worrying about custom metadata at the moment. It is allowed to not
|
||||
provide keys.
|
||||
Generate the targets metadata object. The targets in 'target_files' must
|
||||
exist at the corresponding path on the repository targets directory.
|
||||
'target_files' is a list of targets. The 'custom' field of the targets
|
||||
metadata is not currently supported.
|
||||
|
||||
<Arguments>
|
||||
targets_directory:
|
||||
The directory (absolute path) containing the target files and directories.
|
||||
The directory containing the target files and directories of the
|
||||
repository.
|
||||
|
||||
target_files:
|
||||
The target files tracked by 'targets.txt'. 'target_files' is a list of
|
||||
paths/directories of target files that are relative to the targets
|
||||
directory (e.g., ['file1.txt', 'Django/module.py']). If the target files
|
||||
are saved in
|
||||
the root folder 'targets' on the repository, then 'targets' must be
|
||||
included in the target paths. The repository does not have to name
|
||||
this folder 'targets'.
|
||||
|
||||
The target files listed in 'targets.txt'. 'target_files' is a list of
|
||||
target paths that are relative to the targets directory
|
||||
(e.g., ['file1.txt', 'Django/module.py']).
|
||||
|
||||
version:
|
||||
The metadata version number. Clients use the version number to
|
||||
determine if the downloaded version is newer than the one currently
|
||||
trusted.
|
||||
|
||||
expiration_date:
|
||||
The expiration date, in UTC, of the metadata file.
|
||||
Conformant to 'tuf.formats.TIME_SCHEMA'.
|
||||
The expiration date, in UTC, of the metadata file. Conformant to
|
||||
'tuf.formats.TIME_SCHEMA'.
|
||||
|
||||
delegations:
|
||||
|
||||
The delegations made by the targets role to be generated. 'delegations'
|
||||
must match 'tuf.formats.DELEGATIONS_SCHEMA'.
|
||||
|
||||
<Exceptions>
|
||||
tuf.FormatError, if an error occurred trying to generate the targets
|
||||
metadata object.
|
||||
|
||||
tuf.Error, if any of the target files could not be read.
|
||||
tuf.Error, if any of the target files cannot be read.
|
||||
|
||||
<Side Effects>
|
||||
The target files are read and file information generated about them.
|
||||
|
|
@ -2486,33 +2512,42 @@ def generate_targets_metadata(targets_directory, target_files, version,
|
|||
A targets metadata object, conformant to 'tuf.formats.TARGETS_SCHEMA'.
|
||||
"""
|
||||
|
||||
# Do the arguments have the correct format.
|
||||
# Raise 'tuf.FormatError' if there is a mismatch.
|
||||
# Do the arguments have the correct format?
|
||||
# This check ensures arguments have the appropriate number of objects and
|
||||
# object types, and that all dict keys are properly named.
|
||||
# Raise 'tuf.FormatError' if the check fails.
|
||||
tuf.formats.PATH_SCHEMA.check_match(targets_directory)
|
||||
tuf.formats.PATHS_SCHEMA.check_match(target_files)
|
||||
tuf.formats.METADATAVERSION_SCHEMA.check_match(version)
|
||||
tuf.formats.TIME_SCHEMA.check_match(expiration_date)
|
||||
|
||||
if delegations is not None:
|
||||
tuf.formats.DELEGATIONS_SCHEMA.check_match(delegations)
|
||||
|
||||
|
||||
# Store the file attributes of the targets in 'target_files'. 'filedict',
|
||||
# conformant to 'tuf.formats.FILEDICT_SCHEMA', is added the targes metadata
|
||||
# object returned.
|
||||
filedict = {}
|
||||
|
||||
# Ensure the user is aware of a non-existent 'targets_directory', and convert
|
||||
# it to its absolute path if it exists.
|
||||
targets_directory = _check_directory(targets_directory)
|
||||
|
||||
# Generate the file info for all the target files listed in 'target_files'.
|
||||
# Generate the fileinfo for all the target files listed in 'target_files'.
|
||||
for target in target_files:
|
||||
|
||||
# Strip 'targets/' from from 'target' and keep the rest (e.g.,
|
||||
# 'targets/more_targets/somefile.txt' -> 'more_targets/somefile.txt'
|
||||
#relative_targetpath = os.path.sep.join(target.split(os.path.sep)[1:])
|
||||
# The root folder of the targets directory should not be included
|
||||
# (e.g., 'targets/more_targets/somefile.txt' -> 'more_targets/somefile.txt')
|
||||
relative_targetpath = target
|
||||
target_path = os.path.join(targets_directory, target)
|
||||
|
||||
|
||||
# Ensure all target files listed in 'target_files' exist. If just one of
|
||||
# these files does not exist, raise an exception.
|
||||
if not os.path.exists(target_path):
|
||||
message = repr(target_path)+' cannot be read. Unable to generate '+ \
|
||||
'targets metadata.'
|
||||
raise tuf.Error(message)
|
||||
|
||||
|
||||
# Update 'filedict' with the file attributes of 'target_path'.
|
||||
filedict[relative_targetpath] = get_metadata_file_info(target_path)
|
||||
|
||||
# Generate the targets metadata object.
|
||||
|
|
|
|||
Loading…
Reference in a new issue