Skip to content

Commit 27a57c8

Browse files
authored
Merge pull request #729 from sigstore/verification-options
Verification options
2 parents 1e524df + c0e7338 commit 27a57c8

File tree

10 files changed

+588
-331
lines changed

10 files changed

+588
-331
lines changed

fuzzing/src/main/java/fuzzing/FulcioCertificateVerifierFuzzer.java renamed to fuzzing/src/main/java/fuzzing/FulcioCertificateMatcherFuzzer.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,20 @@
1616
package fuzzing;
1717

1818
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
19-
import dev.sigstore.VerificationOptions.CertificateIdentity;
20-
import dev.sigstore.fulcio.client.FulcioCertificateVerifier;
21-
import dev.sigstore.fulcio.client.FulcioVerificationException;
19+
import dev.sigstore.VerificationOptions.UncheckedCertificateException;
20+
import dev.sigstore.fulcio.client.FulcioCertificateMatcher;
21+
import dev.sigstore.fulcio.client.ImmutableFulcioCertificateMatcher;
22+
import dev.sigstore.strings.StringMatcher;
2223
import java.io.ByteArrayInputStream;
2324
import java.nio.charset.Charset;
2425
import java.security.cert.CertificateFactory;
2526
import java.security.cert.X509Certificate;
26-
import java.util.List;
2727

28-
public class FulcioCertificateVerifierFuzzer {
28+
public class FulcioCertificateMatcherFuzzer {
2929
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
3030
byte[] byteArray = data.consumeRemainingAsBytes();
31-
String string = new String(byteArray, Charset.defaultCharset());
31+
String san = new String(byteArray, Charset.defaultCharset());
32+
String issuer = new String(byteArray, Charset.defaultCharset());
3233

3334
X509Certificate certificate;
3435
try {
@@ -40,14 +41,14 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) {
4041
}
4142

4243
try {
43-
FulcioCertificateVerifier verifier = new FulcioCertificateVerifier();
44-
List<CertificateIdentity> list =
45-
List.of(
46-
CertificateIdentity.builder().subjectAlternativeName(string).issuer(string).build(),
47-
CertificateIdentity.builder().subjectAlternativeName(string).issuer(string).build());
44+
FulcioCertificateMatcher matcher =
45+
ImmutableFulcioCertificateMatcher.builder()
46+
.subjectAlternativeName(StringMatcher.string(san))
47+
.issuer(StringMatcher.string(issuer))
48+
.build();
4849

49-
verifier.verifyCertificateMatches(certificate, list);
50-
} catch (FulcioVerificationException e) {
50+
matcher.test(certificate);
51+
} catch (UncheckedCertificateException e) {
5152
// Known exception
5253
}
5354
}

sigstore-cli/src/main/java/dev/sigstore/cli/Verify.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121
import dev.sigstore.KeylessVerifier;
2222
import dev.sigstore.TrustedRootProvider;
2323
import dev.sigstore.VerificationOptions;
24-
import dev.sigstore.VerificationOptions.CertificateIdentity;
24+
import dev.sigstore.VerificationOptions.CertificateMatcher;
2525
import dev.sigstore.bundle.Bundle;
2626
import dev.sigstore.bundle.Bundle.HashAlgorithm;
2727
import dev.sigstore.bundle.Bundle.MessageSignature;
2828
import dev.sigstore.bundle.ImmutableBundle;
2929
import dev.sigstore.encryption.certificates.Certificates;
3030
import dev.sigstore.rekor.client.RekorEntryFetcher;
31+
import dev.sigstore.strings.StringMatcher;
3132
import dev.sigstore.tuf.RootProvider;
3233
import dev.sigstore.tuf.SigstoreTufClient;
3334
import java.net.URL;
@@ -135,10 +136,10 @@ public Integer call() throws Exception {
135136

136137
var verificationOptionsBuilder = VerificationOptions.builder();
137138
if (policy != null) {
138-
verificationOptionsBuilder.addCertificateIdentities(
139-
CertificateIdentity.builder()
140-
.issuer(policy.certificateIssuer)
141-
.subjectAlternativeName(policy.certificateSan)
139+
verificationOptionsBuilder.addCertificateMatchers(
140+
CertificateMatcher.fulcio()
141+
.issuer(StringMatcher.string(policy.certificateIssuer))
142+
.subjectAlternativeName(StringMatcher.string(policy.certificateSan))
142143
.build());
143144
}
144145
var verificationOptions = verificationOptionsBuilder.build();

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
package dev.sigstore;
1717

1818
import com.google.api.client.util.Preconditions;
19+
import com.google.common.annotations.VisibleForTesting;
1920
import com.google.common.hash.Hashing;
2021
import com.google.common.io.Files;
22+
import dev.sigstore.VerificationOptions.CertificateMatcher;
23+
import dev.sigstore.VerificationOptions.UncheckedCertificateException;
2124
import dev.sigstore.bundle.Bundle;
2225
import dev.sigstore.encryption.certificates.Certificates;
2326
import dev.sigstore.encryption.signers.Verifiers;
24-
import dev.sigstore.fulcio.client.FulcioCertificateVerifier;
2527
import dev.sigstore.fulcio.client.FulcioVerificationException;
2628
import dev.sigstore.fulcio.client.FulcioVerifier;
2729
import dev.sigstore.rekor.client.RekorEntry;
@@ -37,13 +39,17 @@
3739
import java.security.cert.CertificateException;
3840
import java.security.cert.CertificateExpiredException;
3941
import java.security.cert.CertificateNotYetValidException;
42+
import java.security.cert.X509Certificate;
4043
import java.security.spec.InvalidKeySpecException;
4144
import java.sql.Date;
4245
import java.util.Arrays;
46+
import java.util.List;
47+
import java.util.stream.Collectors;
4348
import org.bouncycastle.util.encoders.Hex;
4449

4550
/** Verify hashrekords from rekor signed using the keyless signing flow with fulcio certificates. */
4651
public class KeylessVerifier {
52+
4753
private final FulcioVerifier fulcioVerifier;
4854
private final RekorVerifier rekorVerifier;
4955

@@ -57,6 +63,7 @@ public static KeylessVerifier.Builder builder() {
5763
}
5864

5965
public static class Builder {
66+
6067
private TrustedRootProvider trustedRootProvider;
6168

6269
public KeylessVerifier build()
@@ -162,15 +169,7 @@ public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions opt
162169
}
163170

164171
// verify the certificate identity if options are present
165-
if (options.getCertificateIdentities().size() > 0) {
166-
try {
167-
new FulcioCertificateVerifier()
168-
.verifyCertificateMatches(leafCert, options.getCertificateIdentities());
169-
} catch (FulcioVerificationException fve) {
170-
throw new KeylessVerificationException(
171-
"Could not verify certificate identities: " + fve.getMessage(), fve);
172-
}
173-
}
172+
checkCertificateMatchers(leafCert, options.getCertificateMatchers());
174173

175174
var signature = messageSignature.getSignature();
176175

@@ -208,4 +207,20 @@ public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions opt
208207
"Signature could not be processed: " + ex.getMessage(), ex);
209208
}
210209
}
210+
211+
@VisibleForTesting
212+
void checkCertificateMatchers(X509Certificate cert, List<CertificateMatcher> matchers)
213+
throws KeylessVerificationException {
214+
try {
215+
if (matchers.size() > 0 && matchers.stream().noneMatch(matcher -> matcher.test(cert))) {
216+
var matcherSpec =
217+
matchers.stream().map(Object::toString).collect(Collectors.joining(",", "[", "]"));
218+
throw new KeylessVerificationException(
219+
"No provided certificate identities matched values in certificate: " + matcherSpec);
220+
}
221+
} catch (UncheckedCertificateException ce) {
222+
throw new KeylessVerificationException(
223+
"Could not verify certificate identities: " + ce.getMessage());
224+
}
225+
}
211226
}

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

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,38 @@
1515
*/
1616
package dev.sigstore;
1717

