From 577e75a6aa73ead06e269edd0bc75e6d74620c18 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Mon, 13 Jun 2016 12:21:09 -0400 Subject: [PATCH] Add support for key expiration --- tuf/formats.py | 16 +++++---- tuf/repository_tool.py | 73 ++++++++++++++++++++++++++++++++++++------ tuf/schema.py | 2 +- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/tuf/formats.py b/tuf/formats.py index 135d7109..d47343ec 100755 --- a/tuf/formats.py +++ b/tuf/formats.py @@ -220,16 +220,18 @@ KEY_SCHEMA = SCHEMA.Object( object_name = 'KEY_SCHEMA', keytype = SCHEMA.AnyString(), - keyval = KEYVAL_SCHEMA) + keyval = KEYVAL_SCHEMA, + expires = SCHEMA.Optional(ISO8601_DATETIME_SCHEMA)) -# A TUF key object. This schema simplifies validation of keys that may be -# one of the supported key types. -# Supported key types: 'rsa', 'ed25519'. +# A TUF key object. This schema simplifies validation of keys that may be one +# of the supported key types. Supported key types: 'rsa', 'ed25519'. ANYKEY_SCHEMA = SCHEMA.Object( object_name = 'ANYKEY_SCHEMA', keytype = KEYTYPE_SCHEMA, keyid = KEYID_SCHEMA, - keyval = KEYVAL_SCHEMA) + keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA), + keyval = KEYVAL_SCHEMA, + expires = SCHEMA.Optional(ISO8601_DATETIME_SCHEMA)) # A list of TUF key objects. ANYKEYLIST_SCHEMA = SCHEMA.ListOf(ANYKEY_SCHEMA) @@ -239,6 +241,7 @@ object_name = 'RSAKEY_SCHEMA', keytype = SCHEMA.String('rsa'), keyid = KEYID_SCHEMA, + keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA), keyval = KEYVAL_SCHEMA) # An ED25519 raw public key, which must be 32 bytes. @@ -341,7 +344,8 @@ signed = SCHEMA.Any(), signatures = SCHEMA.ListOf(SIGNATURE_SCHEMA)) -# A dict where the dict keys hold a keyid and the dict values a key object. +# A dictionary where the dict keys hold a keyid and the dict values a key +# object. KEYDICT_SCHEMA = SCHEMA.DictOf( key_schema = KEYID_SCHEMA, value_schema = KEY_SCHEMA) diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 9ea637f3..2b6266cc 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -308,8 +308,8 @@ def write(self, write_partial=False, consistent_snapshot=False, def write_partial(self): """ - Write all the JSON Metadata objects to their corresponding files, but - allow metadata files to contain an invalid threshold of signatures. + Write all the JSON metadata to their corresponding files, but allow + metadata files to contain an invalid threshold of signatures. None. @@ -514,14 +514,14 @@ def __init__(self): - def add_verification_key(self, key): + def add_verification_key(self, key, expires=None): """ - Add 'key' to the role. Adding a key, which should contain only the public - portion, signifies the corresponding private key and signatures the role - is expected to provide. A threshold of signatures is required for a role - to be considered properly signed. If a metadata file contains an - insufficient threshold of signatures, it must not be accepted. + Add 'key' to the role. Adding a key, which should contain only the + public portion, signifies the corresponding private key and signatures + the role is expected to provide. A threshold of signatures is required + for a role to be considered properly signed. If a metadata file contains + an insufficient threshold of signatures, it must not be accepted. >>> >>> @@ -534,8 +534,15 @@ def add_verification_key(self, key): must generate and add its signature to the role. A threshold number of signatures is required for a role to be fully signed. + expires: + The date in which 'key' expires. 'expires' is a datetime.datetime() + object. + - tuf.FormatError, if the 'key' argument is improperly formatted. + tuf.FormatError, if any of the arguments are improperly formatted. + + tuf.Error, if the 'expires' datetime has already expired or the current + role's rolename is not recognized. The role's entries in 'tuf.keydb.py' and 'tuf.roledb.py' are updated. @@ -550,6 +557,52 @@ def add_verification_key(self, key): # Raise 'tuf.FormatError' if any are improperly formatted. tuf.formats.ANYKEY_SCHEMA.check_match(key) + # If 'expires' is unset, choose a default expiration for 'key'. By + # default, Root, Targets, Snapshot, and Timestamp keys are set to expire + # 1 year, 3 months, 1 week, and 1 day from the current time, respectively. + if expires is None: + if self.rolename == 'root': + expiration = \ + tuf.formats.unix_timestamp_to_datetime(int(time.time() + ROOT_EXPIRATION)) + + elif self.rolename == 'Targets': + expiration = \ + tuf.formats.unix_timestamp_to_datetime(int(time.time() + TARGETS_EXPIRATION)) + + elif self.rolename == 'Snapshot': + expiration = \ + tuf.formats.unix_timestamp_to_datetime(int(time.time() + SNAPSHOT_EXPIRATION)) + + elif self.rolename == 'Timestamp': + expiration = \ + tuf.formats.unix_timestamp_to_datetime(int(time.time() + TIMESTAMP_EXPIRATION)) + + else: + tuf.Error('The current role\'s rolename is not recognized.') + + expires = expiration.isoformat() + 'Z' + + # Is 'expires' a datetime.datetime() object? + # Raise 'tuf.FormatError' if not. + if not isinstance(expires, datetime.datetime): + raise tuf.FormatError(repr(expires) + ' is not a' + ' datetime.datetime() object.') + + # Truncate the microseconds value to produce a correct schema string + # of the form 'yyyy-mm-ddThh:mm:ssZ'. + expires = expires.replace(microsecond = 0) + + # Ensure the expiration has not already passed. + current_datetime = \ + tuf.formats.unix_timestamp_to_datetime(int(time.time())) + + if expires < current_datetime: + raise tuf.Error(repr(key) + ' has already expired.') + + # Update the key's 'expires' entry. + expires = datetime_object.isoformat() + 'Z' + key['expires'] = expires + # Ensure 'key', which should contain the public portion, is added to # 'tuf.keydb.py'. Add 'key' to the list of recognized keys. Keys may be # shared, so do not raise an exception if 'key' has already been loaded. @@ -567,7 +620,7 @@ def add_verification_key(self, key): roleinfo['keyids'].append(keyid) tuf.roledb.update_roleinfo(self._rolename, roleinfo) - + def remove_verification_key(self, key): diff --git a/tuf/schema.py b/tuf/schema.py index bbd1a5b1..5a98bff4 100755 --- a/tuf/schema.py +++ b/tuf/schema.py @@ -444,7 +444,7 @@ def __init__(self): def check_match(self, object): if not isinstance(object, bool): - raise tuf.FormatError('Got '+repr(object)+' instead of a boolean.') + raise tuf.FormatError('Got ' + repr(object) + ' instead of a boolean.')