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

Parse DSSE bundles and Intoto payloads #868

Merged
merged 1 commit into from
Dec 6, 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 @@ -122,7 +122,7 @@ public void verify(Path artifact, Bundle bundle, VerificationOptions options)
public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions options)
throws KeylessVerificationException {

if (bundle.getDSSESignature().isPresent()) {
if (bundle.getDsseEnvelope().isPresent()) {
throw new KeylessVerificationException("Cannot verify DSSE signature based bundles");
}

Expand Down
42 changes: 37 additions & 5 deletions sigstore-java/src/main/java/dev/sigstore/bundle/Bundle.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.CertPath;
import java.util.List;
import java.util.Optional;
import org.immutables.gson.Gson;
import org.immutables.value.Value;
import org.immutables.value.Value.Default;
import org.immutables.value.Value.Derived;
import org.immutables.value.Value.Immutable;
import org.immutables.value.Value.Lazy;

Expand Down Expand Up @@ -59,13 +62,13 @@ public String getMediaType() {
public abstract Optional<MessageSignature> getMessageSignature();

/** A DSSE envelope signature type that may contain an arbitrary payload */
public abstract Optional<DSSESignature> getDSSESignature();
public abstract Optional<DsseEnvelope> getDsseEnvelope();

@Value.Check
protected void checkOnlyOneSignature() {
Preconditions.checkState(
(getDSSESignature().isEmpty() && getMessageSignature().isPresent())
|| (getDSSESignature().isPresent() && getMessageSignature().isEmpty()));
(getDsseEnvelope().isEmpty() && getMessageSignature().isPresent())
|| (getDsseEnvelope().isPresent() && getMessageSignature().isEmpty()));
}

@Value.Check
Expand Down Expand Up @@ -132,7 +135,7 @@ public interface MessageDigest {
}

@Immutable
public interface DSSESignature {
public interface DsseEnvelope {

/** An arbitrary payload that does not need to be parsed to be validated */
String getPayload();
Expand All @@ -141,7 +144,36 @@ public interface DSSESignature {
String getPayloadType();

/** DSSE specific signature */
byte[] getSignature();
List<Signature> getSignatures();

/**
* The "Pre-Authentication Encoding" of this statement. The signature is generated over this
* content.
*/
@Gson.Ignore
@Derived
default byte[] getPAE() {
return ("DSSEv1 "
+ getPayloadType().length()
+ " "
+ getPayloadType()
+ " "
+ getPayload().length()
+ " "
+ getPayload())
.getBytes(StandardCharsets.UTF_8);
}

@Lazy
@Gson.Ignore
default byte[] getSignature() {
return getSignatures().get(0).getSig();
}

@Immutable
interface Signature {
byte[] getSig();
}
}

@Immutable
Expand Down
21 changes: 11 additions & 10 deletions sigstore-java/src/main/java/dev/sigstore/bundle/BundleReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,18 @@ static Bundle readBundle(Reader jsonReader) throws BundleParseException {
}

if (protoBundle.hasDsseEnvelope()) {
var dsseEnvelope = protoBundle.getDsseEnvelope();
if (dsseEnvelope.getSignaturesCount() != 1) {
throw new BundleParseException("DSEE envelopes must contain exactly one signature");
var dsseEnvelopeProto = protoBundle.getDsseEnvelope();
var dsseEnvelopeBuilder =
ImmutableDsseEnvelope.builder()
.payload(dsseEnvelopeProto.getPayload().toStringUtf8())
.payloadType(dsseEnvelopeProto.getPayloadType());
for (int sigIndex = 0; sigIndex < dsseEnvelopeProto.getSignaturesCount(); sigIndex++) {
dsseEnvelopeBuilder.addSignatures(
ImmutableSignature.builder()
.sig(dsseEnvelopeProto.getSignatures(sigIndex).getSig().toByteArray())
.build());
}
var dsseSignature =
ImmutableDSSESignature.builder()
.payload(dsseEnvelope.getPayload().toStringUtf8())
.payloadType(dsseEnvelope.getPayloadType())
.signature(dsseEnvelope.getSignatures(0).toByteArray())
.build();
bundleBuilder.dSSESignature(dsseSignature);
bundleBuilder.dsseEnvelope(dsseEnvelopeBuilder.build());
} else if (protoBundle.hasMessageSignature()) {
var signature = protoBundle.getMessageSignature().getSignature().toByteArray();
if (protoBundle.getMessageSignature().hasMessageDigest()) {
Expand Down
57 changes: 57 additions & 0 deletions sigstore-java/src/main/java/dev/sigstore/dsse/InTotoPayload.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2024 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.dsse;

import static dev.sigstore.json.GsonSupplier.GSON;

import com.google.gson.JsonElement;
import dev.sigstore.bundle.Bundle.DsseEnvelope;
import java.util.List;
import java.util.Map;
import org.immutables.gson.Gson;
import org.immutables.value.Value.Immutable;

@Gson.TypeAdapters
@Immutable
public interface InTotoPayload {

String PAYLOAD_TYPE = "application/vnd.in-toto+json";

@Gson.Named("_type")
String getType();

List<Subject> getSubject();

String getPredicateType();

/**
* Predicate is not processed by this library, if you want to inspect the contents of an
* attestation, you want to use an attestation parser.
*/
JsonElement getPredicate();

@Immutable
interface Subject {

String getName();

Map<String, String> getDigest();
}

static InTotoPayload from(DsseEnvelope dsseEnvelope) {
return GSON.get().fromJson(dsseEnvelope.getPayload(), InTotoPayload.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package dev.sigstore.json;

import com.google.gson.*;
import dev.sigstore.dsse.GsonAdaptersInTotoPayload;
import dev.sigstore.rekor.client.GsonAdaptersRekorEntry;
import dev.sigstore.rekor.client.GsonAdaptersRekorEntryBody;
import dev.sigstore.tuf.model.*;
Expand Down Expand Up @@ -59,6 +60,7 @@ public enum GsonSupplier implements Supplier<Gson> {
.registerTypeAdapterFactory(new GsonAdaptersTargetMeta())
.registerTypeAdapterFactory(new GsonAdaptersTimestamp())
.registerTypeAdapterFactory(new GsonAdaptersTimestampMeta())
.registerTypeAdapterFactory(new GsonAdaptersInTotoPayload())
.disableHtmlEscaping()
.create();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package dev.sigstore.bundle;

import com.google.common.io.Resources;
import dev.sigstore.dsse.InTotoPayload;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Assertions;
Expand Down Expand Up @@ -81,7 +82,9 @@ public void readV3_1Bundle_noInclusion() {
@Test
public void readDSSEBundle() throws Exception {
var bundle = readBundle("dev/sigstore/samples/bundles/bundle.dsse.sigstore");
Assertions.assertTrue(bundle.getDSSESignature().isPresent());
Assertions.assertTrue(bundle.getDsseEnvelope().isPresent());
var intotoPayload = InTotoPayload.from(bundle.getDsseEnvelope().get());
Assertions.assertEquals("https://slsa.dev/provenance/v1", intotoPayload.getPredicateType());
}

private Bundle readBundle(String resourcePath) throws Exception {
Expand Down

Large diffs are not rendered by default.

Loading