Skip to content

Commit 94df81e

Browse files
author
Lukas Puehringer
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 <lukas.puehringer@nyu.edu>
1 parent 9370cc7 commit 94df81e

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
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,
@@ -1009,6 +1011,95 @@ def test_get_roles_in_succinct_roles(self) -> None:
10091011
self.assertEqual(role_name, f"bin-{expected_bin_suffix}")
10101012

10111013

1014+
class TestSimpleEnvelope(unittest.TestCase):
1015+
"""Tests for public API in 'tuf/api/dsse.py'."""
1016+
1017+
@classmethod
1018+
def setUpClass(cls) -> None:
1019+
repo_data_dir = Path(utils.TESTS_DIR) / "repository_data"
1020+
cls.metadata_dir = repo_data_dir / "repository" / "metadata"
1021+
cls.signer_store = {}
1022+
for role in [Snapshot, Targets, Timestamp]:
1023+
key_path = repo_data_dir / "keystore" / f"{role.type}_key"
1024+
key = import_ed25519_privatekey_from_file(
1025+
str(key_path),
1026+
password="password",
1027+
)
1028+
cls.signer_store[role.type] = SSlibSigner(key)
1029+
1030+
def test_serialization(self) -> None:
1031+
"""Basic de/serialization test.
1032+
1033+
1. Load test metadata for each role
1034+
2. Wrap metadata payloads in envelope serializing the payload
1035+
3. Serialize envelope
1036+
4. De-serialize envelope
1037+
5. De-serialize payload
1038+
1039+
"""
1040+
for role in [Root, Timestamp, Snapshot, Targets]:
1041+
metadata_path = self.metadata_dir / f"{role.type}.json"
1042+
metadata = Metadata.from_file(str(metadata_path))
1043+
self.assertIsInstance(metadata.signed, role)
1044+
1045+
envelope = SimpleEnvelope.from_signed(metadata.signed)
1046+
envelope_bytes = envelope.to_bytes()
1047+
1048+
envelope2 = SimpleEnvelope.from_bytes(envelope_bytes)
1049+
payload = envelope2.get_signed()
1050+
self.assertEqual(metadata.signed, payload)
1051+
1052+
def test_fail_envelope_serialization(self) -> None:
1053+
envelope = SimpleEnvelope(b"foo", "bar", ["baz"])
1054+
with self.assertRaises(SerializationError):
1055+
envelope.to_bytes()
1056+
1057+
def test_fail_envelope_deserialization(self) -> None:
1058+
with self.assertRaises(DeserializationError):
1059+
SimpleEnvelope.from_bytes(b"[")
1060+
1061+
def test_fail_payload_serialization(self) -> None:
1062+
with self.assertRaises(SerializationError):
1063+
SimpleEnvelope.from_signed("foo") # type: ignore
1064+
1065+
def test_fail_payload_deserialization(self) -> None:
1066+
payloads = [b"[", b'{"_type": "foo"}']
1067+
for payload in payloads:
1068+
envelope = SimpleEnvelope(payload, "bar", [])
1069+
with self.assertRaises(DeserializationError):
1070+
envelope.get_signed()
1071+
1072+
def test_verify_delegate(self) -> None:
1073+
"""Basic verification test.
1074+
1075+
1. Load test metadata for each role
1076+
2. Wrap non-root payloads in envelope serializing the payload
1077+
3. Sign with correct delegated key
1078+
4. Verify delegate with root
1079+
1080+
"""
1081+
root_path = self.metadata_dir / "root.json"
1082+
root = Metadata[Root].from_file(str(root_path)).signed
1083+
1084+
for role in [Timestamp, Snapshot, Targets]:
1085+
metadata_path = self.metadata_dir / f"{role.type}.json"
1086+
metadata = Metadata.from_file(str(metadata_path))
1087+
self.assertIsInstance(metadata.signed, role)
1088+
1089+
signer = self.signer_store[role.type]
1090+
self.assertIn(
1091+
signer.key_dict["keyid"], root.roles[role.type].keyids
1092+
)
1093+
1094+
envelope = SimpleEnvelope.from_signed(metadata.signed)
1095+
envelope.sign(signer)
1096+
self.assertTrue(len(envelope.signatures) == 1)
1097+
1098+
root.verify_delegate(
1099+
role.type, envelope.pae(), envelope.signatures_dict
1100+
)
1101+
1102+
10121103
# Run unit test.
10131104
if __name__ == "__main__":
10141105
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
@@ -14,6 +14,7 @@
1414

1515
from tests import utils
1616
from tuf.api import exceptions
17+
from tuf.api.dsse import SimpleEnvelope
1718
from tuf.api.metadata import (
1819
Metadata,
1920
MetaFile,
@@ -24,7 +25,10 @@
2425
Timestamp,
2526
)
2627
from tuf.api.serialization.json import JSONSerializer
27-
from tuf.ngclient._internal.trusted_metadata_set import TrustedMetadataSet
28+
from tuf.ngclient._internal.trusted_metadata_set import (
29+
TrustedMetadataSet,
30+
_load_from_simple_envelope,
31+
)
2832
from tuf.ngclient.config import EnvelopeType
2933

3034
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)