Skip to content

Commit 38f309b

Browse files
committed
WIP: Update to new securesystemslib API
* API changes covered: * keys and interface modules removed * SSlibSigner removed * CryptoSigner added: this replaces the removed functionality * DSSE "signatures" container type changed * Currently pins a securesystemslib main branch commit: this shoudl be reverted before merging, when securesystemslib has made a release * tests/generated_data/generate_md.py was simplified * Encrypted test keys in tests/repository_data/keystore were replaced with the unencrypted PEM versions of the same keys * The public test keys in tests/repository_data/keystore were removed as they were not used anymore Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
1 parent 411505d commit 38f309b

26 files changed

+245
-313
lines changed

examples/manual_repo/basic_repo.py

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@
2525
import tempfile
2626
from datetime import datetime, timedelta, timezone
2727
from pathlib import Path
28-
from typing import Any, Dict
28+
from typing import Dict
2929

30-
from securesystemslib.keys import generate_ed25519_key
31-
from securesystemslib.signer import SSlibKey, SSlibSigner
30+
from securesystemslib.signer import CryptoSigner, Signer
3231

3332
from tuf.api.metadata import (
3433
SPECIFICATION_VERSION,
@@ -89,7 +88,7 @@ def _in(days: float) -> datetime:
8988
# Define containers for role objects and cryptographic keys created below. This
9089
# allows us to sign and write metadata in a batch more easily.
9190
roles: Dict[str, Metadata] = {}
92-
keys: Dict[str, Dict[str, Any]] = {}
91+
signers: Dict[str, Signer] = {}
9392

9493

9594
# Targets (integrity)
@@ -157,10 +156,8 @@ def _in(days: float) -> datetime:
157156
# See https://github.com/secure-systems-lab/securesystemslib for more details
158157
# about key handling, and don't forget to password-encrypt your private keys!
159158
for name in ["targets", "snapshot", "timestamp", "root"]:
160-
keys[name] = generate_ed25519_key()
161-
roles["root"].signed.add_key(
162-
SSlibKey.from_securesystemslib_key(keys[name]), name
163-
)
159+
signers[name] = CryptoSigner.generate_ecdsa()
160+
roles["root"].signed.add_key(signers[name].public_key, name)
164161

165162
# NOTE: We only need the public part to populate root, so it is possible to use
166163
# out-of-band mechanisms to generate key pairs and only expose the public part
@@ -173,10 +170,8 @@ def _in(days: float) -> datetime:
173170
# threshold of multiple keys to sign root metadata. For this example we
174171
# generate another root key (you can pretend it's out-of-band) and increase the
175172
# required signature threshold.
176-
another_root_key = generate_ed25519_key()
177-
roles["root"].signed.add_key(
178-
SSlibKey.from_securesystemslib_key(another_root_key), "root"
179-
)
173+
another_root_signer = CryptoSigner.generate_ecdsa()
174+
roles["root"].signed.add_key(another_root_signer.public_key, "root")
180175
roles["root"].signed.roles["root"].threshold = 2
181176

182177

@@ -185,9 +180,7 @@ def _in(days: float) -> datetime:
185180
# In this example we have access to all top-level signing keys, so we can use
186181
# them to create and add a signature for each role metadata.
187182
for name in ["targets", "snapshot", "timestamp", "root"]:
188-
key = keys[roles[name].signed.type]
189-
signer = SSlibSigner(key)
190-
roles[name].sign(signer)
183+
roles[name].sign(signers[name])
191184

192185

193186
# Persist metadata (consistent snapshot)
@@ -227,9 +220,9 @@ def _in(days: float) -> datetime:
227220
# file, sign it, and write it back to the same file, and this can be repeated
228221
# until the threshold is satisfied.
229222
root_path = os.path.join(TMP_DIR, "1.root.json")
230-
roles["root"].from_file(root_path)
231-
roles["root"].sign(SSlibSigner(another_root_key), append=True)
232-
roles["root"].to_file(root_path, serializer=PRETTY)
223+
root = Metadata.from_file(root_path)
224+
root.sign(another_root_signer, append=True)
225+
root.to_file(root_path, serializer=PRETTY)
233226

234227

235228
# Targets delegation
@@ -243,7 +236,7 @@ def _in(days: float) -> datetime:
243236
# In this example the top-level targets role trusts a new "python-scripts"
244237
# targets role to provide integrity for any target file that ends with ".py".
245238
delegatee_name = "python-scripts"
246-
keys[delegatee_name] = generate_ed25519_key()
239+
signers[delegatee_name] = CryptoSigner.generate_ecdsa()
247240

248241
# Delegatee
249242
# ---------
@@ -271,16 +264,13 @@ def _in(days: float) -> datetime:
271264
# delegatee is responsible for, e.g. a list of path patterns. For details about
272265
# all configuration parameters see
273266
# https://theupdateframework.github.io/specification/latest/#delegations
267+
delegatee_key = signers[delegatee_name].public_key
274268
roles["targets"].signed.delegations = Delegations(
275-
keys={
276-
keys[delegatee_name]["keyid"]: SSlibKey.from_securesystemslib_key(
277-
keys[delegatee_name]
278-
)
279-
},
269+
keys={delegatee_key.keyid: delegatee_key},
280270
roles={
281271
delegatee_name: DelegatedRole(
282272
name=delegatee_name,
283-
keyids=[keys[delegatee_name]["keyid"]],
273+
keyids=[delegatee_key.keyid],
284274
threshold=1,
285275
terminating=True,
286276
paths=["*.py"],
@@ -319,8 +309,7 @@ def _in(days: float) -> datetime:
319309

320310
# Sign and write metadata for all changed roles, i.e. all but root
321311
for role_name in ["targets", "python-scripts", "snapshot", "timestamp"]:
322-
signer = SSlibSigner(keys[role_name])
323-
roles[role_name].sign(signer)
312+
roles[role_name].sign(signers[role_name])
324313

325314
# Prefix all but timestamp with version number (see consistent snapshot)
326315
filename = f"{role_name}.json"
@@ -343,17 +332,15 @@ def _in(days: float) -> datetime:
343332
# In this example we will replace a root key, and sign a new version of root
344333
# with the threshold of old and new keys. Since one of the previous root keys
345334
# remains in place, it can be used to count towards the old and new threshold.
346-
new_root_key = generate_ed25519_key()
335+
new_root_signer = CryptoSigner.generate_ecdsa()
347336

348-
roles["root"].signed.revoke_key(keys["root"]["keyid"], "root")
349-
roles["root"].signed.add_key(
350-
SSlibKey.from_securesystemslib_key(new_root_key), "root"
351-
)
337+
roles["root"].signed.revoke_key(signers["root"].public_key.keyid, "root")
338+
roles["root"].signed.add_key(new_root_signer.public_key, "root")
352339
roles["root"].signed.version += 1
353340

354341
roles["root"].signatures.clear()
355-
for key in [keys["root"], another_root_key, new_root_key]:
356-
roles["root"].sign(SSlibSigner(key), append=True)
342+
for signer in [signers["root"], another_root_signer, new_root_signer]:
343+
roles["root"].sign(signer, append=True)
357344

358345
roles["root"].to_file(
359346
os.path.join(TMP_DIR, f"{roles['root'].signed.version}.root.json"),

examples/manual_repo/hashed_bin_delegation.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@
2121
import tempfile
2222
from datetime import datetime, timedelta, timezone
2323
from pathlib import Path
24-
from typing import Any, Dict, Iterator, List, Tuple
24+
from typing import Dict, Iterator, List, Tuple
2525

26-
from securesystemslib.keys import generate_ed25519_key
27-
from securesystemslib.signer import SSlibKey, SSlibSigner
26+
from securesystemslib.signer import CryptoSigner, Signer
2827

2928
from tuf.api.metadata import (
3029
DelegatedRole,
@@ -44,7 +43,7 @@ def _in(days: float) -> datetime:
4443

4544

4645
roles: Dict[str, Metadata[Targets]] = {}
47-
keys: Dict[str, Dict[str, Any]] = {}
46+
signers: Dict[str, Signer] = {}
4847

4948
# Hash bin delegation
5049
# ===================
@@ -138,7 +137,7 @@ def find_hash_bin(path: str) -> str:
138137
# NOTE: See "Targets delegation" and "Signature thresholds" paragraphs in
139138
# 'basic_repo.py' for more details
140139
for name in ["bin-n", "bins"]:
141-
keys[name] = generate_ed25519_key()
140+
signers[name] = CryptoSigner.generate_ecdsa()
142141

143142

144143
# Targets roles
@@ -149,7 +148,7 @@ def find_hash_bin(path: str) -> str:
149148
# Create preliminary delegating targets role (bins) and add public key for
150149
# delegated targets (bin_n) to key store. Delegation details are update below.
151150
roles["bins"] = Metadata(Targets(expires=_in(365)))
152-
bin_n_key = SSlibKey.from_securesystemslib_key(keys["bin-n"])
151+
bin_n_key = signers["bin-n"].public_key
153152
roles["bins"].signed.delegations = Delegations(
154153
keys={bin_n_key.keyid: bin_n_key},
155154
roles={},
@@ -169,7 +168,7 @@ def find_hash_bin(path: str) -> str:
169168
# delegated targets role (bin_n).
170169
roles["bins"].signed.delegations.roles[bin_n_name] = DelegatedRole(
171170
name=bin_n_name,
172-
keyids=[keys["bin-n"]["keyid"]],
171+
keyids=[signers["bin-n"].public_key.keyid],
173172
threshold=1,
174173
terminating=False,
175174
path_hash_prefixes=bin_n_hash_prefixes,
@@ -210,8 +209,7 @@ def find_hash_bin(path: str) -> str:
210209
TMP_DIR = tempfile.mkdtemp(dir=os.getcwd())
211210

212211
for role_name, role in roles.items():
213-
key = keys["bins"] if role_name == "bins" else keys["bin-n"]
214-
signer = SSlibSigner(key)
212+
signer = signers["bins"] if role_name == "bins" else signers["bin-n"]
215213
role.sign(signer)
216214

217215
filename = f"1.{role_name}.json"

examples/manual_repo/succinct_hash_bin_delegations.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@
2323
import tempfile
2424
from datetime import datetime, timedelta, timezone
2525
from pathlib import Path
26-
from typing import Dict, Tuple
26+
from typing import Dict
2727

28-
from securesystemslib.keys import generate_ed25519_key
29-
from securesystemslib.signer import SSlibKey, SSlibSigner
28+
from securesystemslib.signer import CryptoSigner
3029

3130
from tuf.api.metadata import (
3231
Delegations,
@@ -80,15 +79,10 @@
8079
THRESHOLD = 1
8180

8281

83-
def create_key() -> Tuple[Key, SSlibSigner]:
84-
"""Generates a new Key and Signer."""
85-
sslib_key = generate_ed25519_key()
86-
return SSlibKey.from_securesystemslib_key(sslib_key), SSlibSigner(sslib_key)
87-
88-
8982
# Create one signing key for all bins, and one for the delegating targets role.
90-
bins_key, bins_signer = create_key()
91-
_, targets_signer = create_key()
83+
bins_signer = CryptoSigner.generate_ecdsa()
84+
bins_key = bins_signer.public_key
85+
targets_signer = CryptoSigner.generate_ecdsa()
9286

9387
# Delegating targets role
9488
# -----------------------

examples/repository/_simplerepo.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
from datetime import datetime, timedelta, timezone
1111
from typing import Dict, List, Union
1212

13-
from securesystemslib import keys
14-
from securesystemslib.signer import Key, Signer, SSlibKey, SSlibSigner
13+
from securesystemslib.signer import CryptoSigner, Key, Signer
1514

1615
from tuf.api.exceptions import RepositoryError
1716
from tuf.api.metadata import (
@@ -76,9 +75,9 @@ def __init__(self) -> None:
7675
# setup a basic repository, generate signing key per top-level role
7776
with self.edit_root() as root:
7877
for role in ["root", "timestamp", "snapshot", "targets"]:
79-
key = keys.generate_ed25519_key()
80-
self.signer_cache[role].append(SSlibSigner(key))
81-
root.add_key(SSlibKey.from_securesystemslib_key(key), role)
78+
signer = CryptoSigner.generate_ecdsa()
79+
self.signer_cache[role].append(signer)
80+
root.add_key(signer.public_key, role)
8281

8382
for role in ["timestamp", "snapshot", "targets"]:
8483
with self.edit(role):

examples/uploader/_localrepo.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@
1212
from typing import Dict
1313

1414
import requests
15-
from securesystemslib import keys
16-
from securesystemslib.signer import SSlibKey, SSlibSigner
15+
from cryptography.hazmat.primitives.serialization import (
16+
Encoding,
17+
NoEncryption,
18+
PrivateFormat,
19+
)
20+
from securesystemslib.signer import CryptoSigner, Signer
1721

1822
from tuf.api.exceptions import RepositoryError
1923
from tuf.api.metadata import Metadata, MetaFile, TargetFile, Targets
@@ -75,18 +79,22 @@ def open(self, role: str) -> Metadata:
7579
md.signed.version = 0
7680
return md
7781

78-
def close(self, role: str, md: Metadata) -> None:
82+
def close(self, role_name: str, md: Metadata) -> None:
7983
"""Store a version of metadata. Handle version bumps, expiry, signing"""
84+
targets = self.targets()
85+
role = targets.get_delegated_role(role_name)
86+
public_key = targets.get_key(role.keyids[0])
87+
uri = f"file2:{self.key_dir}/{role_name}"
88+
89+
signer = Signer.from_priv_key_uri(uri, public_key)
90+
8091
md.signed.version += 1
8192
md.signed.expires = datetime.now(timezone.utc) + self.expiry_period
8293

83-
with open(f"{self.key_dir}/{role}", encoding="utf-8") as f:
84-
signer = SSlibSigner(json.loads(f.read()))
85-
8694
md.sign(signer, append=False)
8795

8896
# Upload using "api/role"
89-
uri = f"{self.base_url}/api/role/{role}"
97+
uri = f"{self.base_url}/api/role/{role_name}"
9098
r = requests.post(uri, data=md.to_bytes(JSONSerializer()), timeout=5)
9199
r.raise_for_status()
92100

@@ -115,19 +123,24 @@ def add_target(self, role: str, targetpath: str) -> bool:
115123

116124
def add_delegation(self, role: str) -> bool:
117125
"""Use the (unauthenticated) delegation adding API endpoint"""
118-
keydict = keys.generate_ed25519_key()
119-
pubkey = SSlibKey.from_securesystemslib_key(keydict)
126+
signer = CryptoSigner.generate_ecdsa()
120127

121-
data = {pubkey.keyid: pubkey.to_dict()}
128+
data = {signer.public_key.keyid: signer.public_key.to_dict()}
122129
url = f"{self.base_url}/api/delegation/{role}"
123130
r = requests.post(url, data=json.dumps(data), timeout=5)
124131
if r.status_code != 200:
125132
print(f"delegation failed with {r}")
126133
return False
127134

128135
# Store the private key using rolename as filename
129-
with open(f"{self.key_dir}/{role}", "w", encoding="utf-8") as f:
130-
f.write(json.dumps(keydict))
136+
with open(f"{self.key_dir}/{role}", "wb") as f:
137+
# TODO this is dumb and needs to be securesystemslibs job...
138+
priv_key = signer._private_key.private_bytes(
139+
encoding=Encoding.PEM,
140+
format=PrivateFormat.PKCS8,
141+
encryption_algorithm=NoEncryption(),
142+
)
143+
f.write(priv_key)
131144

132145
print(f"Uploaded new delegation, stored key in {self.key_dir}/{role}")
133146
return True

requirements/pinned.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ idna==3.7 # via requests
66
pycparser==2.22 # via cffi
77
pynacl==1.5.0 # via securesystemslib
88
requests==2.31.0
9-
securesystemslib[crypto,pynacl]==0.31.0
9+
securesystemslib[crypto,pynacl] @ git+https://github.com/secure-systems-lab/securesystemslib@34a42954b5064610ec98406bb55719fd19874089
1010
urllib3==2.2.1 # via requests

0 commit comments

Comments
 (0)