Skip to content

Commit 0057cb1

Browse files
authored
Merge pull request #965 from sigstore/readSigningConfig
Consume signing_config v0.2 from TUF repo if availalbe.
2 parents 5e4585d + a2a100e commit 0057cb1

File tree

3 files changed

+89
-39
lines changed

3 files changed

+89
-39
lines changed

sigstore-java/src/main/java/dev/sigstore/TrustedRootProvider.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@
2222
import java.io.IOException;
2323
import java.nio.file.Files;
2424
import java.nio.file.Path;
25-
import java.security.InvalidKeyException;
26-
import java.security.NoSuchAlgorithmException;
27-
import java.security.cert.CertificateException;
28-
import java.security.spec.InvalidKeySpecException;
2925

3026
@FunctionalInterface
3127
public interface TrustedRootProvider {
@@ -39,11 +35,7 @@ static TrustedRootProvider from(SigstoreTufClient.Builder tufClientBuilder) {
3935
var tufClient = tufClientBuilder.build();
4036
tufClient.update();
4137
return tufClient.getSigstoreTrustedRoot();
42-
} catch (IOException
43-
| NoSuchAlgorithmException
44-
| InvalidKeySpecException
45-
| InvalidKeyException
46-
| CertificateException ex) {
38+
} catch (IOException ex) {
4739
throw new SigstoreConfigurationException(ex);
4840
}
4941
};

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

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,20 @@
1717

1818
import com.google.common.annotations.VisibleForTesting;
1919
import com.google.common.base.Preconditions;
20-
import com.google.protobuf.util.JsonFormat;
21-
import dev.sigstore.proto.trustroot.v1.TrustedRoot;
2220
import dev.sigstore.trustroot.SigstoreConfigurationException;
21+
import dev.sigstore.trustroot.SigstoreSigningConfig;
2322
import dev.sigstore.trustroot.SigstoreTrustedRoot;
2423
import java.io.IOException;
2524
import java.net.MalformedURLException;
2625
import java.net.URL;
27-
import java.nio.charset.StandardCharsets;
2826
import java.nio.file.Files;
2927
import java.nio.file.Path;
3028
import java.security.InvalidKeyException;
3129
import java.security.NoSuchAlgorithmException;
32-
import java.security.cert.CertificateException;
3330
import java.security.spec.InvalidKeySpecException;
3431
import java.time.Duration;
3532
import java.time.Instant;
33+
import javax.annotation.Nullable;
3634

