Skip to content

Commit 24a3b8f

Browse files
committed
Reorganize message signature checks
Signed-off-by: Appu Goundan <[email protected]>
1 parent 9ff9f39 commit 24a3b8f

File tree

1 file changed

+60
-49
lines changed

1 file changed

+60
-49
lines changed

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

Lines changed: 60 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@
2222
import dev.sigstore.VerificationOptions.CertificateMatcher;
2323
import dev.sigstore.VerificationOptions.UncheckedCertificateException;
2424
import dev.sigstore.bundle.Bundle;
25+
import dev.sigstore.bundle.Bundle.MessageSignature;
2526
import dev.sigstore.encryption.certificates.Certificates;
2627
import dev.sigstore.encryption.signers.Verifiers;
2728
import dev.sigstore.fulcio.client.FulcioVerificationException;
2829
import dev.sigstore.fulcio.client.FulcioVerifier;
2930
import dev.sigstore.rekor.client.HashedRekordRequest;
3031
import dev.sigstore.rekor.client.RekorEntry;
32+
import dev.sigstore.rekor.client.RekorTypeException;
33+
import dev.sigstore.rekor.client.RekorTypes;
3134
import dev.sigstore.rekor.client.RekorVerificationException;
3235
import dev.sigstore.rekor.client.RekorVerifier;
3336
import dev.sigstore.tuf.SigstoreTufClient;
@@ -45,10 +48,10 @@
4548
import java.security.spec.InvalidKeySpecException;
4649
import java.sql.Date;
4750
import java.util.Arrays;
51+
import java.util.Base64;
4852
import java.util.List;
4953
import java.util.Objects;
5054
import java.util.stream.Collectors;
51-
import org.bouncycastle.util.encoders.Base64;
5255
import org.bouncycastle.util.encoders.Hex;
5356

5457
/** Verify hashrekords from rekor signed using the keyless signing flow with fulcio certificates. */
@@ -125,12 +128,10 @@ public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions opt
125128
if (bundle.getDsseEnvelope().isPresent()) {
126129
throw new KeylessVerificationException("Cannot verify DSSE signature based bundles");
127130
}
128-
129131
if (bundle.getMessageSignature().isEmpty()) {
130132
// this should be unreachable
131133
throw new IllegalStateException("Bundle must contain a message signature to verify");
132134
}
133-
var messageSignature = bundle.getMessageSignature().get();
134135

135136
if (bundle.getEntries().isEmpty()) {
136137
throw new KeylessVerificationException("Cannot verify bundle without tlog entry");
@@ -149,20 +150,6 @@ public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions opt
149150
var signingCert = bundle.getCertPath();
150151
var leafCert = Certificates.getLeaf(signingCert);
151152

152-
// this ensures the provided artifact digest matches what may have come from a bundle (in
153-
// keyless signature)
154-
if (messageSignature.getMessageDigest().isPresent()) {
155-
var bundleDigest = messageSignature.getMessageDigest().get().getDigest();
156-
if (!Arrays.equals(artifactDigest, bundleDigest)) {
157-
throw new KeylessVerificationException(
158-
"Provided artifact digest does not match digest used for verification"
159-
+ "\nprovided(hex) : "
160-
+ Hex.toHexString(artifactDigest)
161-
+ "\nverification : "
162-
+ Hex.toHexString(bundleDigest));
163-
}
164-
}
165-
166153
// verify the certificate chains up to a trusted root (fulcio) and contains a valid SCT from
167154
// a trusted CT log
168155
try {
@@ -175,8 +162,6 @@ public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions opt
175162
// verify the certificate identity if options are present
176163
checkCertificateMatchers(leafCert, options.getCertificateMatchers());
177164

178-
var signature = messageSignature.getSignature();
179-
180165
RekorEntry rekorEntry = bundle.getEntries().get(0);
181166

182167
// verify the rekor entry is signed by the log keys
@@ -186,23 +171,6 @@ public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions opt
186171
throw new KeylessVerificationException("Rekor entry signature was not valid", ex);
187172
}
188173

189-
// verify the log entry is relevant to the provided verification materials
190-
try {
191-
var calculatedHashedRekord =
192-
Base64.toBase64String(
193-
HashedRekordRequest.newHashedRekordRequest(
194-
artifactDigest, Certificates.toPemBytes(leafCert), signature)
195-
.toJsonPayload()
196-
.getBytes(StandardCharsets.UTF_8));
197-
if (!Objects.equals(calculatedHashedRekord, rekorEntry.getBody())) {
198-
throw new KeylessVerificationException(
199-
"Provided verification materials are inconsistent with log entry");
200-
}
201-
} catch (IOException e) {
202-
// this should be unreachable, we know leafCert is a valid certificate at this point
203-
throw new RuntimeException("Unexpected IOException on valid leafCert", e);
204-
}
205-
206174
// check if the time of entry inclusion in the log (a stand-in for signing time) is within the
207175
// validity period for the certificate
208176
var entryTime = Date.from(rekorEntry.getIntegratedTimeInstant());
@@ -214,19 +182,7 @@ public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions opt
214182
throw new KeylessVerificationException("Signing time was after certificate expiry", e);
215183
}
216184

