Skip to content

Commit

Permalink
tests: add basic tests for dsse support
Browse files Browse the repository at this point in the history
* Add API tests for SimpleEnvelope
  This is not as comprehensive as Metadata API. The latter also includes
  tests for all payload classes, which should cover the same scenarios as
  if used with SimpleEnvelope.

* Add unit test for newly added simple envelope load helper function in
  trusted metadata set.

Signed-off-by: Lukas Puehringer <[email protected]>
  • Loading branch information
lukpueh committed Feb 21, 2024
1 parent 3da15e5 commit 77bd83a
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 1 deletion.
91 changes: 91 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import unittest
from copy import copy, deepcopy
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, ClassVar, Dict, Optional

from securesystemslib import exceptions as sslib_exceptions
Expand All @@ -33,6 +34,7 @@

from tests import utils
from tuf.api import exceptions
from tuf.api.dsse import SimpleEnvelope
from tuf.api.metadata import (
TOP_LEVEL_ROLE_NAMES,
DelegatedRole,
Expand Down Expand Up @@ -1144,6 +1146,95 @@ def test_delegations_get_delegated_role(self) -> None:
)


class TestSimpleEnvelope(unittest.TestCase):
"""Tests for public API in 'tuf/api/dsse.py'."""

@classmethod
def setUpClass(cls) -> None:
repo_data_dir = Path(utils.TESTS_DIR) / "repository_data"
cls.metadata_dir = repo_data_dir / "repository" / "metadata"
cls.signer_store = {}
for role in [Snapshot, Targets, Timestamp]:
key_path = repo_data_dir / "keystore" / f"{role.type}_key"
key = import_ed25519_privatekey_from_file(
str(key_path),
password="password",
)
cls.signer_store[role.type] = SSlibSigner(key)

def test_serialization(self) -> None:
"""Basic de/serialization test.
1. Load test metadata for each role
2. Wrap metadata payloads in envelope serializing the payload
3. Serialize envelope
4. De-serialize envelope
5. De-serialize payload
"""
for role in [Root, Timestamp, Snapshot, Targets]:
metadata_path = self.metadata_dir / f"{role.type}.json"
metadata = Metadata.from_file(str(metadata_path))
self.assertIsInstance(metadata.signed, role)

envelope = SimpleEnvelope.from_signed(metadata.signed)
envelope_bytes = envelope.to_bytes()

envelope2 = SimpleEnvelope.from_bytes(envelope_bytes)
payload = envelope2.get_signed()
self.assertEqual(metadata.signed, payload)

def test_fail_envelope_serialization(self) -> None:
envelope = SimpleEnvelope(b"foo", "bar", ["baz"])
with self.assertRaises(SerializationError):
envelope.to_bytes()

def test_fail_envelope_deserialization(self) -> None:
with self.assertRaises(DeserializationError):
SimpleEnvelope.from_bytes(b"[")

def test_fail_payload_serialization(self) -> None:
with self.assertRaises(SerializationError):
SimpleEnvelope.from_signed("foo") # type: ignore

def test_fail_payload_deserialization(self) -> None:
payloads = [b"[", b'{"_type": "foo"}']
for payload in payloads:
envelope = SimpleEnvelope(payload, "bar", [])
with self.assertRaises(DeserializationError):
envelope.get_signed()

def test_verify_delegate(self) -> None:
"""Basic verification test.
1. Load test metadata for each role
2. Wrap non-root payloads in envelope serializing the payload
3. Sign with correct delegated key
4. Verify delegate with root
"""
root_path = self.metadata_dir / "root.json"
root = Metadata[Root].from_file(str(root_path)).signed

for role in [Timestamp, Snapshot, Targets]:
metadata_path = self.metadata_dir / f"{role.type}.json"
metadata = Metadata.from_file(str(metadata_path))
self.assertIsInstance(metadata.signed, role)

signer = self.signer_store[role.type]
self.assertIn(
signer.key_dict["keyid"], root.roles[role.type].keyids
)

envelope = SimpleEnvelope.from_signed(metadata.signed)
envelope.sign(signer)
self.assertTrue(len(envelope.signatures) == 1)

root.verify_delegate(
role.type, envelope.pae(), envelope.signatures_dict
)


# Run unit test.
if __name__ == "__main__":
utils.configure_test_logging(sys.argv)
Expand Down
53 changes: 52 additions & 1 deletion tests/test_trusted_metadata_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from tests import utils
from tuf.api import exceptions
from tuf.api.dsse import SimpleEnvelope
from tuf.api.metadata import (
Metadata,
MetaFile,
Expand All @@ -25,7 +26,10 @@
Timestamp,
)
from tuf.api.serialization.json import JSONSerializer
from tuf.ngclient._internal.trusted_metadata_set import TrustedMetadataSet
from tuf.ngclient._internal.trusted_metadata_set import (
TrustedMetadataSet,
_load_from_simple_envelope,
)
from tuf.ngclient.config import EnvelopeType

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -490,6 +494,53 @@ def target_expired_modifier(target: Targets) -> None:

# TODO test updating over initial metadata (new keys, newer timestamp, etc)

def test_load_from_simple_envelope(self) -> None:
"""Basic unit test for ``_load_from_simple_envelope`` helper.
TODO: Test via trusted metadata set tests like for traditional metadata
"""
# pylint: disable=protected-access
metadata = Metadata.from_bytes(self.metadata[Root.type])
root = metadata.signed
envelope = SimpleEnvelope.from_signed(root)

# Unwrap unsigned envelope without verification
envelope_bytes = envelope.to_bytes()
payload_obj, signed_bytes, signatures = _load_from_simple_envelope(
Root, envelope_bytes
)

self.assertEqual(payload_obj, root)
self.assertEqual(signed_bytes, envelope.pae())
self.assertDictEqual(signatures, {})

# Unwrap correctly signed envelope (use default role name)
sig = envelope.sign(self.keystore[Root.type])
envelope_bytes = envelope.to_bytes()
_, _, signatures = _load_from_simple_envelope(
Root, envelope_bytes, root
)
self.assertDictEqual(signatures, {sig.keyid: sig})

# Load correctly signed envelope (with explicit role name)
_, _, signatures = _load_from_simple_envelope(
Root, envelope.to_bytes(), root, Root.type
)
self.assertDictEqual(signatures, {sig.keyid: sig})

# Fail load envelope with unexpected 'payload_type'
envelope_bad_type = SimpleEnvelope.from_signed(root)
envelope_bad_type.payload_type = "foo"
envelope_bad_type_bytes = envelope_bad_type.to_bytes()
with self.assertRaises(exceptions.RepositoryError):
_load_from_simple_envelope(Root, envelope_bad_type_bytes)

# Fail load envelope with unexpected payload type
envelope_bad_signed = SimpleEnvelope.from_signed(root)
envelope_bad_signed_bytes = envelope_bad_signed.to_bytes()
with self.assertRaises(exceptions.RepositoryError):
_load_from_simple_envelope(Targets, envelope_bad_signed_bytes)


if __name__ == "__main__":
utils.configure_test_logging(sys.argv)
Expand Down

0 comments on commit 77bd83a

Please sign in to comment.