Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for succinct roles (TAP 15) #2010

Merged
merged 9 commits into from
Jun 17, 2022
Merged
3 changes: 3 additions & 0 deletions docs/api/tuf.api.metadata.supporting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ones (Root, Timestamp, Snapshot, Targets):
tuf.api.metadata.MetaFile
tuf.api.metadata.Role
tuf.api.metadata.TargetFile
tuf.api.metadata.SuccinctRoles

.. autoclass:: tuf.api.metadata.DelegatedRole

Expand All @@ -25,3 +26,5 @@ ones (Root, Timestamp, Snapshot, Targets):
.. autoclass:: tuf.api.metadata.Role

.. autoclass:: tuf.api.metadata.TargetFile

.. autoclass:: tuf.api.metadata.SuccinctRoles
2 changes: 1 addition & 1 deletion docs/repository-library-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ are found, in the python-tuf library):
```python
with repository.edit(“targets”) as targets:
# adds a key for role1 (as an example, arbitrary edits are allowed)
targets.add_key(“role1”, key)
targets.add_key(key, “role1”)
```

This code loads current targets metadata for editing, adds the key to a role,
Expand Down
8 changes: 4 additions & 4 deletions examples/repo_example/basic_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def _in(days: float) -> datetime:
for name in ["targets", "snapshot", "timestamp", "root"]:
keys[name] = generate_ed25519_key()
roles["root"].signed.add_key(
name, Key.from_securesystemslib_key(keys[name])
Key.from_securesystemslib_key(keys[name]), name
)

# NOTE: We only need the public part to populate root, so it is possible to use
Expand All @@ -173,7 +173,7 @@ def _in(days: float) -> datetime:
# required signature threshold.
another_root_key = generate_ed25519_key()
roles["root"].signed.add_key(
"root", Key.from_securesystemslib_key(another_root_key)
Key.from_securesystemslib_key(another_root_key), "root"
)
roles["root"].signed.roles["root"].threshold = 2

Expand Down Expand Up @@ -343,9 +343,9 @@ def _in(days: float) -> datetime:
# remains in place, it can be used to count towards the old and new threshold.
new_root_key = generate_ed25519_key()

roles["root"].signed.remove_key("root", keys["root"]["keyid"])
roles["root"].signed.revoke_key(keys["root"]["keyid"], "root")
roles["root"].signed.add_key(
"root", Key.from_securesystemslib_key(new_root_key)
Key.from_securesystemslib_key(new_root_key), "root"
)
roles["root"].signed.version += 1

Expand Down
1 change: 1 addition & 0 deletions examples/repo_example/hashed_bin_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ def find_hash_bin(path: str) -> str:
# 10-17 10 11 12 13 14 15 16 17
# ... ...
# f8-ff f8 f9 fa fb fc fd fe ff
assert roles["bins"].signed.delegations.roles is not None
for bin_n_name, bin_n_hash_prefixes in generate_hash_bins():
# Update delegating targets role (bins) with delegation details for each
# delegated targets role (bin_n).
Expand Down
8 changes: 4 additions & 4 deletions tests/generated_data/generate_md.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ def generate_all_files(
md_snapshot = Metadata(Snapshot(expires=EXPIRY))
md_targets = Metadata(Targets(expires=EXPIRY))

md_root.signed.add_key("root", keys["ed25519_0"])
md_root.signed.add_key("timestamp", keys["ed25519_1"])
md_root.signed.add_key("snapshot", keys["ed25519_2"])
md_root.signed.add_key("targets", keys["ed25519_3"])
md_root.signed.add_key(keys["ed25519_0"], "root")
md_root.signed.add_key(keys["ed25519_1"], "timestamp")
md_root.signed.add_key(keys["ed25519_2"], "snapshot")
md_root.signed.add_key(keys["ed25519_3"], "targets")

for i, md in enumerate([md_root, md_timestamp, md_snapshot, md_targets]):
assert isinstance(md, Metadata)
Expand Down
66 changes: 54 additions & 12 deletions tests/repository_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
MetaFile,
Root,
Snapshot,
SuccinctRoles,
TargetFile,
Targets,
Timestamp,
Expand Down Expand Up @@ -169,7 +170,7 @@ def rotate_keys(self, role: str) -> None:
self.signers[role].clear()
for _ in range(0, self.root.roles[role].threshold):
key, signer = self.create_key()
self.root.add_key(role, key)
self.root.add_key(key, role)
self.add_signer(role, signer)

def _initialize(self) -> None:
Expand All @@ -182,7 +183,7 @@ def _initialize(self) -> None:

for role in TOP_LEVEL_ROLE_NAMES:
key, signer = self.create_key()
self.md_root.signed.add_key(role, key)
self.md_root.signed.add_key(key, role)
self.add_signer(role, signer)

self.publish_root()
Expand Down Expand Up @@ -334,12 +335,16 @@ def update_snapshot(self) -> None:
self.snapshot.version += 1
self.update_timestamp()

def _get_delegator(self, delegator_name: str) -> Targets:
"""Given a delegator name return, its corresponding Targets object."""
if delegator_name == Targets.type:
return self.targets

return self.md_delegates[delegator_name].signed

def add_target(self, role: str, data: bytes, path: str) -> None:
"""Create a target from data and add it to the target_files."""
if role == Targets.type:
targets = self.targets
else:
targets = self.md_delegates[role].signed
targets = self._get_delegator(role)

target = TargetFile.from_data(path, data, ["sha256"])
targets.targets[path] = target
Expand All @@ -349,26 +354,63 @@ def add_delegation(
self, delegator_name: str, role: DelegatedRole, targets: Targets
) -> None:
"""Add delegated target role to the repository."""
if delegator_name == Targets.type:
delegator = self.targets
else:
delegator = self.md_delegates[delegator_name].signed
delegator = self._get_delegator(delegator_name)

if (
delegator.delegations is not None
and delegator.delegations.succinct_roles is not None
):
raise ValueError("Can't add a role when succinct_roles is used")

# Create delegation
if delegator.delegations is None:
delegator.delegations = Delegations({}, {})
delegator.delegations = Delegations({}, roles={})

assert delegator.delegations.roles is not None
MVrachev marked this conversation as resolved.
Show resolved Hide resolved
# put delegation last by default
delegator.delegations.roles[role.name] = role

# By default add one new key for the role
key, signer = self.create_key()
delegator.add_key(role.name, key)
delegator.add_key(key, role.name)
self.add_signer(role.name, signer)

# Add metadata for the role
if role.name not in self.md_delegates:
self.md_delegates[role.name] = Metadata(targets, {})

def add_succinct_roles(
self, delegator_name: str, bit_length: int, name_prefix: str
) -> None:
"""Add succinct roles info to a delegator with name "delegator_name".

Note that for each delegated role represented by succinct roles an empty
Targets instance is created.
"""
delegator = self._get_delegator(delegator_name)

if (
delegator.delegations is not None
and delegator.delegations.roles is not None
):
raise ValueError(
"Can't add a succinct_roles when delegated roles are used"
)

key, signer = self.create_key()
succinct_roles = SuccinctRoles([], 1, bit_length, name_prefix)
delegator.delegations = Delegations({}, None, succinct_roles)

# Add targets metadata for all bins.
for delegated_name in succinct_roles.get_roles():
self.md_delegates[delegated_name] = Metadata(
Targets(expires=self.safe_expiry)
)

self.add_signer(delegated_name, signer)

delegator.add_key(key)

def write(self) -> None:
"""Dump current repository metadata to self.dump_dir

Expand Down