Skip to content

Commit 77bd83a

Browse files
committed
tests: add basic tests for dsse support
* 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]>
1 parent 3da15e5 commit 77bd83a

File tree

2 files changed

+143
-1
lines changed

2 files changed

+143
-1
lines changed

tests/test_api.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import unittest
1616
from copy import copy, deepcopy
1717
from datetime import datetime, timedelta
18+
from pathlib import Path
1819
from typing import Any, ClassVar, Dict, Optional
1920

2021
from securesystemslib import exceptions as sslib_exceptions
@@ -33,6 +34,7 @@
3334

3435
from tests import utils
3536
from tuf.api import exceptions
37+
from tuf.api.dsse import SimpleEnvelope
3638
from tuf.api.metadata import (
3739
TOP_LEVEL_ROLE_NAMES,
3840
DelegatedRole,
@@ -1144,6 +1146,95 @@ def test_delegations_get_delegated_role(self) -> None:
11441146
)
11451147

11461148

1149+
class TestSimpleEnvelope(unittest.TestCase):
1150+
"""Tests for public API in 'tuf/api/dsse.py'."""
1151+
1152+
@classmethod
1153+
def setUpClass(cls) -> None:
1154+
repo_data_dir = Path(utils.TESTS_DIR) / "repository_data"
1155+
cls.metadata_dir = repo_data_dir / "repository" / "metadata"
1156+
cls.signer_store = {}
1157+
for role in [Snapshot, Targets, Timestamp]:
1158+
key_path = repo_data_dir / "keystore" / f"{role.type}_key"
1159+
key = import_ed25519_privatekey_from_file(
1160+
str(key_path),
1161+
password="password",
1162+
)
1163+
cls.signer_store[role.type] = SSlibSigner(key)
1164+
1165+
def test_serialization(self) -> None:
1166+
"""Basic de/serialization test.
1167+
1168+
1. Load test metadata for each role
1169+
2. Wrap metadata payloads in envelope serializing the payload
1170+
3. Serialize envelope
1171+
4. De-serialize envelope
1172+
5. De-serialize payload
1173+
1174+
"""
1175+
for role in [Root, Timestamp, Snapshot, Targets]:
1176+
metadata_path = self.metadata_dir / f"{role.type}.json"
1177+
metadata = Metadata.from_file(str(metadata_path))
1178+
self.assertIsInstance(metadata.signed, role)
1179+
1180+
envelope = SimpleEnvelope.from_signed(metadata.signed)
1181+
envelope_bytes = envelope.to_bytes()
1182+
1183+
envelope2 = SimpleEnvelope.from_bytes(envelope_bytes)
1184+
payload = envelope2.get_signed()
1185+
self.assertEqual(metadata.signed, payload)
1186+
1187+
def test_fail_envelope_serialization(self) -> None:
1188+
envelope = SimpleEnvelope(b"foo", "bar", ["baz"])
1189+
with self.assertRaises(SerializationError):
1190+
envelope.to_bytes()
1191+
1192+
def test_fail_envelope_deserialization(self) -> None:
1193+
with self.assertRaises(DeserializationError):
1194+
SimpleEnvelope.from_bytes(b"[")
1195+
1196+
def test_fail_payload_serialization(self) -> None:
1197+
with self.assertRaises(SerializationError):
1198+
SimpleEnvelope.from_signed("foo") # type: ignore
1199+
1200+
def test_fail_payload_deserialization(self) -> None:
1201+
payloads = [b"[", b'{"_type": "foo"}']
1202+
for payload in payloads:
1203+
envelope = SimpleEnvelope(payload, "bar", [])
1204+
with self.assertRaises(DeserializationError):
1205+
envelope.get_signed()
1206+
1207+
def test_verify_delegate(self) -> None:
1208+
"""Basic verification test.
1209+
1210+
1. Load test metadata for each role
1211+
2. Wrap non-root payloads in envelope serializing the payload
1212+
3. Sign with correct delegated key
1213+
4. Verify delegate with root
1214+
1215+
"""
1216+
root_path = self.metadata_dir / "root.json"
1217+
root = Metadata[Root].from_file(str(root_path)).signed
1218+
1219+
for role in [Timestamp, Snapshot, Targets]:
1220+
metadata_path = self.metadata_dir / f"{role.type}.json"
1221+
metadata = Metadata.from_file(str(metadata_path))
1222+
self.assertIsInstance(metadata.signed, role)
1223+
1224+
signer = self.signer_store[role.type]
1225+
self.assertIn(
1226+
signer.key_dict["keyid"], root.roles[role.type].keyids
1227+
)
1228+
1229+
envelope = SimpleEnvelope.from_signed(metadata.signed)
1230+
envelope.sign(signer)
1231+
self.assertTrue(len(envelope.signatures) == 1)
1232+
1233+
root.verify_delegate(
1234+
role.type, envelope.pae(), envelope.signatures_dict
1235+
)
1236+
1237+
11471238
# Run unit test.
11481239
if __name__ == "__main__":
11491240
utils.configure_test_logging(sys.argv)

