Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checkpoint verification #660

Merged
merged 1 commit into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public void verify(byte[] artifactDigest, KeylessVerificationRequest request)
try {
rekorVerifier.verifyEntry(rekorEntry);
} catch (RekorVerificationException ex) {
throw new KeylessVerificationException("Rekor entry signature was not valid");
throw new KeylessVerificationException("Rekor entry signature was not valid", ex);
}

// check if the time of entry inclusion in the log (a stand-in for signing time) is within the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@

import com.google.common.hash.Hashing;
import dev.sigstore.encryption.signers.Verifiers;
import dev.sigstore.rekor.client.RekorEntry.Checkpoint;
import dev.sigstore.trustroot.SigstoreTrustedRoot;
import dev.sigstore.trustroot.TransparencyLog;
import dev.sigstore.trustroot.TransparencyLogs;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Base64;
import org.bouncycastle.util.encoders.Hex;

Expand Down Expand Up @@ -84,6 +87,7 @@ public void verifyEntry(RekorEntry entry) throws RekorVerificationException {

// verify inclusion proof
verifyInclusionProof(entry);
verifyCheckpoint(entry, tlog);
}

/** Verify that a Rekor Entry is in the log by checking inclusion proof. */
Expand Down Expand Up @@ -134,6 +138,43 @@ private void verifyInclusionProof(RekorEntry entry) throws RekorVerificationExce
}
}

private void verifyCheckpoint(RekorEntry entry, TransparencyLog tlog)
throws RekorVerificationException {
Checkpoint checkpoint;
try {
checkpoint = entry.getVerification().getInclusionProof().parsedCheckpoint();
} catch (RekorParseException ex) {
throw new RekorVerificationException("Could not parse checkpoint", ex);
}

byte[] inclusionRootHash =
Hex.decode(entry.getVerification().getInclusionProof().getRootHash());
byte[] checkpointRootHash = Base64.getDecoder().decode(checkpoint.getBase64Hash());

if (!Arrays.equals(inclusionRootHash, checkpointRootHash)) {
throw new RekorVerificationException(
"Checkpoint root hash does not match root hash provided in inclusion proof");
}
var keyHash = Hashing.sha256().hashBytes(tlog.getPublicKey().getRawBytes()).asBytes();
// checkpoint 0 is always the log, not any of the cross signing verifiers/monitors
var sig = checkpoint.getSignatures().get(0);
for (int i = 0; i < 4; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the 4 for?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Key ID is 4 bytes, https://github.com/sigstore/rekor/blob/main/pkg/util/signed_note.go#L140, truncated public key hash

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that should be added in a comment at least IMO

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's defined in the spec and the spec is linked in the checkpoint type

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (sig.getKeyHint()[i] != keyHash[i]) {
throw new RekorVerificationException(
"Checkpoint key hint did not match provided log public key");
}
}
try {
Verifiers.newVerifier(tlog.getPublicKey().toJavaPublicKey())
.verifyDigest(inclusionRootHash, sig.getSignature());
} catch (NoSuchAlgorithmException
| InvalidKeySpecException
| SignatureException
| InvalidKeyException ex) {
throw new RekorVerificationException("Could not verify checkpoint signature", ex);
}
}

private static byte[] combineBytes(byte[] first, byte[] second) {
byte[] result = new byte[first.length + second.length];
System.arraycopy(first, 0, result, 0, first.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@
"67e9d9f66f0ad388f7e1a20991e9a2ae3efad5cbf281e8b3d2aaf1ef99a4618c",
"16a106400c53465f6e18c2475df6ba889ca30f5667bacf32b1a5661f14a5080c",
"b4439e8d71edbc96271723cb7a969dd725e23e73d139361864a62ed76ce8dc11",
"f4926d3efb0abd42b18e18372886854825a2b378e249bd333f81d8d4534485c4",
"49b3e90806c7b63b5a86f5748e3ecb7d264ea0828eb74a45bc1a2cd7962408e8",
"5059ad9b48fa50bd9adcbff0dd81c5a0dcb60f37e0716e723a33805a464f72f8",
"6c2ce64219799e61d72996884eee9e19fb906e4d7fa04b71625fde4108f21762",
"784f79c817abb78db3ae99b6c1ede640470bf4bb678673a05bf3a6b50aaaddd6",
"0da021f68571b65e49e926e4c69024de3ac248a1319d254bc51a85a657b93c33"
"c6d92ebf4e10cdba500ca410166cd0a8d8b312154d2f45bc4292d63dea6112f6",
"1768732027401f6718b0df7769e2803127cfc099eb130a8ed7d913218f6a65f6",
"0da021f68571b65e49e926e4c69024de3ac248a1319d254bc51a85a657b93c33",
"bc8cf0c8497d5c24841de0c9bef598ec99bbd59d9538d58568340646fe289e9a",
"be328fa737b8fa9461850b8034250f237ff5b0b590b9468e6223968df294872b",
"6f06f4025d0346f04830352b23f65c8cd9e3ce4b8cb899877c35282521ddaf85"
],
"logIndex": 1227,
"rootHash": "c2aaeaf36d5899ee2ab6d931bdc39d0c50e6a9dccee0322b6a9483538f4ef079",
"checkpoint": "The checkpoint (signed tree head) that the inclusion proof is based on",
"treeSize": 1237
"rootHash": "effa4fa4575f72829016a64e584441203de533212f9470d63a56d1992e73465d",
"treeSize": 14358,
"checkpoint": "rekor.sigstage.dev - 108574341321668964\n14358\n7/pPpFdfcoKQFqZOWERBID3lMyEvlHDWOlbRmS5zRl0=\n\n— rekor.sigstage.dev 0y8wozBFAiB8OkuzdwlL6/rDEu2CsIfqmesaH/KLfmIMvlH3YTdIYgIhAPFZeXK6+b0vbWy4GSU/YZxiTpFrrzjsVOShN4LlPdZb\n"
},
"signedEntryTimestamp": "MEUCIAM2WgNNS1xwUBuoX/rYUBxbyKa6PVwNakss5KJzxw4jAiEA1//71yKdbHUi+rZRX7UyWBf4yBRp1vrkvOcWx6bbqfY="
"signedEntryTimestamp": "MEUCIQCO8dFvolJwFZDHkhkSdsW3Ny+07fG8CF7G32feG8NJMgIgd2qfJ5shezuXX8I1S6DsudvIZ8xN/+y95at/V5xHfEQ="
}
}
}
Loading