Skip to content

Commit 1cd9762

Browse files
authored
Merge pull request #840 from sigstore/more-tuf-updates
More tuf updates for conformance
2 parents 90d958b + ed37516 commit 1cd9762

File tree

11 files changed

+63
-125
lines changed

11 files changed

+63
-125
lines changed

sigstore-java/src/main/java/dev/sigstore/tuf/FileSystemTufStore.java

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,14 @@
2020
import com.google.common.annotations.VisibleForTesting;
2121
import dev.sigstore.tuf.model.*;
2222
import java.io.BufferedWriter;
23-
import java.io.File;
2423
import java.io.IOException;
25-
import java.nio.file.FileAlreadyExistsException;
2624
import java.nio.file.Files;
2725
import java.nio.file.Path;
2826
import java.util.Optional;
2927

3028
/** Uses a local file system directory to store the trusted TUF metadata. */
3129
public class FileSystemTufStore implements MetaStore, TargetStore {
3230

33-
private static final String ROOT_FILE_NAME = "root.json";
34-
private static final String SNAPSHOT_FILE_NAME = "snapshot.json";
35-
private static final String TIMESTAMP_FILE_NAME = "timestamp.json";
3631
private final Path repoBaseDir;
3732
private final Path targetsCache;
3833

@@ -101,30 +96,10 @@ <T extends SignedTufMeta<?>> void storeRole(String roleName, T role) throws IOEx
10196
}
10297