tests/test_trusted_metadata_set.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from tests import utils
1717
from tuf.api import exceptions
18+
from tuf.api.dsse import SimpleEnvelope
1819
from tuf.api.metadata import (
1920
Metadata,
2021
MetaFile,
@@ -25,7 +26,10 @@
2526
Timestamp,
2627
)
2728
from tuf.api.serialization.json import JSONSerializer
28-
from tuf.ngclient._internal.trusted_metadata_set import TrustedMetadataSet
29+
from tuf.ngclient._internal.trusted_metadata_set import (
30+
TrustedMetadataSet,
31+
_load_from_simple_envelope,
32+
)
2933
from tuf.ngclient.config import EnvelopeType
3034

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

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

497+
def test_load_from_simple_envelope(self) -> None:
498+
"""Basic unit test for ``_load_from_simple_envelope`` helper.
499+
500+
TODO: Test via trusted metadata set tests like for traditional metadata
501+
"""
502+
# pylint: disable=protected-access
503+
metadata = Metadata.from_bytes(self.metadata[Root.type])
504+
root = metadata.signed
505+
envelope = SimpleEnvelope.from_signed(root)
506+
507+
# Unwrap unsigned envelope without verification
508+
envelope_bytes = envelope.to_bytes()
509+
payload_obj, signed_bytes, signatures = _load_from_simple_envelope(
510+
Root, envelope_bytes
511+
)
512+
513+
self.assertEqual(payload_obj, root)
514+
self.assertEqual(signed_bytes, envelope.pae())
515+
self.assertDictEqual(signatures, {})
516+
517+
# Unwrap correctly signed envelope (use default role name)
518+
sig = envelope.sign(self.keystore[Root.type])
519+
envelope_bytes = envelope.to_bytes()
520+
_, _, signatures = _load_from_simple_envelope(
521+
Root, envelope_bytes, root
522+
)
523+
self.assertDictEqual(signatures, {sig.keyid: sig})
524+
525+
# Load correctly signed envelope (with explicit role name)
526+
_, _, signatures = _load_from_simple_envelope(
527+
Root, envelope.to_bytes(), root, Root.type
528+
)
529+
self.assertDictEqual(signatures, {sig.keyid: sig})
530+
531+
# Fail load envelope with unexpected 'payload_type'
532+
envelope_bad_type = SimpleEnvelope.from_signed(root)
533+
envelope_bad_type.payload_type = "foo"
534+
envelope_bad_type_bytes = envelope_bad_type.to_bytes()
535+
with self.assertRaises(exceptions.RepositoryError):
536+
_load_from_simple_envelope(Root, envelope_bad_type_bytes)
537+
538+
# Fail load envelope with unexpected payload type
539+
envelope_bad_signed = SimpleEnvelope.from_signed(root)
540+
envelope_bad_signed_bytes = envelope_bad_signed.to_bytes()
541+
with self.assertRaises(exceptions.RepositoryError):
542+
_load_from_simple_envelope(Targets, envelope_bad_signed_bytes)
543+
493544

494545
if __name__ == "__main__":
495546
utils.configure_test_logging(sys.argv)

0 commit comments

Comments
 (0)