diff --git a/tuf/formats.py b/tuf/formats.py index 975f78a9..e0905302 100755 --- a/tuf/formats.py +++ b/tuf/formats.py @@ -220,7 +220,8 @@ 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'. @@ -229,7 +230,8 @@ keytype = KEYTYPE_SCHEMA, keyid = KEYID_SCHEMA, keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA), - keyval = KEYVAL_SCHEMA) + keyval = KEYVAL_SCHEMA, + expires = SCHEMA.Optional(ISO8601_DATETIME_SCHEMA)) # A list of TUF key objects. ANYKEYLIST_SCHEMA = SCHEMA.ListOf(ANYKEY_SCHEMA) diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 5c099805..e78a6dcb 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -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.')