Metadata API: Make sure Signed.expires is UTC

* Most importantly use strftime() to serialize the datetime
* Force the timezone as UTC when deserializing

Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
This commit is contained in:
Jussi Kukkonen 2024-02-29 15:12:18 +02:00
parent 048d3b343b
commit c2edd30669
4 changed files with 11 additions and 10 deletions

View file

@ -5,7 +5,7 @@
import os
import sys
from datetime import datetime
from datetime import UTC, datetime
from typing import Dict, List, Optional
from securesystemslib.signer import SSlibKey, SSlibSigner
@ -48,8 +48,7 @@
}
)
expires_str = "2050-01-01T00:00:00Z"
EXPIRY = datetime.strptime(expires_str, "%Y-%m-%dT%H:%M:%SZ")
EXPIRY = datetime(2050, 1, 1, tzinfo=UTC)
OUT_DIR = "generated_data/ed25519_metadata"
if not os.path.exists(OUT_DIR):
os.mkdir(OUT_DIR)

View file

@ -313,7 +313,8 @@ def test_metadata_signed_is_expired(self) -> None:
snapshot_path = os.path.join(self.repo_dir, "metadata", "snapshot.json")
md = Metadata.from_file(snapshot_path)
self.assertEqual(md.signed.expires, datetime(2030, 1, 1, 0, 0))
expected_expiry = datetime(2030, 1, 1, 0, 0, tzinfo=timezone.utc)
self.assertEqual(md.signed.expires, expected_expiry)
# Test is_expired with reference_time provided
is_expired = md.signed.is_expired(md.signed.expires)

View file

@ -4,7 +4,7 @@
import os
import sys
import unittest
from datetime import datetime
from datetime import UTC, datetime
from typing import Callable, ClassVar, Dict, List, Optional, Tuple
from securesystemslib.interface import (
@ -279,7 +279,7 @@ def test_update_root_new_root_ver_same_as_trusted_root_ver(self) -> None:
def test_root_expired_final_root(self) -> None:
def root_expired_modifier(root: Root) -> None:
root.expires = datetime(1970, 1, 1)
root.expires = datetime(1970, 1, 1, tzinfo=UTC)
# intermediate root can be expired
root = self.modify_metadata(Root.type, root_expired_modifier)
@ -329,7 +329,7 @@ def bump_snapshot_version(timestamp: Timestamp) -> None:
def test_update_timestamp_expired(self) -> None:
# new_timestamp has expired
def timestamp_expired_modifier(timestamp: Timestamp) -> None:
timestamp.expires = datetime(1970, 1, 1)
timestamp.expires = datetime(1970, 1, 1, tzinfo=UTC)
# expired intermediate timestamp is loaded but raises
timestamp = self.modify_metadata(
@ -406,7 +406,7 @@ def test_update_snapshot_expired_new_snapshot(self) -> None:
self.trusted_set.update_timestamp(self.metadata[Timestamp.type])
def snapshot_expired_modifier(snapshot: Snapshot) -> None:
snapshot.expires = datetime(1970, 1, 1)
snapshot.expires = datetime(1970, 1, 1, tzinfo=UTC)
# expired intermediate snapshot is loaded but will raise
snapshot = self.modify_metadata(
@ -486,7 +486,7 @@ def test_update_targets_expired_new_target(self) -> None:
# new_delegated_target has expired
def target_expired_modifier(target: Targets) -> None:
target.expires = datetime(1970, 1, 1)
target.expires = datetime(1970, 1, 1, tzinfo=UTC)
targets = self.modify_metadata(Targets.type, target_expired_modifier)
with self.assertRaises(exceptions.ExpiredMetadataError):

View file

@ -176,6 +176,7 @@ def _common_fields_from_dict(
# what the constructor expects and what we store. The inverse operation
# is implemented in '_common_fields_to_dict'.
expires = datetime.strptime(expires_str, "%Y-%m-%dT%H:%M:%SZ")
expires = expires.replace(tzinfo=timezone.utc)
return version, spec_version, expires
@ -190,7 +191,7 @@ def _common_fields_to_dict(self) -> Dict[str, Any]:
"_type": self._type,
"version": self.version,
"spec_version": self.spec_version,
"expires": self.expires.isoformat() + "Z",
"expires": self.expires.strftime("%Y-%m-%dT%H:%M:%SZ"),
**self.unrecognized_fields,
}