Skip to content

Commit 1897f9a

Browse files
author
Lukas Puehringer
committed
ngclient: change envelope type config to flag
The flag allows adding other envelope types in the future (unlikely), or parallel support (`METADATA & SIMPLE`) without breaking the API. Internally, the flag is now just passed on to TrustedMetadataSet as mandatory parameter. (Optional parameters make less sense when we control all the invocations.) This change requires updating all invocations of TrustedMetadataSet, including the duplication of a test function. Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
1 parent 8544bbd commit 1897f9a

5 files changed

Lines changed: 78 additions & 36 deletions

File tree

examples/client/client

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ from urllib import request
1515

1616
from tuf.api.exceptions import DownloadError, RepositoryError
1717
from tuf.ngclient import Updater, UpdaterConfig
18+
from tuf.ngclient.config import EnvelopeType
1819

1920
# constants
2021
DOWNLOAD_DIR = "./downloads"
@@ -74,7 +75,7 @@ def download(base_url: str, target: str, use_dsse: bool) -> bool:
7475
os.mkdir(DOWNLOAD_DIR)
7576

7677
config = UpdaterConfig()
77-
config.use_dsse = use_dsse
78+
config.envelope_type = EnvelopeType.SIMPLE
7879

7980
try:
8081
updater = Updater(

tests/test_trusted_metadata_set.py

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
)
2727
from tuf.api.serialization.json import JSONSerializer
2828
from tuf.ngclient._internal.trusted_metadata_set import TrustedMetadataSet
29+
from tuf.ngclient.config import EnvelopeType
2930

3031
logger = logging.getLogger(__name__)
3132

@@ -94,7 +95,9 @@ def hashes_length_modifier(timestamp: Timestamp) -> None:
9495
)
9596

9697
def setUp(self) -> None:
97-
self.trusted_set = TrustedMetadataSet(self.metadata[Root.type])
98+
self.trusted_set = TrustedMetadataSet(
99+
self.metadata[Root.type], EnvelopeType.METADATA
100+
)
98101

99102
def _update_all_besides_targets(
100103
self,
@@ -193,22 +196,37 @@ def test_out_of_order_ops(self) -> None:
193196
self.metadata["role1"], "role1", Targets.type
194197
)
195198

196-
def test_root_with_invalid_json(self) -> None:
197-
# Test loading initial root and root update
198-
for test_func in [TrustedMetadataSet, self.trusted_set.update_root]:
199-
# root is not json
200-
with self.assertRaises(exceptions.RepositoryError):
201-
test_func(b"") # type: ignore[operator]
199+
def test_bad_initial_root(self) -> None:
200+
# root is not json
201+
with self.assertRaises(exceptions.RepositoryError):
202+
TrustedMetadataSet(b"", EnvelopeType.METADATA)
202203

203-
# root is invalid
204-
root = Metadata.from_bytes(self.metadata[Root.type])
205-
root.signed.version += 1
206-
with self.assertRaises(exceptions.UnsignedMetadataError):
207-
test_func(root.to_bytes()) # type: ignore[operator]
204+
# root is invalid
205+
root = Metadata.from_bytes(self.metadata[Root.type])
206+
root.signed.version += 1
207+
with self.assertRaises(exceptions.UnsignedMetadataError):
208+
TrustedMetadataSet(root.to_bytes(), EnvelopeType.METADATA)
208209

209-
# metadata is of wrong type
210-
with self.assertRaises(exceptions.RepositoryError):
211-
test_func(self.metadata[Snapshot.type]) # type: ignore[operator]
210+
# metadata is of wrong type
211+
with self.assertRaises(exceptions.RepositoryError):
212+
TrustedMetadataSet(
213+
self.metadata[Snapshot.type], EnvelopeType.METADATA
214+
)
215+
216+
def test_bad_root_update(self) -> None:
217+
# root is not json
218+
with self.assertRaises(exceptions.RepositoryError):
219+
self.trusted_set.update_root(b"")
220+
221+
# root is invalid
222+
root = Metadata.from_bytes(self.metadata[Root.type])
223+
root.signed.version += 1
224+
with self.assertRaises(exceptions.UnsignedMetadataError):
225+
self.trusted_set.update_root(root.to_bytes())
226+
227+
# metadata is of wrong type
228+
with self.assertRaises(exceptions.RepositoryError):
229+
self.trusted_set.update_root(self.metadata[Snapshot.type])
212230