18+
import dev.sigstore.fulcio.client.FulcioCertificateMatcher;
19+
import dev.sigstore.fulcio.client.ImmutableFulcioCertificateMatcher;
20+
import java.security.cert.X509Certificate;
1821
import java.util.List;
19-
import java.util.Map;
22+
import java.util.function.Predicate;
2023
import org.immutables.value.Value.Immutable;
2124

2225
@Immutable(singleton = true)
2326
public interface VerificationOptions {
2427

2528
/** An allow list of certificate identities to match with. */
26-
List<CertificateIdentity> getCertificateIdentities();
27-
28-
@Immutable
29-
interface CertificateIdentity {
30-
String getIssuer();
31-
32-
String getSubjectAlternativeName();
33-
34-
Map<String, String> getOther();
29+
List<CertificateMatcher> getCertificateMatchers();
30+
31+
/**
32+
* An interface for allowing matching of certificates. Use {@link #fulcio()} to instantiate the
33+
* default {@link FulcioCertificateMatcher} implementation. Custom implementations may throw
34+
* {@link UncheckedCertificateException} if an error occurs processing the certificate on calls to
35+
* {@link #test(X509Certificate)}. Any other runtime exception will not be handled.
36+
*/
37+
interface CertificateMatcher extends Predicate<X509Certificate> {
38+
@Override
39+
boolean test(X509Certificate certificate) throws UncheckedCertificateException;
40+
41+
static ImmutableFulcioCertificateMatcher.Builder fulcio() {
42+
return ImmutableFulcioCertificateMatcher.builder();
43+
}
44+
}
3545

36-
static ImmutableCertificateIdentity.Builder builder() {
37-
return ImmutableCertificateIdentity.builder();
46+
/** Exceptions thrown by implementations of {@link CertificateMatcher#test(X509Certificate)} */
47+
class UncheckedCertificateException extends RuntimeException {
48+
public UncheckedCertificateException(String message, Throwable cause) {
49+
super(message, cause);
3850
}
3951
}
4052

0 commit comments

Comments
 (0)