10398
@Override
104-
public void writeRoot(Root root) throws IOException {
105-
Optional<Root> trustedRoot = readMeta(RootRole.ROOT, Root.class);
106-
if (trustedRoot.isPresent()) {
107-
try {
108-
Files.move(
109-
repoBaseDir.resolve(ROOT_FILE_NAME),
110-
repoBaseDir.resolve(
111-
trustedRoot.get().getSignedMeta().getVersion() + "." + ROOT_FILE_NAME));
112-
} catch (FileAlreadyExistsException e) {
113-
// The file is already backed-up. continue.
114-
}
115-
}
116-
storeRole(RootRole.ROOT, root);
117-
}
118-
119-
@Override
120-
public void clearMetaDueToKeyRotation() throws IOException {
121-
File snapshotMetaFile = repoBaseDir.resolve(SNAPSHOT_FILE_NAME).toFile();
122-
if (snapshotMetaFile.exists()) {
123-
Files.delete(snapshotMetaFile.toPath());
124-
}
125-
File timestampMetaFile = repoBaseDir.resolve(TIMESTAMP_FILE_NAME).toFile();
126-
if (timestampMetaFile.exists()) {
127-
Files.delete(timestampMetaFile.toPath());
99+
public void clearMeta(String role) throws IOException {
100+
Path metaFile = repoBaseDir.resolve(role + ".json");
101+
if (Files.isRegularFile(metaFile)) {
102+
Files.delete(metaFile);
128103
}
129104
}
130105
}

sigstore-java/src/main/java/dev/sigstore/tuf/MetaStore.java

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package dev.sigstore.tuf;
1717

18-
import dev.sigstore.tuf.model.Root;
1918
import dev.sigstore.tuf.model.SignedTufMeta;
2019
import dev.sigstore.tuf.model.TufMeta;
2120
import java.io.IOException;
@@ -31,8 +30,7 @@ public interface MetaStore extends MetaReader {
3130
String getIdentifier();
3231

3332
/**
34-
* Generic method to store one of the {@link SignedTufMeta} resources in the local tuf store. Do
35-
* not use for Root role, use {@link #writeRoot(Root)} instead.
33+
* Generic method to store one of the {@link SignedTufMeta} resources in the local tuf store.
3634
*
3735
* @param roleName the name of the role
3836
* @param meta the metadata to store
@@ -41,26 +39,13 @@ public interface MetaStore extends MetaReader {
4139
void writeMeta(String roleName, SignedTufMeta<? extends TufMeta> meta) throws IOException;
4240

4341
/**
44-
* Once you have ascertained that your root is trustworthy use this method to persist it to your
45-
* local store. This will usually only be called with a root loaded statically from a bundled
46-
* trusted root, or after the successful verification of an updated root from a mirror.
47-
*
48-
* @param root a root that has been proven trustworthy by the client
49-
* @throws IOException since some implementations may persist the root to disk or over the network
50-
* we throw {@code IOException} in case of IO error.
51-
* @see <a
52-
* href="https://theupdateframework.github.io/specification/latest/#detailed-client-workflow">5.3.8</a>
53-
*/
54-
void writeRoot(Root root) throws IOException;
55-
56-
/**
57-
* This clears out the snapshot and timestamp metadata from the store, as required when snapshot
58-
* or timestamp verification keys have changed as a result of a root update.
42+
* Generic method to remove meta, useful when keys rotated in root. Deletion is not optional,
43+
* implementers must ensure meta is removed from the storage medium.
5944
*
6045
* @throws IOException implementations that read/write IO to clear the data may throw {@code
6146
* IOException}
6247
* @see <a
6348
* href="https://theupdateframework.github.io/specification/latest/#detailed-client-workflow">5.3.11</a>
6449
*/
65-
void clearMetaDueToKeyRotation() throws IOException;
50+
void clearMeta(String role) throws IOException;
6651
}

sigstore-java/src/main/java/dev/sigstore/tuf/PassthroughCacheMetaStore.java

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,11 @@
1515
*/
1616
package dev.sigstore.tuf;
1717

18-
import dev.sigstore.tuf.model.Root;
19-
import dev.sigstore.tuf.model.RootRole;
2018
import dev.sigstore.tuf.model.SignedTufMeta;
2119
import dev.sigstore.tuf.model.TufMeta;
2220
import java.io.IOException;
2321
import java.util.HashMap;
2422
import java.util.Map;
25-
import java.util.Objects;
2623
import java.util.Optional;
2724

2825
/** An in memory cache that will pass through to a provided local tuf store. */
@@ -44,13 +41,6 @@ public static PassthroughCacheMetaStore newPassthroughMetaCache(MetaStore localS
4441
return new PassthroughCacheMetaStore(localStore);
4542
}
4643

47-
@Override
48-
public void writeRoot(Root root) throws IOException {
49-
// call writeRoot instead of generic writeMeta because it may do extra work when storing on disk
50-
localStore.writeRoot(root);
51-
cache.put(RootRole.ROOT, root);
52-
}
53-
5444
@Override
5545
@SuppressWarnings("unchecked")
5646
public <T extends SignedTufMeta<? extends TufMeta>> Optional<T> readMeta(
@@ -69,17 +59,13 @@ public <T extends SignedTufMeta<? extends TufMeta>> Optional<T> readMeta(
6959

7060
@Override
7161
public void writeMeta(String roleName, SignedTufMeta<? extends TufMeta> meta) throws IOException {
72-
if (Objects.equals(roleName, RootRole.ROOT)) {
73-
throw new IllegalArgumentException("Calling writeMeta on root instead of writeRoot");
74-
}
7562
localStore.writeMeta(roleName, meta);
7663
cache.put(roleName, meta);
7764
}
7865

7966
@Override
80-
public void clearMetaDueToKeyRotation() throws IOException {
81-
localStore.clearMetaDueToKeyRotation();
82-
cache.remove(RootRole.TIMESTAMP);
83-
cache.remove(RootRole.SNAPSHOT);
67+
public void clearMeta(String role) throws IOException {
68+
localStore.clearMeta(role);
69+
cache.remove(role);
8470
}
8571
}

sigstore-java/src/main/java/dev/sigstore/tuf/TrustedMetaStore.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ <T extends SignedTufMeta<? extends TufMeta>> T getMeta(String roleName, Class<T>
7070
}
7171

7272
public void setRoot(Root root) throws IOException {
73-
metaStore.writeRoot(root);
73+
metaStore.writeMeta(RootRole.ROOT, root);
7474
}
7575

7676
public Root getRoot() throws IOException {
@@ -118,6 +118,7 @@ public Optional<Targets> findTargets() throws IOException {
118118
}
119119

120120
public void clearMetaDueToKeyRotation() throws IOException {
121-
metaStore.clearMetaDueToKeyRotation();
121+
metaStore.clearMeta(RootRole.TIMESTAMP);
122+
metaStore.clearMeta(RootRole.SNAPSHOT);
122123
}
123124
}

sigstore-java/src/main/java/dev/sigstore/tuf/Updater.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,14 @@ public void updateMeta() throws IOException, NoSuchAlgorithmException, InvalidKe
104104
updateRoot();
105105
var oldTimestamp = trustedMetaStore.findTimestamp();
106106
updateTimestamp();
107-
if (Objects.equals(oldTimestamp.orElse(null), trustedMetaStore.getTimestamp())
108-
&& trustedMetaStore.findSnapshot().isPresent()
109-
&& trustedMetaStore.findTargets().isPresent()) {
110-
return;
107+
if (!Objects.equals(oldTimestamp.orElse(null), trustedMetaStore.getTimestamp())
108+
|| trustedMetaStore.findSnapshot().isEmpty()
109+
|| trustedMetaStore.findTargets().isEmpty()) {
110+
// if we need to update or we can't find targets/snapshots locally then grab new snapshot and
111+
// targets from remote
112+
updateSnapshot();
113+
updateTargets();
111114
}
112-
// if we need to update or we can't find targets/timestamps locally then grab new snapshot and
113-
// targets from remote
114-
updateSnapshot();
115-
updateTargets();
116115
}
117116

118117
/** Download a single target defined in targets. Does not handle delegated targets. */
@@ -141,7 +140,11 @@ void updateRoot()
141140
trustedRoot = localRoot.get();
142141
} else {
143142
trustedRoot = GSON.get().fromJson(trustedRootPath.get(), Root.class);
143+
trustedMetaStore.setRoot(trustedRoot);
144144
}
145+
// verify root that we're bootstrapping this update with is good to go
146+
verifyDelegate(trustedRoot, trustedRoot);
147+
145148
int baseVersion = trustedRoot.getSignedMeta().getVersion();
146149
int nextVersion = baseVersion + 1;
147150
// keep these for verifying the last step. 5.3.11
@@ -194,7 +197,6 @@ void updateRoot()
194197
trustedRoot.getSignedMeta().getRoles().get(RootRole.TIMESTAMP))) {
195198
trustedMetaStore.clearMetaDueToKeyRotation();
196199
}
197-
trustedMetaStore.setRoot(trustedRoot);
198200
}
199201