3735
/**
3836
* Wrapper around {@link dev.sigstore.tuf.Updater} that provides access to sigstore specific
@@ -41,6 +39,7 @@
4139
public class SigstoreTufClient {
4240

4341
@VisibleForTesting static final String TRUST_ROOT_FILENAME = "trusted_root.json";
42+
@VisibleForTesting static final String SIGNING_CONFIG_FILENAME = "signing_config.v0.2.json";
4443

4544
public static final String PUBLIC_GOOD_ROOT_RESOURCE =
4645
"dev/sigstore/tuf/sigstore-tuf-root/root.json";
@@ -49,6 +48,10 @@ public class SigstoreTufClient {
4948
private final Updater updater;
5049
private Instant lastUpdate;
5150
private SigstoreTrustedRoot sigstoreTrustedRoot;
51+
// TODO: this is nullable because we expect all future sigstore tuf repos to contain a signing
52+
// config
53+
// but while we transition, we need to handle the null case.
54+
@Nullable private SigstoreSigningConfig sigstoreSigningConfig;
5255
private final Duration cacheValidity;
5356

5457
@VisibleForTesting
@@ -66,8 +69,8 @@ public static class Builder {
6669
Path tufCacheLocation =
6770
Path.of(System.getProperty("user.home")).resolve(".sigstore-java").resolve("root");
6871

69-
URL remoteMirror;
70-
RootProvider trustedRoot;
72+
private URL remoteMirror;
73+
private RootProvider trustedRoot;
7174

7275
public Builder usePublicGoodInstance() {
7376
if (remoteMirror != null || trustedRoot != null) {
@@ -152,39 +155,53 @@ public SigstoreTufClient build() throws IOException {
152155
* Update the tuf metadata if the cache has not been updated for at least {@code cacheValidity}
153156
* defined on the client.
154157
*/
155-
public void update()
156-
throws SigstoreConfigurationException,
157-
CertificateException,
158-
IOException,
159-
NoSuchAlgorithmException,
160-
InvalidKeySpecException,
161-
InvalidKeyException {
158+
public void update() throws SigstoreConfigurationException {
162159
if (lastUpdate == null
163160
|| Duration.between(lastUpdate, Instant.now()).compareTo(cacheValidity) > 0) {
164161
this.forceUpdate();
165162
}
166163
}
167164

168165
/** Force an update, ignoring any cache validity. */
169-
public void forceUpdate()
170-
throws SigstoreConfigurationException,
171-
CertificateException,
172-
IOException,
173-
NoSuchAlgorithmException,
174-
InvalidKeySpecException,
175-
InvalidKeyException {
176-
updater.update();
166+
public void forceUpdate() throws SigstoreConfigurationException {
167+
try {
168+
updater.update();
169+
} catch (IOException
170+
| NoSuchAlgorithmException
171+
| InvalidKeySpecException
172+
| InvalidKeyException ex) {
173+
throw new SigstoreConfigurationException("TUF repo failed to update", ex);
174+
}
177175
lastUpdate = Instant.now();
178-
var trustedRootBuilder = TrustedRoot.newBuilder();
179-
JsonFormat.parser()
180-
.merge(
181-
new String(
182-
updater.getTargetStore().readTarget(TRUST_ROOT_FILENAME), StandardCharsets.UTF_8),
183-
trustedRootBuilder);
184-
sigstoreTrustedRoot = SigstoreTrustedRoot.from(trustedRootBuilder.build());
176+
try {
177+
sigstoreTrustedRoot =
178+
SigstoreTrustedRoot.from(
179+
updater.getTargetStore().getTargetInputSteam(TRUST_ROOT_FILENAME));
180+
} catch (IOException ex) {
181+
throw new SigstoreConfigurationException("Failed to read trusted root from target store", ex);
182+
}
183+
try {
184+
if (updater.getTargetStore().hasTarget(SIGNING_CONFIG_FILENAME)) {
185+
sigstoreSigningConfig =
186+
SigstoreSigningConfig.from(
187+
updater.getTargetStore().getTargetInputSteam(SIGNING_CONFIG_FILENAME));
188+
} else {
189+
sigstoreSigningConfig = null;
190+
// TODO: Remove when prod and staging TUF repos have fully configured signing configs, but
191+
// right now sigstore tuf repos not having sigstoreSigningConfig is a valid state.
192+
}
193+
} catch (IOException ex) {
194+
throw new SigstoreConfigurationException(
195+
"Failed to read signing config from target store", ex);
196+
}
185197
}
186198

187199
public SigstoreTrustedRoot getSigstoreTrustedRoot() {
188200
return sigstoreTrustedRoot;
189201
}
202+
203+
@Nullable
204+
public SigstoreSigningConfig getSigstoreSigningConfig() {
205+
return sigstoreSigningConfig;
206+
}
190207
}

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

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,20 @@
1717

1818
import com.google.protobuf.util.JsonFormat;
1919
import dev.sigstore.proto.trustroot.v1.TrustedRoot;
20+
import dev.sigstore.trustroot.SigstoreSigningConfig;
2021
import dev.sigstore.trustroot.SigstoreTrustedRoot;
22+
import java.io.ByteArrayInputStream;
2123
import java.io.IOException;
24+
import java.io.InputStream;
2225
import java.nio.charset.StandardCharsets;
2326
import java.nio.file.Path;
2427
import java.time.Duration;
28+
import java.util.List;
2529
import org.junit.jupiter.api.Assertions;
2630
import org.junit.jupiter.api.Test;
2731
import org.junit.jupiter.api.io.TempDir;
2832
import org.mockito.Mockito;
33+
import org.mockito.stubbing.Answer;
2934

3035
class SigstoreTufClientTest {
3136

@@ -43,6 +48,19 @@ public void testUpdate_publicGoodHasTrustedRootJson() throws Exception {
4348
assertTrustedRootValid(client.getSigstoreTrustedRoot());
4449
}
4550

51+
@Test
52+
public void testUpdate_publicGoodNoSigningConfigV02() throws Exception {
53+
var client =
54+
SigstoreTufClient.builder()
55+
.usePublicGoodInstance()
56+
.tufCacheLocation(localStorePath)
57+
.build();
58+
client.forceUpdate();
59+
60+
// TODO: change this when we publish new signing config to public good
61+
Assertions.assertNull(client.getSigstoreSigningConfig());
62+
}
63+
4664
@Test
4765
public void testUpdate_stagingHasTrustedRootJson() throws Exception {
4866
var client =
@@ -52,6 +70,15 @@ public void testUpdate_stagingHasTrustedRootJson() throws Exception {
5270
assertTrustedRootValid(client.getSigstoreTrustedRoot());
5371
}
5472

73+
@Test
74+
public void testUpdate_stagingHasSigningConfigV02() throws Exception {
75+
var client =
76+
SigstoreTufClient.builder().useStagingInstance().tufCacheLocation(localStorePath).build();
77+
client.forceUpdate();
78+
79+
assertSigningConfigValid(client.getSigstoreSigningConfig());
80+
}
81+
5582
private void assertTrustedRootValid(SigstoreTrustedRoot trustedRoot) {
5683
Assertions.assertNotNull(trustedRoot);
5784

@@ -64,6 +91,15 @@ private void assertTrustedRootValid(SigstoreTrustedRoot trustedRoot) {
6491
}
6592
}
6693

94+
private void assertSigningConfigValid(SigstoreSigningConfig signingConfig) {
95+
Assertions.assertNotNull(signingConfig);
96+
97+
assertNonEmpty(signingConfig.getCas());
98+
assertNonEmpty(signingConfig.getTsas());
99+
assertNonEmpty(signingConfig.getTLogs());
100+
assertNonEmpty(signingConfig.getOidcProviders());
101+
}
102+
67103
@Test
68104
public void testUpdate_updateWhenCacheInvalid() throws Exception {
69105
var mockUpdater = mockUpdater();
@@ -90,10 +126,15 @@ private static Updater mockUpdater() throws IOException {
90126
JsonFormat.printer().print(TrustedRoot.newBuilder()).getBytes(StandardCharsets.UTF_8);
91127
var mockUpdater = Mockito.mock(Updater.class);
92128
var mockTargetStore = Mockito.mock(TargetStore.class);
93-
Mockito.when(mockTargetStore.readTarget(SigstoreTufClient.TRUST_ROOT_FILENAME))
94-
.thenReturn(trustRootBytes);
129+
Mockito.when(mockTargetStore.getTargetInputSteam(SigstoreTufClient.TRUST_ROOT_FILENAME))
130+
.thenAnswer((Answer<InputStream>) invocation -> new ByteArrayInputStream(trustRootBytes));
95131
Mockito.when(mockUpdater.getTargetStore()).thenReturn(mockTargetStore);
96132

97133
return mockUpdater;
98134
}
135+
136+
private <T> void assertNonEmpty(List<T> list) {
137+
Assertions.assertNotNull(list);
138+
Assertions.assertFalse(list.isEmpty());
139+
}
99140
}

0 commit comments

Comments
 (0)