Skip to content

Commit 2001c55

Browse files
committed
Parse DSSE bundles and Intoto payloads
Signed-off-by: Appu Goundan <[email protected]>
1 parent 2a3ac62 commit 2001c55

File tree

7 files changed

+171
-18
lines changed

7 files changed

+171
-18
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public void verify(Path artifact, Bundle bundle, VerificationOptions options)
122122
public void verify(byte[] artifactDigest, Bundle bundle, VerificationOptions options)
123123
throws KeylessVerificationException {
124124

125-
if (bundle.getDSSESignature().isPresent()) {
125+
if (bundle.getDsseEnvelope().isPresent()) {
126126
throw new KeylessVerificationException("Cannot verify DSSE signature based bundles");
127127
}
128128

sigstore-java/src/main/java/dev/sigstore/bundle/Bundle.java

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
import java.io.IOException;
2121
import java.io.Reader;
2222
import java.nio.charset.Charset;
23+
import java.nio.charset.StandardCharsets;
2324
import java.nio.file.Files;
2425
import java.nio.file.Path;
2526
import java.security.cert.CertPath;
2627
import java.util.List;
2728
import java.util.Optional;
29+
import org.immutables.gson.Gson;
2830
import org.immutables.value.Value;
2931
import org.immutables.value.Value.Default;
32+
import org.immutables.value.Value.Derived;
3033
import org.immutables.value.Value.Immutable;
3134
import org.immutables.value.Value.Lazy;
3235

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

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

6467
@Value.Check
6568
protected void checkOnlyOneSignature() {
6669
Preconditions.checkState(
67-
(getDSSESignature().isEmpty() && getMessageSignature().isPresent())
68-
|| (getDSSESignature().isPresent() && getMessageSignature().isEmpty()));
70+
(getDsseEnvelope().isEmpty() && getMessageSignature().isPresent())
71+
|| (getDsseEnvelope().isPresent() && getMessageSignature().isEmpty()));
6972
}
7073

7174
@Value.Check
@@ -132,7 +135,7 @@ public interface MessageDigest {
132135
}
133136

134137
@Immutable
135-
public interface DSSESignature {
138+
public interface DsseEnvelope {
136139

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

143146
/** DSSE specific signature */
144-
byte[] getSignature();
147+
List<Signature> getSignatures();
148+
149+
/**
150+
* The "Pre-Authentication Encoding" of this statement. The signature is generated over this
151+
* content.
152+
*/
153+
@Gson.Ignore
154+
@Derived
155+
default byte[] getPAE() {
156+
return ("DSSEv1 "
157+
+ getPayloadType().length()
158+
+ " "
159+
+ getPayloadType()
160+
+ " "
161+
+ getPayload().length()
162+
+ " "
163+
+ getPayload())
164+
.getBytes(StandardCharsets.UTF_8);
165+
}
166+
167+
@Lazy
168+
@Gson.Ignore
169+
default byte[] getSignature() {
170+
return getSignatures().get(0).getSig();
171+
}
172+
173+
@Immutable
174+
interface Signature {
175+
byte[] getSig();
176+
}
145177
}
146178

147179
@Immutable

sigstore-java/src/main/java/dev/sigstore/bundle/BundleReader.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,18 @@ static Bundle readBundle(Reader jsonReader) throws BundleParseException {
100100
}
101101

102102
if (protoBundle.hasDsseEnvelope()) {
103-
var dsseEnvelope = protoBundle.getDsseEnvelope();
104-
if (dsseEnvelope.getSignaturesCount() != 1) {
105-
throw new BundleParseException("DSEE envelopes must contain exactly one signature");
103+
var dsseEnvelopeProto = protoBundle.getDsseEnvelope();
104+
var dsseEnvelopeBuilder =
105+
ImmutableDsseEnvelope.builder()
106+
.payload(dsseEnvelopeProto.getPayload().toStringUtf8())
107+
.payloadType(dsseEnvelopeProto.getPayloadType());
108+
for (int sigIndex = 0; sigIndex < dsseEnvelopeProto.getSignaturesCount(); sigIndex++) {
109+
dsseEnvelopeBuilder.addSignatures(
110+
ImmutableSignature.builder()
111+
.sig(dsseEnvelopeProto.getSignatures(sigIndex).getSig().toByteArray())
112+
.build());
106113
}
107-
var dsseSignature =
108-
ImmutableDSSESignature.builder()
109-
.payload(dsseEnvelope.getPayload().toStringUtf8())
110-
.payloadType(dsseEnvelope.getPayloadType())
111-
.signature(dsseEnvelope.getSignatures(0).toByteArray())
112-
.build();
113-
bundleBuilder.dSSESignature(dsseSignature);
114+
bundleBuilder.dsseEnvelope(dsseEnvelopeBuilder.build());
114115
} else if (protoBundle.hasMessageSignature()) {
115116
var signature = protoBundle.getMessageSignature().getSignature().toByteArray();
116117
if (protoBundle.getMessageSignature().hasMessageDigest()) {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2024 The Sigstore Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package dev.sigstore.dsse;
17+
18+
import static dev.sigstore.json.GsonSupplier.GSON;
19+
20+
import com.google.gson.JsonElement;
21+
import dev.sigstore.bundle.Bundle.DsseEnvelope;
22+
import java.util.List;
23+
import java.util.Map;
24+
import org.immutables.gson.Gson;
25+
import org.immutables.value.Value.Immutable;
26+
27+
@Gson.TypeAdapters
28+
@Immutable
29+
public interface InTotoPayload {
30+
31+
String PAYLOAD_TYPE = "application/vnd.in-toto+json";
32+
33+
@Gson.Named("_type")
34+
String getType();
35+
36+
List<Subject> getSubject();
37+
38+
String getPredicateType();
39+
40+
/**
41+
* Predicate is not processed by this library, if you want to inspect the contents of an
42+
* attestation, you want to use an attestation parser.
43+
*/
44+
JsonElement getPredicate();
45+
46+
@Immutable
47+
interface Subject {
48+
49+
String getName();
50+
51+
Map<String, String> getDigest();
52+
}
53+
54+
static InTotoPayload from(DsseEnvelope dsseEnvelope) {
55+
return GSON.get().fromJson(dsseEnvelope.getPayload(), InTotoPayload.class);
56+
}
57+
}

sigstore-java/src/main/java/dev/sigstore/json/GsonSupplier.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package dev.sigstore.json;
1717

1818
import com.google.gson.*;
19+
import dev.sigstore.dsse.GsonAdaptersInTotoPayload;
1920
import dev.sigstore.rekor.client.GsonAdaptersRekorEntry;
2021
import dev.sigstore.rekor.client.GsonAdaptersRekorEntryBody;
2122
import dev.sigstore.tuf.model.*;
@@ -59,6 +60,7 @@ public enum GsonSupplier implements Supplier<Gson> {
5960
.registerTypeAdapterFactory(new GsonAdaptersTargetMeta())
6061
.registerTypeAdapterFactory(new GsonAdaptersTimestamp())
6162
.registerTypeAdapterFactory(new GsonAdaptersTimestampMeta())
63+
.registerTypeAdapterFactory(new GsonAdaptersInTotoPayload())
6264
.disableHtmlEscaping()
6365
.create();
6466

sigstore-java/src/test/java/dev/sigstore/bundle/BundleReaderTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package dev.sigstore.bundle;
1717

1818
import com.google.common.io.Resources;
19+
import dev.sigstore.dsse.InTotoPayload;
1920
import java.io.InputStreamReader;
2021
import java.nio.charset.StandardCharsets;
2122
import org.junit.jupiter.api.Assertions;
@@ -81,7 +82,9 @@ public void readV3_1Bundle_noInclusion() {
8182
@Test
8283
public void readDSSEBundle() throws Exception {
8384
var bundle = readBundle("dev/sigstore/samples/bundles/bundle.dsse.sigstore");
84-
Assertions.assertTrue(bundle.getDSSESignature().isPresent());
85+
Assertions.assertTrue(bundle.getDsseEnvelope().isPresent());
86+
var intotoPayload = InTotoPayload.from(bundle.getDsseEnvelope().get());
87+
Assertions.assertEquals("https://slsa.dev/provenance/v1", intotoPayload.getPredicateType());
8588
}
8689

8790
private Bundle readBundle(String resourcePath) throws Exception {

sigstore-java/src/test/resources/dev/sigstore/samples/bundles/bundle.dsse.sigstore

Lines changed: 59 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)