200202
private void throwIfExpired(ZonedDateTime expires) {

sigstore-java/src/main/java/dev/sigstore/tuf/model/Root.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package dev.sigstore.tuf.model;
1717

18+
import com.google.common.base.Preconditions;
1819
import org.immutables.gson.Gson;
1920
import org.immutables.value.Value;
2021
import org.immutables.value.Value.Derived;
@@ -29,4 +30,9 @@ public interface Root extends SignedTufMeta<RootMeta> {
2930
default RootMeta getSignedMeta() {
3031
return getSignedMeta(RootMeta.class);
3132
}
33+
34+
@Value.Check
35+
default void checkType() {
36+
Preconditions.checkState(getSignedMeta().getType().equals("root"));
37+
}
3238
}

sigstore-java/src/main/java/dev/sigstore/tuf/model/Snapshot.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package dev.sigstore.tuf.model;
1717

18+
import com.google.common.base.Preconditions;
1819
import org.immutables.gson.Gson;
1920
import org.immutables.value.Value;
2021
import org.immutables.value.Value.Derived;
@@ -29,4 +30,9 @@ public interface Snapshot extends SignedTufMeta<SnapshotMeta> {
2930
default SnapshotMeta getSignedMeta() {
3031
return getSignedMeta(SnapshotMeta.class);
3132
}
33+
34+
@Value.Check
35+
default void checkType() {
36+
Preconditions.checkState(getSignedMeta().getType().equals("snapshot"));
37+
}
3238
}

sigstore-java/src/main/java/dev/sigstore/tuf/model/Targets.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package dev.sigstore.tuf.model;
1717

18+
import com.google.common.base.Preconditions;
1819
import org.immutables.gson.Gson;
1920
import org.immutables.value.Value;
2021
import org.immutables.value.Value.Derived;
@@ -29,4 +30,9 @@ public interface Targets extends SignedTufMeta<TargetMeta> {
2930
default TargetMeta getSignedMeta() {
3031
return getSignedMeta(TargetMeta.class);
3132
}
33+
34+
@Value.Check
35+
default void checkType() {
36+
Preconditions.checkState(getSignedMeta().getType().equals("targets"));
37+
}
3238
}

sigstore-java/src/main/java/dev/sigstore/tuf/model/Timestamp.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package dev.sigstore.tuf.model;
1717

18+
import com.google.common.base.Preconditions;
1819
import org.immutables.gson.Gson;
1920
import org.immutables.value.Value;
2021
import org.immutables.value.Value.Derived;
@@ -30,4 +31,9 @@ public interface Timestamp extends SignedTufMeta<TimestampMeta> {
3031
default TimestampMeta getSignedMeta() {
3132
return getSignedMeta(TimestampMeta.class);
3233
}
34+
35+
@Value.Check
36+
default void checkType() {
37+
Preconditions.checkState(getSignedMeta().getType().equals("timestamp"));
38+
}
3339
}

sigstore-java/src/test/java/dev/sigstore/tuf/FileSystemTufStoreTest.java

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,33 +43,24 @@ void newFileSystemStore_hasRepo(@TempDir Path repoBase) throws IOException {
4343
}
4444

4545
@Test
46-
void setTrustedRoot_noPrevious(@TempDir Path repoBase) throws IOException {
46+
void writeMeta(@TempDir Path repoBase) throws IOException {
4747
FileSystemTufStore tufStore = FileSystemTufStore.newFileSystemStore(repoBase);
4848
assertFalse(repoBase.resolve("root.json").toFile().exists());
49-
tufStore.writeRoot(TestResources.loadRoot(TestResources.UPDATER_REAL_TRUSTED_ROOT));
49+
tufStore.writeMeta(
50+
RootRole.ROOT, TestResources.loadRoot(TestResources.UPDATER_REAL_TRUSTED_ROOT));
5051
assertEquals(2, repoBase.toFile().list().length, "Expect 2: root.json plus the /targets dir.");
5152
assertTrue(repoBase.resolve("root.json").toFile().exists());
5253
assertTrue(repoBase.resolve("targets").toFile().isDirectory());
5354
}
5455

5556
@Test
56-
void setTrustedRoot_backupPerformed(@TempDir Path repoBase) throws IOException {
57-
TestResources.setupRepoFiles(PROD_REPO, repoBase, "root.json");
58-
FileSystemTufStore tufStore = FileSystemTufStore.newFileSystemStore(repoBase);
59-
int version = tufStore.readMeta(RootRole.ROOT, Root.class).get().getSignedMeta().getVersion();
60-
assertFalse(repoBase.resolve(version + ".root.json").toFile().exists());
61-
tufStore.writeRoot(TestResources.loadRoot(TestResources.UPDATER_REAL_TRUSTED_ROOT));
62-
assertTrue(repoBase.resolve(version + ".root.json").toFile().exists());
63-
}
64-
65-
@Test
66-
void clearMetaDueToKeyRotation(@TempDir Path repoBase) throws IOException {
57+
void clearMeta(@TempDir Path repoBase) throws IOException {
6758
TestResources.setupRepoFiles(PROD_REPO, repoBase, "snapshot.json", "timestamp.json");
6859
FileSystemTufStore tufStore = FileSystemTufStore.newFileSystemStore(repoBase);
6960
assertTrue(repoBase.resolve("snapshot.json").toFile().exists());
7061
assertTrue(repoBase.resolve("timestamp.json").toFile().exists());
71-
tufStore.clearMetaDueToKeyRotation();
72-
assertFalse(repoBase.resolve("snapshot.json").toFile().exists());
62+
tufStore.clearMeta(RootRole.TIMESTAMP);
63+
assertTrue(repoBase.resolve("snapshot.json").toFile().exists());
7364
assertFalse(repoBase.resolve("timestamp.json").toFile().exists());
7465
}
7566
}

0 commit comments

Comments
 (0)