213231
def test_top_level_md_with_invalid_json(self) -> None:
214232
top_level_md: List[Tuple[bytes, Callable[[bytes], Signed]]] = [
@@ -261,7 +279,7 @@ def root_expired_modifier(root: Root) -> None:
261279

262280
# intermediate root can be expired
263281
root = self.modify_metadata(Root.type, root_expired_modifier)
264-
tmp_trusted_set = TrustedMetadataSet(root)
282+
tmp_trusted_set = TrustedMetadataSet(root, EnvelopeType.METADATA)
265283
# update timestamp to trigger final root expiry check
266284
with self.assertRaises(exceptions.ExpiredMetadataError):
267285
tmp_trusted_set.update_timestamp(self.metadata[Timestamp.type])

tuf/ngclient/_internal/trusted_metadata_set.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
3535
>>> # Load local root (RepositoryErrors here stop the update)
3636
>>> with open(root_path, "rb") as f:
37-
>>> trusted_set = TrustedMetadataSet(f.read())
37+
>>> trusted_set = TrustedMetadataSet(f.read(), EnvelopeType.METADATA)
3838
>>>
3939
>>> # update root from remote until no more are available
4040
>>> with download(Root.type, trusted_set.root.version + 1) as f:
@@ -68,7 +68,12 @@
6868

6969
from tuf.api import exceptions
7070
from tuf.api.metadata import Root, Signed, Snapshot, Targets, Timestamp
71-
from tuf.ngclient._internal.wrapping import MetadataUnwrapper, Unwrapper
71+
from tuf.ngclient._internal.wrapping import (
72+
EnvelopeUnwrapper,
73+
MetadataUnwrapper,
74+
Unwrapper,
75+
)
76+
from tuf.ngclient.config import EnvelopeType
7277

7378
logger = logging.getLogger(__name__)
7479

@@ -82,22 +87,26 @@ class TrustedMetadataSet(abc.Mapping):
8287
what is updated.
8388
"""
8489

85-
def __init__(self, root_data: bytes, unwrapper: Optional[Unwrapper] = None):
90+
def __init__(self, root_data: bytes, envelope_type: EnvelopeType):
8691
"""Initialize ``TrustedMetadataSet`` by loading trusted root metadata.
8792
8893
Args:
8994
root_data: Trusted root metadata as bytes. Note that this metadata
9095
will only be verified by itself: it is the source of trust for
9196
all metadata in the ``TrustedMetadataSet``
92-
unwrapper: Used to unwrap and verify metadata. Default is
93-
MetadataUnwrapper.
97+
envelope_type: Configures deserialization and verification mode of
98+
TUF metadata.
9499
95100
Raises:
96101
RepositoryError: Metadata failed to load or verify. The actual
97102
error type and content will contain more details.
98103
"""
99-
if unwrapper is None:
104+
unwrapper: Unwrapper
105+
if envelope_type is EnvelopeType.SIMPLE:
106+
unwrapper = EnvelopeUnwrapper()
107+
else:
100108
unwrapper = MetadataUnwrapper()
109+
101110
self._unwrapper = unwrapper
102111

103112
self._trusted_set: Dict[str, Signed] = {}

tuf/ngclient/config.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@
55
"""
66

77
from dataclasses import dataclass
8+
from enum import Flag, unique
9+
10+
11+
@unique
12+
class EnvelopeType(Flag):
13+
"""Configures deserialization and verification mode of TUF metadata.
14+
15+
Args:
16+
METADATA: Traditional canonical JSON -based TUF Metadata.
17+
SIMPLE: Dead Simple Signing Envelope. (experimental)
18+
"""
19+
20+
METADATA = 1
21+
SIMPLE = 2
822

923

1024
@dataclass
@@ -23,8 +37,9 @@ class UpdaterConfig:
2337
are used, target download URLs are formed by prefixing the filename
2438
with a hash digest of file content by default. This can be
2539
overridden by setting ``prefix_targets_with_hash`` to ``False``.
26-
use_dsse: If true, expect metadata in a DSSE Envelope. Use
27-
traditional Metadata (canonical json) otherwise.
40+
envelope_type: Configures deserialization and verification mode of TUF
41+
metadata. Per default, it is treated as traditional canonical JSON
42+
-based TUF Metadata.
2843
"""
2944

3045
max_root_rotations: int = 32
@@ -34,4 +49,4 @@ class UpdaterConfig:
3449
snapshot_max_length: int = 2000000 # bytes
3550
targets_max_length: int = 5000000 # bytes
3651
prefix_targets_with_hash: bool = True
37-
use_dsse: bool = False
52+
envelope_type: EnvelopeType = EnvelopeType.METADATA

tuf/ngclient/updater.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,8 @@
4646

4747
from tuf.api import exceptions
4848
from tuf.api.metadata import Root, Snapshot, TargetFile, Targets, Timestamp
49-
from tuf.ngclient._internal import (
50-
requests_fetcher,
51-
trusted_metadata_set,
52-
wrapping,
53-
)
54-
from tuf.ngclient.config import UpdaterConfig
49+
from tuf.ngclient._internal import requests_fetcher, trusted_metadata_set
50+
from tuf.ngclient.config import EnvelopeType, UpdaterConfig
5551
from tuf.ngclient.fetcher import FetcherInterface
5652

5753
logger = logging.getLogger(__name__)
@@ -101,12 +97,15 @@ def __init__(
10197
self._fetcher = fetcher or requests_fetcher.RequestsFetcher()
10298
self.config = config or UpdaterConfig()
10399

104-
unwrapper: Optional[wrapping.Unwrapper] = None
105-
if self.config.use_dsse:
106-
unwrapper = wrapping.EnvelopeUnwrapper()
100+
supported_envelopes = [EnvelopeType.METADATA, EnvelopeType.SIMPLE]
101+
if self.config.envelope_type not in supported_envelopes:
102+
raise ValueError(
103+
f"config: envelope_type must be one of {supported_envelopes}, "
104+
f"got '{self.config.envelope_type}'"
105+
)
107106

108107
self._trusted_set = trusted_metadata_set.TrustedMetadataSet(
109-
data, unwrapper
108+
data, self.config.envelope_type
110109
)
111110

112111
def refresh(self) -> None:

0 commit comments

Comments
 (0)