217-
// finally check the supplied signature can be verified by the public key in the certificate
218-
var publicKey = leafCert.getPublicKey();
219-
try {
220-
var verifier = Verifiers.newVerifier(publicKey);
221-
if (!verifier.verifyDigest(artifactDigest, signature)) {
222-
throw new KeylessVerificationException("Artifact signature was not valid");
223-
}
224-
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
225-
throw new RuntimeException(ex);
226-
} catch (SignatureException ex) {
227-
throw new KeylessVerificationException(
228-
"Signature could not be processed: " + ex.getMessage(), ex);
229-
}
185+
checkMessageSignature(bundle.getMessageSignature().get(), rekorEntry, artifactDigest, leafCert);
230186
}
231187

232188
@VisibleForTesting
@@ -244,4 +200,59 @@ void checkCertificateMatchers(X509Certificate cert, List<CertificateMatcher> mat
244200
"Could not verify certificate identities: " + ce.getMessage());
245201
}
246202
}
203+
204+
void checkMessageSignature(
205+
MessageSignature messageSignature,
206+
RekorEntry rekorEntry,
207+
byte[] artifactDigest,
208+
X509Certificate leafCert)
209+
throws KeylessVerificationException {
210+
// this ensures the provided artifact digest matches what may have come from a bundle (in
211+
// keyless signature)
212+
if (messageSignature.getMessageDigest().isPresent()) {
213+
var bundleDigest = messageSignature.getMessageDigest().get().getDigest();
214+
if (!Arrays.equals(artifactDigest, bundleDigest)) {
215+
throw new KeylessVerificationException(
216+
"Provided artifact digest does not match digest used for verification"
217+
+ "\nprovided(hex) : "
218+
+ Hex.toHexString(artifactDigest)
219+
+ "\nverification(hex) : "
220+
+ Hex.toHexString(bundleDigest));
221+
}
222+
}
223+
224+
// verify the signature over the artifact
225+
var signature = messageSignature.getSignature();
226+
try {
227+
if (!Verifiers.newVerifier(leafCert.getPublicKey()).verifyDigest(artifactDigest, signature)) {
228+
throw new KeylessVerificationException("Artifact signature was not valid");
229+
}
230+
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
231+
throw new RuntimeException(ex);
232+
} catch (SignatureException ex) {
233+
throw new KeylessVerificationException(
234+
"Signature could not be processed: " + ex.getMessage(), ex);
235+
}
236+
237+
// recreate the log entry and check if it matches what was provided in the rekorEntry
238+
try {
239+
RekorTypes.getHashedRekord(rekorEntry);
240+
var calculatedHashedRekord =
241+
Base64.getEncoder()
242+
.encodeToString(
243+
HashedRekordRequest.newHashedRekordRequest(
244+
artifactDigest, Certificates.toPemBytes(leafCert), signature)
245+
.toJsonPayload()
246+
.getBytes(StandardCharsets.UTF_8));
247+
if (!Objects.equals(calculatedHashedRekord, rekorEntry.getBody())) {
248+
throw new KeylessVerificationException(
249+
"Provided verification materials are inconsistent with log entry");
250+
}
251+
} catch (IOException e) {
252+
// this should be unreachable, we know leafCert is a valid certificate at this point
253+
throw new RuntimeException("Unexpected IOException on valid leafCert", e);
254+
} catch (RekorTypeException re) {
255+
throw new KeylessVerificationException("Unexpected rekor type", re);
256+
}
257+
}
247258
}

0 commit comments

Comments
 (0)