Skip to content

Commit

Permalink
Parse DSSE bundles and Intoto payloads
Browse files Browse the repository at this point in the history
Signed-off-by: Appu Goundan <[email protected]>
  • Loading branch information
loosebazooka committed Dec 18, 2024
1 parent ba87e0c commit eb12af7
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 18 deletions.
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.

0 comments on commit eb12af7

Please sign in to comment.