Skip to content
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
4 changes: 2 additions & 2 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
distribution: 'temurin'

- name: Run Conformance Tests (production)
uses: sigstore/sigstore-conformance@4d66ba3cb0c9c95f705c757c0f5e226d3f4d5151 # v0.0.27
uses: sigstore/sigstore-conformance@21533cde107c734ebc153c3e3a24d75fc9811a36 # v0.0.29
with:
entrypoint: ${{ github.workspace }}/sigstore-cli/sigstore-cli-server
environment: production
Expand All @@ -66,7 +66,7 @@ jobs:
test_sign_verify_dsse

- name: Run Conformance Tests (staging)
uses: sigstore/sigstore-conformance@4d66ba3cb0c9c95f705c757c0f5e226d3f4d5151 # v0.0.27
uses: sigstore/sigstore-conformance@21533cde107c734ebc153c3e3a24d75fc9811a36 # v0.0.29
with:
entrypoint: ${{ github.workspace }}/sigstore-cli/sigstore-cli-server
environment: staging
Expand Down
3 changes: 0 additions & 3 deletions fuzzing/src/main/java/fuzzing/RekorTypesFuzzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ public static void fuzzerTestOneInput(FuzzedDataProvider data) {
case 2:
RekorTypes.getHashedRekordV002(entry);
break;
case 3:
RekorTypes.getDsseV002(entry);
break;
}
} catch (URISyntaxException | RekorTypeException | RekorParseException e) {
// Known exception
Expand Down
41 changes: 20 additions & 21 deletions sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import dev.sigstore.oidc.client.OidcTokenMatcher;
import dev.sigstore.proto.ProtoMutators;
import dev.sigstore.proto.common.v1.X509Certificate;
import dev.sigstore.proto.rekor.v2.DSSERequestV002;
import dev.sigstore.proto.rekor.v2.HashedRekordRequestV002;
import dev.sigstore.proto.rekor.v2.Signature;
import dev.sigstore.proto.rekor.v2.Verifier;
Expand All @@ -69,7 +68,6 @@
import dev.sigstore.trustroot.Service;
import dev.sigstore.trustroot.SigstoreConfigurationException;
import dev.sigstore.tuf.SigstoreTufClient;
import io.intoto.EnvelopeOuterClass;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
Expand Down Expand Up @@ -765,37 +763,38 @@ public Bundle attest(String payload) throws KeylessSignerException {
.build();

var pae = dsse.getPAE();
var paeDigest = Hashers.from(signingAlgorithm).hashBytes(pae).asBytes();
byte[] signature;

Bundle.DsseEnvelope dsseSigned;
try {
var sig = signer.sign(pae);
dsseSigned =
ImmutableDsseEnvelope.builder()
.from(dsse)
.addSignatures(ImmutableSignature.builder().sig(sig).build())
.build();
signature = signer.signDigest(paeDigest);
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException ex) {
throw new KeylessSignerException("Failed to sign artifact", ex);
}

Bundle.DsseEnvelope dsseSigned =
ImmutableDsseEnvelope.builder()
.from(dsse)
.addSignatures(ImmutableSignature.builder().sig(signature).build())
.build();

var verifier =
Verifier.newBuilder()
.setX509Certificate(
X509Certificate.newBuilder().setRawBytes(ByteString.copyFrom(encodedCert)).build())
.setKeyDetails(ProtoMutators.toPublicKeyDetails(signingAlgorithm))
.build();

var dsseRequest =
DSSERequestV002.newBuilder()
.setEnvelope(
EnvelopeOuterClass.Envelope.newBuilder()
.setPayload(ByteString.copyFrom(dsseSigned.getPayload()))
.setPayloadType(dsseSigned.getPayloadType())
.addSignatures(
EnvelopeOuterClass.Signature.newBuilder()
.setSig(ByteString.copyFrom(dsseSigned.getSignature())))
.build())
.addVerifiers(verifier)
var reqSignature =
Signature.newBuilder()
.setVerifier(verifier)
.setContent(ByteString.copyFrom(dsseSigned.getSignature()))
.build();

var hashedRekordRequest =
HashedRekordRequestV002.newBuilder()
.setSignature(reqSignature)
.setDigest(ByteString.copyFrom(paeDigest))
.build();

var hashFunction = Hashers.from(signingAlgorithm);
Expand Down Expand Up @@ -827,7 +826,7 @@ public Bundle attest(String payload) throws KeylessSignerException {

RekorEntry entry;
try {
entry = rekorV2Client.putEntry(dsseRequest);
entry = rekorV2Client.putEntry(hashedRekordRequest);
} catch (IOException | RekorParseException ex) {
throw new KeylessSignerException("Failed to put entry in rekor", ex);
}
Expand Down
48 changes: 18 additions & 30 deletions sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@
import dev.sigstore.fulcio.client.FulcioVerifier;
import dev.sigstore.json.JsonParseException;
import dev.sigstore.proto.ProtoMutators;
import dev.sigstore.proto.rekor.v2.DSSELogEntryV002;
import dev.sigstore.proto.rekor.v2.HashedRekordLogEntryV002;
import dev.sigstore.proto.rekor.v2.Signature;
import dev.sigstore.rekor.client.HashedRekordRequest;
import dev.sigstore.rekor.client.RekorEntry;
import dev.sigstore.rekor.client.RekorEntryBody;
import dev.sigstore.rekor.client.RekorTypeException;
import dev.sigstore.rekor.client.RekorTypes;
import dev.sigstore.rekor.client.RekorVerificationException;
Expand Down Expand Up @@ -469,13 +469,15 @@ private void checkDsseEnvelope(
throw new KeylessVerificationException("Signature could not be processed", se);
}

String version;
RekorEntryBody entryBody;
try {
version = rekorEntry.getBodyDecoded().getApiVersion();
entryBody = rekorEntry.getBodyDecoded();
} catch (JsonParseException ex) {
throw new KeylessVerificationException("Could not extract body from log entry");
}
if ("0.0.1".equals(version)) {
var kind = entryBody.getKind();
var version = entryBody.getApiVersion();
if ("0.0.1".equals(version) && "dsse".equals(kind)) {
Dsse rekorDsse;
try {
rekorDsse = RekorTypes.getDsseV001(rekorEntry);
Expand Down Expand Up @@ -517,45 +519,30 @@ private void checkDsseEnvelope(
throw new KeylessVerificationException(
"Provided DSSE signature materials are inconsistent with DSSE log entry");
}
} else if ("0.0.2".equals(version)) {
DSSELogEntryV002 logEntrySpec;
} else if ("0.0.2".equals(version) && "hashedrekord".equals(kind)) {
HashedRekordLogEntryV002 logEntrySpec;
try {
logEntrySpec = RekorTypes.getDsseV002(rekorEntry);
logEntrySpec = RekorTypes.getHashedRekordV002(rekorEntry);
} catch (RekorTypeException re) {
throw new KeylessVerificationException("Could not parse DSSE from log entry body", re);
}

try {
ProtoMutators.toHashAlgorithm(logEntrySpec.getPayloadHash().getAlgorithm());
} catch (UnsupportedAlgorithmException ex) {
throw new KeylessVerificationException("Unsupported digest algorithm in log entry", ex);
}

// check if the digest over the dsse payload matches the digest in the transparency log entry
byte[] calculatedDigest = hashing.hashBytes(dsseEnvelope.getPayload()).asBytes();
if (!Arrays.equals(
logEntrySpec.getPayloadHash().getDigest().toByteArray(), calculatedDigest)) {
throw new KeylessVerificationException(
"Digest of DSSE payload in bundle does not match DSSE payload digest in log entry");
}

// check if the signature over the dsse payload matches the signature in the rekorEntry
if (logEntrySpec.getSignaturesCount() != 1) {
// check if the digest over the dsse pae matches the digest in the transparency log entry
byte[] calculatedDigest = hashing.hashBytes(dsseEnvelope.getPAE()).asBytes();
if (!Arrays.equals(logEntrySpec.getData().getDigest().toByteArray(), calculatedDigest)) {
throw new KeylessVerificationException(
"Log entry spec must have exactly 1 signature, but found: "
+ logEntrySpec.getSignaturesCount());
"Digest of DSSE.pae in bundle does not match digest in log entry");
}

Signature logSignature = logEntrySpec.getSignatures(0);
Signature logSignature = logEntrySpec.getSignature();
if (!Arrays.equals(dsseEnvelope.getSignature(), logSignature.getContent().toByteArray())) {
throw new KeylessVerificationException(
"Signature in DSSE envelope does not match signature in log entry spec");
"Signature in DSSE envelope does not match signature in log entry");
}

var verifier = logSignature.getVerifier();
if (!verifier.hasX509Certificate()) {
throw new KeylessVerificationException(
"Rekor entry DSSE verifier is missing X.509 certificate");
throw new KeylessVerificationException("Log entry is missing X.509 certificate");
}
try {
byte[] certFromRekor = verifier.getX509Certificate().getRawBytes().toByteArray();
Expand All @@ -569,7 +556,8 @@ private void checkDsseEnvelope(
"Could not encode leaf certificate for comparison", e);
}
} else {
throw new KeylessVerificationException("Unsupported DSSE version: " + version);
throw new KeylessVerificationException(
"Unsupported entry type: '" + kind + ":" + version + "' for DSSE bundle");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.google.protobuf.InvalidProtocolBufferException;
import dev.sigstore.json.JsonParseException;
import dev.sigstore.json.ProtoJson;
import dev.sigstore.proto.rekor.v2.DSSELogEntryV002;
import dev.sigstore.proto.rekor.v2.HashedRekordLogEntryV002;
import dev.sigstore.rekor.dsse.v0_0_1.Dsse;
import dev.sigstore.rekor.hashedRekord.v0_0_1.HashedRekord;
Expand Down Expand Up @@ -91,32 +90,6 @@ public static Dsse getDsseV001(RekorEntry entry) throws RekorTypeException {
}
}

/**
* Parse a dsse from rekor at api version 0.0.2.
*
* @param entry the rekor entry obtained from rekor
* @return the parsed proto
* @throws RekorTypeException if the dsse:0.0.2 entry could not be parsed
*/
public static DSSELogEntryV002 getDsseV002(RekorEntry entry) throws RekorTypeException {
expect(entry, "dsse", "0.0.2");

try {
DSSELogEntryV002.Builder builder = DSSELogEntryV002.newBuilder();
ProtoJson.parser()
.ignoringUnknownFields()
.merge(
GSON.get().toJson(entry.getBodyDecoded().getSpec().getAsJsonObject().get("dsseV002")),
builder);
return builder.build();
} catch (InvalidProtocolBufferException
| JsonParseException
| NullPointerException
| IllegalStateException e) {
throw new RekorTypeException("Could not parse dsse:0.0.2", e);
}
}

private static void expect(RekorEntry entry, String expectedKind, String expectedApiVersion)
throws RekorTypeException {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package dev.sigstore.rekor.v2.client;

import dev.sigstore.proto.rekor.v2.DSSERequestV002;
import dev.sigstore.proto.rekor.v2.HashedRekordRequestV002;
import dev.sigstore.rekor.client.RekorEntry;
import dev.sigstore.rekor.client.RekorParseException;
Expand All @@ -31,12 +30,4 @@ public interface RekorV2Client {
*/
RekorEntry putEntry(HashedRekordRequestV002 hashedRekordRequest)
throws IOException, RekorParseException;

/**
* Put a new dsse entry on the Rekor log.
*
* @param dsseRequest the request to send to rekor
* @return a {@link RekorEntry} with information about the log entry
*/
RekorEntry putEntry(DSSERequestV002 dsseRequest) throws IOException, RekorParseException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import dev.sigstore.http.ImmutableHttpParams;
import dev.sigstore.http.URIFormat;
import dev.sigstore.proto.rekor.v2.CreateEntryRequest;
import dev.sigstore.proto.rekor.v2.DSSERequestV002;
import dev.sigstore.proto.rekor.v2.HashedRekordRequestV002;
import dev.sigstore.rekor.client.RekorEntry;
import dev.sigstore.rekor.client.RekorParseException;
Expand Down Expand Up @@ -83,11 +82,6 @@ public RekorEntry putEntry(HashedRekordRequestV002 hashedRekordRequest)
CreateEntryRequest.newBuilder().setHashedRekordRequestV002(hashedRekordRequest).build());
}

@Override
public RekorEntry putEntry(DSSERequestV002 dsseRequest) throws IOException, RekorParseException {
return putEntry(CreateEntryRequest.newBuilder().setDsseRequestV002(dsseRequest).build());
}

private RekorEntry putEntry(CreateEntryRequest request) throws IOException, RekorParseException {
URI rekorPutEndpoint = URIFormat.appendPath(uri, REKOR_ENTRIES_PATH);

Expand Down
3 changes: 2 additions & 1 deletion sigstore-java/src/test/java/dev/sigstore/KeylessTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ public void attest_production() throws Exception {
Assertions.assertNotNull(result.getDsseEnvelope().get());
Assertions.assertEquals(payload, result.getDsseEnvelope().get().getPayloadAsString());
Assertions.assertEquals(1, result.getEntries().size());
Assertions.assertEquals("hashedrekord", result.getEntries().get(0).getBodyDecoded().getKind());
Assertions.assertEquals("0.0.2", result.getEntries().get(0).getBodyDecoded().getApiVersion());

var verifier = KeylessVerifier.builder().sigstorePublicDefaults().build();
Expand Down Expand Up @@ -190,7 +191,7 @@ public void attest_staging() throws Exception {
}

private void verifySigningResult(List<Bundle> results, boolean enableRekorV2)
throws IOException, JsonParseException {
throws JsonParseException {

Assertions.assertEquals(artifactDigests.size(), results.size());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,16 +444,19 @@ static Stream<Arguments> badDsseProvider() {
Arguments.arguments(
"bundle.dsse.rekor-v2.bad-signature.sigstore", "DSSE signature was not valid"),
Arguments.arguments(
"bundle.dsse.rekor-v2.mismatched-payload.sigstore",
"Digest of DSSE payload in bundle does not match DSSE payload digest in log entry"),
"bundle.dsse.rekor-v2.mismatched-envelope.sigstore",
"Digest of DSSE.pae in bundle does not match digest in log entry"),
Arguments.arguments(
"bundle.dsse.rekor-v2.mismatched-signature.sigstore",
"Signature in DSSE envelope does not match signature in log entry spec"));
"Signature in DSSE envelope does not match signature in log entry"),
Arguments.arguments(
"bundle.dsse.rekor-v2.bad-entry-type.sigstore",
"Unsupported entry type: 'dsse:0.0.2' for DSSE bundle"));
}

@ParameterizedTest
@MethodSource("badDsseProvider")
public void testVerify_dsseBundleInvalid(String bundleName, String expectedError)
public void testVerify_dsseBundleInvalid_rekor(String bundleName, String expectedError)
throws Exception {
var bundleFile =
Resources.toString(
Expand Down Expand Up @@ -708,7 +711,7 @@ public void testVerify_dsseBundle_rekorV2() throws Exception {
Resources.getResource("dev/sigstore/samples/bundles/bundle.dsse.rekor-v2.sigstore"),
StandardCharsets.UTF_8);

var verifier = KeylessVerifier.builder().sigstoreStagingDefaults().build();
var verifier = KeylessVerifier.builder().sigstorePublicDefaults().build();
verifier.verify(
Path.of(artifact), Bundle.from(new StringReader(bundleFile)), VerificationOptions.empty());
}
Expand All @@ -721,7 +724,7 @@ public void testVerify_dsseBundleArtifactNotInSubjects_rekorV2() throws Exceptio
StandardCharsets.UTF_8);
var badArtifactDigest =
Hashing.sha256().hashString("nonsense", StandardCharsets.UTF_8).asBytes();
var verifier = KeylessVerifier.builder().sigstoreStagingDefaults().build();
var verifier = KeylessVerifier.builder().sigstorePublicDefaults().build();

var ex =
Assertions.assertThrows(
Expand Down
Loading
Loading