diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index cd125a345..50a04ffd4 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -31,6 +31,6 @@ jobs: - name: Unpack sigstore-java distribution run: tar -xvf ${{ github.workspace }}/sigstore-cli/build/distributions/sigstore-cli-*.tar --strip-components 1 - - uses: sigstore/sigstore-conformance@064fb32a890c30235f305281f3509c5e65e6f9e5 # tag=v0.0.4 + - uses: sigstore/sigstore-conformance@1abc82cdefe80bd907855d8447f903ba8b4918e0 # v0.0.6 with: entrypoint: ${{ github.workspace }}/bin/sigstore-cli diff --git a/sigstore-cli/src/main/java/dev/sigstore/cli/Policy.java b/sigstore-cli/src/main/java/dev/sigstore/cli/Policy.java new file mode 100644 index 000000000..603775b52 --- /dev/null +++ b/sigstore-cli/src/main/java/dev/sigstore/cli/Policy.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023 The Sigstore Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.sigstore.cli; + +import picocli.CommandLine.Option; + +public class Policy { + @Option( + names = {"--certificate-identity"}, + description = "subject alternative name in certificate", + required = true) + String certificateSan; + + @Option( + names = {"--certificate-oidc-issuer"}, + description = "sigstore issuer in certificate", + required = true) + String certificateIssuer; +} diff --git a/sigstore-cli/src/main/java/dev/sigstore/cli/Sigstore.java b/sigstore-cli/src/main/java/dev/sigstore/cli/Sigstore.java index 54eba87bc..b0e3430e8 100644 --- a/sigstore-cli/src/main/java/dev/sigstore/cli/Sigstore.java +++ b/sigstore-cli/src/main/java/dev/sigstore/cli/Sigstore.java @@ -23,7 +23,7 @@ @Command( name = "sigstore", mixinStandardHelpOptions = true, - subcommands = {Sign.class, Verify.class}) + subcommands = {Sign.class, Verify.class, VerifyBundle.class}) public class Sigstore { @Spec CommandSpec spec; 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 5dd47fb42..d1f9be894 100644 --- a/sigstore-cli/src/main/java/dev/sigstore/cli/Verify.java +++ b/sigstore-cli/src/main/java/dev/sigstore/cli/Verify.java @@ -33,7 +33,6 @@ import java.util.concurrent.Callable; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; -import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; @Command(name = "verify", description = "verify an artifact") @@ -47,20 +46,6 @@ public class Verify implements Callable { @ArgGroup(multiplicity = "0..1", exclusive = false) Policy policy; - static class Policy { - @Option( - names = {"--certificate-identity"}, - description = "subject alternative name in certificate", - required = true) - private String certificateSan; - - @Option( - names = {"--certificate-oidc-issuer"}, - description = "sigstore issuer in certificate", - required = true) - private String certificateIssuer; - } - @Override public Integer call() throws Exception { byte[] digest = asByteSource(artifact.toFile()).hash(Hashing.sha256()).asBytes(); diff --git a/sigstore-cli/src/main/java/dev/sigstore/cli/VerifyBundle.java b/sigstore-cli/src/main/java/dev/sigstore/cli/VerifyBundle.java new file mode 100644 index 000000000..f18d00790 --- /dev/null +++ b/sigstore-cli/src/main/java/dev/sigstore/cli/VerifyBundle.java @@ -0,0 +1,72 @@ +/* + * Copyright 2023 The Sigstore Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.sigstore.cli; + +import static com.google.common.io.Files.newReader; + +import dev.sigstore.KeylessSignature; +import dev.sigstore.KeylessVerificationRequest; +import dev.sigstore.KeylessVerificationRequest.CertificateIdentity; +import dev.sigstore.KeylessVerificationRequest.VerificationOptions; +import dev.sigstore.KeylessVerifier; +import dev.sigstore.bundle.BundleFactory; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.concurrent.Callable; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + +@Command(name = "verify-bundle", description = "verify an artifact using a sigstore bundle") +public class VerifyBundle implements Callable { + @Parameters(arity = "1", paramLabel = "", description = "artifact to verify") + Path artifact; + + @Option( + names = {"--bundle"}, + description = "path to bundle file", + required = true) + Path bundleFile; + + @ArgGroup(multiplicity = "0..1", exclusive = false) + Policy policy; + + @Override + public Integer call() throws Exception { + KeylessSignature keylessSignature = + BundleFactory.readBundle(newReader(bundleFile.toFile(), StandardCharsets.UTF_8)); + + var verificationOptionsBuilder = VerificationOptions.builder(); + if (policy != null) { + verificationOptionsBuilder.addCertificateIdentities( + CertificateIdentity.builder() + .issuer(policy.certificateIssuer) + .subjectAlternativeName(policy.certificateSan) + .build()); + } + var verificationOptions = verificationOptionsBuilder.isOnline(true).build(); + + var verifier = new KeylessVerifier.Builder().sigstorePublicDefaults().build(); + verifier.verify( + artifact, + KeylessVerificationRequest.builder() + .keylessSignature(keylessSignature) + .verificationOptions(verificationOptions) + .build()); + return 0; + } +}