From 2e57381ff58b13dc7fdfb6aa164a74a3b5a51d56 Mon Sep 17 00:00:00 2001 From: Appu Goundan Date: Tue, 28 May 2024 14:39:17 -0400 Subject: [PATCH] Allow cli to run with alt tuf remote repository Signed-off-by: Appu Goundan --- .../src/main/java/dev/sigstore/cli/Sign.java | 67 ++++++++++++++++--- .../main/java/dev/sigstore/cli/Verify.java | 57 ++++++++++++++-- .../main/java/dev/sigstore/KeylessSigner.java | 4 +- .../java/dev/sigstore/KeylessVerifier.java | 4 +- .../dev/sigstore/tuf/SigstoreTufClient.java | 8 ++- 5 files changed, 118 insertions(+), 22 deletions(-) diff --git a/sigstore-cli/src/main/java/dev/sigstore/cli/Sign.java b/sigstore-cli/src/main/java/dev/sigstore/cli/Sign.java index 50dac316..c518dd62 100644 --- a/sigstore-cli/src/main/java/dev/sigstore/cli/Sign.java +++ b/sigstore-cli/src/main/java/dev/sigstore/cli/Sign.java @@ -16,8 +16,12 @@ package dev.sigstore.cli; import dev.sigstore.KeylessSigner; +import dev.sigstore.TrustedRootProvider; import dev.sigstore.encryption.certificates.Certificates; import dev.sigstore.oidc.client.OidcClients; +import dev.sigstore.tuf.RootProvider; +import dev.sigstore.tuf.SigstoreTufClient; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -40,12 +44,29 @@ public class Sign implements Callable { @ArgGroup(multiplicity = "1", exclusive = true) SignatureFiles signatureFiles; - @Option( - names = {"--staging"}, - description = "test against staging", - required = false, - defaultValue = "false") - Boolean staging; + @ArgGroup(multiplicity = "0..1", exclusive = true) + Verify.Target target; + + static class Target { + @Option( + names = {"--staging"}, + description = "test against staging", + required = false, + defaultValue = "false") + Boolean staging; + + @Option( + names = {"--public-good-with-tuf-url-override"}, + description = "use public good with a tuf remote repository override", + required = false) + String publicGoodWithTufUrlOverride; + + @Option( + names = {"--staging-with-tuf-url-override"}, + description = "use staging with a tuf remote repository override", + required = false) + String stagingWithTufUrlOverride; + } @Option( names = {"--identity-token"}, @@ -55,10 +76,36 @@ public class Sign implements Callable { @Override public Integer call() throws Exception { - var signerBuilder = - staging - ? KeylessSigner.builder().sigstoreStagingDefaults() - : KeylessSigner.builder().sigstorePublicDefaults(); + KeylessSigner.Builder signerBuilder; + if (target == null) { + signerBuilder = new KeylessSigner.Builder().sigstorePublicDefaults(); + } else if (target.staging) { + signerBuilder = new KeylessSigner.Builder().sigstoreStagingDefaults(); + } else if (target.publicGoodWithTufUrlOverride != null) { + var tufClientBuilder = + SigstoreTufClient.builder() + .usePublicGoodInstance() + .tufMirror( + new URL(target.publicGoodWithTufUrlOverride), + RootProvider.fromResource(SigstoreTufClient.PUBLIC_GOOD_ROOT_RESOURCE)); + signerBuilder = + KeylessSigner.builder() + .sigstorePublicDefaults() + .trustedRootProvider(TrustedRootProvider.from(tufClientBuilder)); + } else if (target.stagingWithTufUrlOverride != null) { + var tufClientBuilder = + SigstoreTufClient.builder() + .useStagingInstance() + .tufMirror( + new URL(target.stagingWithTufUrlOverride), + RootProvider.fromResource(SigstoreTufClient.STAGING_ROOT_RESOURCE)); + signerBuilder = + KeylessSigner.builder() + .sigstoreStagingDefaults() + .trustedRootProvider(TrustedRootProvider.from(tufClientBuilder)); + } else { + throw new IllegalStateException("Unable to initialize signer"); + } if (identityToken != null) { // If we've explicitly provided an identity token, customize the signer to only use the token // string OIDC client. diff --git a/sigstore-cli/src/main/java/dev/sigstore/cli/Verify.java b/sigstore-cli/src/main/java/dev/sigstore/cli/Verify.java index 2afd1e79..d98e22db 100644 --- a/sigstore-cli/src/main/java/dev/sigstore/cli/Verify.java +++ b/sigstore-cli/src/main/java/dev/sigstore/cli/Verify.java @@ -19,6 +19,7 @@ import com.google.common.hash.Hashing; import dev.sigstore.KeylessVerifier; +import dev.sigstore.TrustedRootProvider; import dev.sigstore.VerificationOptions; import dev.sigstore.VerificationOptions.CertificateIdentity; import dev.sigstore.bundle.Bundle; @@ -27,6 +28,9 @@ import dev.sigstore.bundle.ImmutableBundle; import dev.sigstore.encryption.certificates.Certificates; import dev.sigstore.rekor.client.RekorEntryFetcher; +import dev.sigstore.tuf.RootProvider; +import dev.sigstore.tuf.SigstoreTufClient; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -72,6 +76,18 @@ static class Target { description = "an alternative to the TUF managed sigstore public good trusted root", required = false) Path trustedRoot; + + @Option( + names = {"--public-good-with-tuf-url-override"}, + description = "use public good with a tuf remote repository override", + required = false) + String publicGoodWithTufUrlOverride; + + @Option( + names = {"--staging-with-tuf-url-override"}, + description = "use staging with a tuf remote repository override", + required = false) + String stagingWithTufUrlOverride; } static class Policy { @@ -127,12 +143,41 @@ public Integer call() throws Exception { } var verificationOptions = verificationOptionsBuilder.build(); - var verifier = - target == null - ? new KeylessVerifier.Builder().sigstorePublicDefaults().build() - : target.staging - ? new KeylessVerifier.Builder().sigstoreStagingDefaults().build() - : new KeylessVerifier.Builder().fromTrustedRoot(target.trustedRoot).build(); + KeylessVerifier verifier; + if (target == null) { + verifier = new KeylessVerifier.Builder().sigstorePublicDefaults().build(); + } else if (target.staging) { + verifier = new KeylessVerifier.Builder().sigstoreStagingDefaults().build(); + } else if (target.trustedRoot != null) { + verifier = + new KeylessVerifier.Builder() + .trustedRootProvider(TrustedRootProvider.from(target.trustedRoot)) + .build(); + } else if (target.publicGoodWithTufUrlOverride != null) { + var tufClientBuilder = + SigstoreTufClient.builder() + .usePublicGoodInstance() + .tufMirror( + new URL(target.publicGoodWithTufUrlOverride), + RootProvider.fromResource(SigstoreTufClient.PUBLIC_GOOD_ROOT_RESOURCE)); + verifier = + KeylessVerifier.builder() + .trustedRootProvider(TrustedRootProvider.from(tufClientBuilder)) + .build(); + } else if (target.stagingWithTufUrlOverride != null) { + var tufClientBuilder = + SigstoreTufClient.builder() + .useStagingInstance() + .tufMirror( + new URL(target.stagingWithTufUrlOverride), + RootProvider.fromResource(SigstoreTufClient.STAGING_ROOT_RESOURCE)); + verifier = + KeylessVerifier.builder() + .trustedRootProvider(TrustedRootProvider.from(tufClientBuilder)) + .build(); + } else { + throw new IllegalStateException("Unable to initialize verifier"); + } verifier.verify(artifact, bundle, verificationOptions); return 0; } diff --git a/sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java b/sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java index eb5846a1..71c63a75 100644 --- a/sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java +++ b/sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java @@ -158,8 +158,8 @@ public Builder rekorUrl(URI uri) { } @CanIgnoreReturnValue - public Builder trustedRoot(Path trustedRoot) { - trustedRootProvider = TrustedRootProvider.from(trustedRoot); + public Builder trustedRootProvider(TrustedRootProvider trustedRootProvider) { + this.trustedRootProvider = trustedRootProvider; return this; } diff --git a/sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java b/sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java index b8a787b2..bccd0f01 100644 --- a/sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java +++ b/sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java @@ -81,8 +81,8 @@ public Builder sigstoreStagingDefaults() { return this; } - public Builder fromTrustedRoot(Path trustedRoot) { - trustedRootProvider = TrustedRootProvider.from(trustedRoot); + public Builder trustedRootProvider(TrustedRootProvider trustedRootProvider) { + this.trustedRootProvider = trustedRootProvider; return this; } } diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/SigstoreTufClient.java b/sigstore-java/src/main/java/dev/sigstore/tuf/SigstoreTufClient.java index 5bea288b..f8ecccd9 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/SigstoreTufClient.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/SigstoreTufClient.java @@ -41,6 +41,10 @@ public class SigstoreTufClient { @VisibleForTesting static final String TRUST_ROOT_FILENAME = "trusted_root.json"; + public static final String PUBLIC_GOOD_ROOT_RESOURCE = + "dev/sigstore/tuf/sigstore-tuf-root/root.json"; + public static final String STAGING_ROOT_RESOURCE = "dev/sigstore/tuf/tuf-root-staging/root.json"; + private final Updater updater; private Instant lastUpdate; private SigstoreTrustedRoot sigstoreTrustedRoot; @@ -72,7 +76,7 @@ public Builder usePublicGoodInstance() { try { tufMirror( new URL("https://tuf-repo-cdn.sigstore.dev"), - RootProvider.fromResource("dev/sigstore/tuf/sigstore-tuf-root/root.json")); + RootProvider.fromResource(PUBLIC_GOOD_ROOT_RESOURCE)); } catch (MalformedURLException e) { throw new AssertionError(e); } @@ -87,7 +91,7 @@ public Builder useStagingInstance() { try { tufMirror( new URL("https://tuf-repo-cdn.sigstage.dev"), - RootProvider.fromResource("dev/sigstore/tuf/tuf-root-staging/root.json")); + RootProvider.fromResource(STAGING_ROOT_RESOURCE)); } catch (MalformedURLException e) { throw new AssertionError(e); }