Skip to content

Commit 68498f2

Browse files
authored
Merge pull request #544 from sigstore/update-protobuf-spec
Update protobuf-spec to 0.2.1
2 parents c16c808 + 3d1cb32 commit 68498f2

File tree

11 files changed

+116
-42
lines changed

11 files changed

+116
-42
lines changed

sigstore-java/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencies {
2020

2121
implementation("io.github.erdtman:java-json-canonicalization:1.1")
2222

23-
implementation("dev.sigstore:protobuf-specs:0.1.0") {
23+
implementation("dev.sigstore:protobuf-specs:0.2.1") {
2424
because("It generates Sigstore Bundle file")
2525
}
2626
implementation(platform("com.google.protobuf:protobuf-bom:3.24.3"))

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222

2323
@Value.Immutable
2424
public interface KeylessSignature {
25-
/** The sha256 hash digest of the artifact */
25+
/**
26+
* The sha256 hash digest of the artifact, this may be empty and should be treated as not present
27+
* in that case.
28+
*/
2629
byte[] getDigest();
2730

2831
/**

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,16 @@ public void verify(byte[] artifactDigest, KeylessVerificationRequest request)
146146

147147
// this ensures the provided artifact digest matches what may have come from a bundle (in
148148
// keyless signature)
149-
if (!Arrays.equals(artifactDigest, request.getKeylessSignature().getDigest())) {
150-
throw new KeylessVerificationException(
151-
"Provided artifact sha256 digest does not match digest used for verification"
152-
+ "\nprovided(hex) : "
153-
+ Hex.toHexString(artifactDigest)
154-
+ "\nverification : "
155-
+ Hex.toHexString(request.getKeylessSignature().getDigest()));
149+
var digest = request.getKeylessSignature().getDigest();
150+
if (digest.length > 0) {
151+
if (!Arrays.equals(artifactDigest, digest)) {
152+
throw new KeylessVerificationException(
153+
"Provided artifact sha256 digest does not match digest used for verification"
154+
+ "\nprovided(hex) : "
155+
+ Hex.toHexString(artifactDigest)
156+
+ "\nverification : "
157+
+ Hex.toHexString(digest));
158+
}
156159
}
157160

158161
// verify the certificate chains up to a trusted root (fulcio) and contains a valid SCT from

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

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import java.util.ArrayList;
4747
import java.util.Base64;
4848
import java.util.List;
49-
import java.util.Optional;
5049
import java.util.stream.Collectors;
5150
import org.bouncycastle.util.encoders.Hex;
5251

@@ -68,8 +67,12 @@ class BundleFactoryInternal {
6867
* @return Sigstore Bundle in protobuf builder format
6968
*/
7069
static Bundle.Builder createBundleBuilder(KeylessSignature signingResult) {
70+
if (signingResult.getDigest().length == 0) {
71+
throw new IllegalStateException(
72+
"keyless signature must have artifact digest when serializing to bundle");
73+
}
7174
return Bundle.newBuilder()
72-
.setMediaType("application/vnd.dev.sigstore.bundle+json;version=0.1")
75+
.setMediaType("application/vnd.dev.sigstore.bundle+json;version=0.2")
7376
.setVerificationMaterial(buildVerificationMaterial(signingResult))
7477
.setMessageSignature(
7578
MessageSignature.newBuilder()
@@ -157,35 +160,47 @@ static KeylessSignature readBundle(Reader jsonReader) throws BundleParseExceptio
157160
}
158161
Bundle bundle = bundleBuilder.build();
159162

163+
// TODO: only allow v0.2 bundles at some point, we will only be producing v0.2 bundles
164+
// TODO: in our GA release.
165+
// var supportedMediaType = "application/vnd.dev.sigstore.bundle+json;version=0.2";
166+
// if (!supportedMediaType.equals(bundle.getMediaType())) {
167+
// throw new BundleParseException(
168+
// "Unsupported media type '"
169+
// + bundle.getMediaType()
170+
// + "', only '"
171+
// + supportedMediaType
172+
// + "' is supported");
173+
// }
174+
160175
if (bundle.getVerificationMaterial().getTlogEntriesCount() == 0) {
161176
throw new BundleParseException("Could not find any tlog entries in bundle json");
162177
}
163178
var bundleEntry = bundle.getVerificationMaterial().getTlogEntries(0);
179+
if (!bundleEntry.hasInclusionProof()) {
180+
throw new BundleParseException("Could not find an inclusion proof");
181+
}
164182
var bundleInclusionProof = bundleEntry.getInclusionProof();
165183

166-
ImmutableInclusionProof inclusionProof = null;
167-
if (bundleEntry.hasInclusionProof()) {
168-
inclusionProof =
169-
ImmutableInclusionProof.builder()
170-
.logIndex(bundleInclusionProof.getLogIndex())
171-
.rootHash(Hex.toHexString(bundleInclusionProof.getRootHash().toByteArray()))
172-
.treeSize(bundleInclusionProof.getTreeSize())
173-
.checkpoint(bundleInclusionProof.getCheckpoint().getEnvelope())
174-
.addAllHashes(
175-
bundleInclusionProof.getHashesList().stream()
176-
.map(ByteString::toByteArray)
177-
.map(Hex::toHexString)
178-
.collect(Collectors.toList()))
179-
.build();
180-
}
184+
ImmutableInclusionProof inclusionProof =
185+
ImmutableInclusionProof.builder()
186+
.logIndex(bundleInclusionProof.getLogIndex())
187+
.rootHash(Hex.toHexString(bundleInclusionProof.getRootHash().toByteArray()))
188+
.treeSize(bundleInclusionProof.getTreeSize())
189+
.checkpoint(bundleInclusionProof.getCheckpoint().getEnvelope())
190+
.addAllHashes(
191+
bundleInclusionProof.getHashesList().stream()
192+
.map(ByteString::toByteArray)
193+
.map(Hex::toHexString)
194+
.collect(Collectors.toList()))
195+
.build();
181196

182197
var verification =
183198
ImmutableVerification.builder()
184199
.signedEntryTimestamp(
185200
Base64.getEncoder()
186201
.encodeToString(
187202
bundleEntry.getInclusionPromise().getSignedEntryTimestamp().toByteArray()))
188-
.inclusionProof(Optional.ofNullable(inclusionProof))
203+
.inclusionProof(inclusionProof)
189204
.build();
190205

191206
var rekorEntry =
@@ -199,18 +214,23 @@ static KeylessSignature readBundle(Reader jsonReader) throws BundleParseExceptio
199214
.verification(verification)
200215
.build();
201216

202-
var hashAlgorithm = bundle.getMessageSignature().getMessageDigest().getAlgorithm();
203-
if (hashAlgorithm != HashAlgorithm.SHA2_256) {
204-
throw new BundleParseException(
205-
"Cannot read message digests of type "
206-
+ hashAlgorithm
207-
+ ", only "
208-
+ HashAlgorithm.SHA2_256
209-
+ " is supported");
217+
var digest = new byte[] {};
218+
if (bundle.getMessageSignature().hasMessageDigest()) {
219+
var hashAlgorithm = bundle.getMessageSignature().getMessageDigest().getAlgorithm();
220+
if (hashAlgorithm != HashAlgorithm.SHA2_256) {
221+
throw new BundleParseException(
222+
"Cannot read message digests of type "
223+
+ hashAlgorithm
224+
+ ", only "
225+
+ HashAlgorithm.SHA2_256
226+
+ " is supported");
227+
}
228+
digest = bundle.getMessageSignature().getMessageDigest().getDigest().toByteArray();
210229
}
230+
211231
try {
212232
return KeylessSignature.builder()
213-
.digest(bundle.getMessageSignature().getMessageDigest().getDigest().toByteArray())
233+
.digest(digest)
214234
.certPath(
215235
toCertPath(
216236
bundle.getVerificationMaterial().getX509CertificateChain().getCertificatesList()))

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ private static String subMessagePrefix(
128128
}
129129

130130
static boolean isRequired(Descriptors.FieldDescriptor field) {
131+
// while this isn't configured into the spec, we do not support rfc3161 timestamps in java yet,
132+
// so make SETs from rekor required in code here
133+
if (field
134+
.getFullName()
135+
.equals("dev.sigstore.rekor.v1.TransparencyLogEntry.inclusion_promise")) {
136+
return true;
137+
}
131138
return field.isRequired()
132139
|| field
133140
.toProto()

sigstore-java/src/test/java/dev/sigstore/KeylessTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ public static void setupArtifact() throws IOException {
5959
}
6060

6161
@Test
62-
@SuppressWarnings("deprecation")
6362
@EnabledIfOidcExists(provider = OidcProviderType.ANY)
6463
public void sign_production() throws Exception {
6564
var signer = KeylessSigner.builder().sigstorePublicDefaults().build();
@@ -78,7 +77,6 @@ public void sign_production() throws Exception {
7877
}
7978

8079
@Test
81-
@SuppressWarnings("deprecation")
8280
@EnabledIfOidcExists(provider = OidcProviderType.ANY)
8381
public void sign_staging() throws Exception {
8482
var signer = KeylessSigner.builder().sigstoreStagingDefaults().build();
@@ -108,7 +106,7 @@ private void verifySigningResult(List<KeylessSignature> results)
108106
Assertions.assertNotNull(result.getSignature());
109107

110108
var hr = RekorTypes.getHashedRekord(result.getEntry().get());
111-
// check if ht rekor entry has the digest we sent
109+
// check if the rekor entry has the digest we sent
112110
Assertions.assertArrayEquals(artifactDigest, result.getDigest());
113111
// check if the rekor entry has the signature we sent
114112
Assertions.assertArrayEquals(

sigstore-java/src/test/java/dev/sigstore/KeylessVerifierTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,24 @@ public void testVerify(boolean isOnline) throws Exception {
4545
verifier.verify(Path.of(artifact), verificationReq);
4646
}
4747

48+
@ParameterizedTest
49+
@ValueSource(booleans = {true, false})
50+
public void testVerify_noDigestInBundle(boolean isOnline) throws Exception {
51+
var bundleFile =
52+
Resources.toString(
53+
Resources.getResource("dev/sigstore/samples/bundles/bundle-no-digest.sigstore"),
54+
StandardCharsets.UTF_8);
55+
var artifact = Resources.getResource("dev/sigstore/samples/bundles/artifact.txt").getPath();
56+
57+
var verifier = KeylessVerifier.builder().sigstorePublicDefaults().build();
58+
var verificationReq =
59+
KeylessVerificationRequest.builder()
60+
.keylessSignature(BundleFactory.readBundle(new StringReader(bundleFile)))
61+
.verificationOptions(VerificationOptions.builder().isOnline(isOnline).build())
62+
.build();
63+
verifier.verify(Path.of(artifact), verificationReq);
64+
}
65+
4866
@ParameterizedTest
4967
@ValueSource(booleans = {true, false})
5068
public void testVerify_mismatchedSet(boolean isOnline) throws Exception {

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,38 @@ void allRequiredFieldsInBundle() {
6363
+ "dev.sigstore.common.v1.LogId\n"
6464
+ " key_id\n"
6565
+ "\n"
66+
+ "dev.sigstore.rekor.v1.KindVersion\n"
67+
+ " kind\n"
68+
+ " version\n"
69+
+ "\n"
70+
+ "dev.sigstore.rekor.v1.InclusionPromise\n"
71+
+ " signed_entry_timestamp\n"
72+
+ "\n"
73+
+ "dev.sigstore.rekor.v1.Checkpoint\n"
74+
+ " envelope\n"
75+
+ "\n"
76+
+ "dev.sigstore.rekor.v1.InclusionProof\n"
77+
+ " log_index\n"
78+
+ " root_hash\n"
79+
+ " tree_size\n"
80+
+ " hashes\n"
81+
+ " checkpoint\n"
82+
+ "\n"
83+
+ "dev.sigstore.rekor.v1.TransparencyLogEntry\n"
84+
+ " log_index\n"
85+
+ " log_id\n"
86+
+ " kind_version\n"
87+
+ " integrated_time\n"
88+
+ " inclusion_promise\n"
89+
+ " inclusion_proof\n"
90+
+ "\n"
6691
+ "dev.sigstore.common.v1.RFC3161SignedTimestamp\n"
6792
+ " signed_timestamp\n"
6893
+ "\n"
6994
+ "dev.sigstore.bundle.v1.VerificationMaterial\n"
7095
+ " content\n"
7196
+ "\n"
7297
+ "dev.sigstore.common.v1.MessageSignature\n"
73-
+ " message_digest\n"
7498
+ " signature\n"
7599
+ "\n"
76100
+ "dev.sigstore.bundle.v1.Bundle\n"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=0.2","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIICyDCCAk6gAwIBAgIUenpCAKVU2BcGqhnsCe0doys6ibwwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMxMDExMTQyODAzWhcNMjMxMDExMTQzODAzWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC0NNg9GUBzDJz2+i3Ffl1RiCwBekqVQvaYdjXJu6O7zd0WOofuc9zaBQ3WhE8o3EXH0Y5prD6bGajd2XEaMW6KOCAW0wggFpMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQULM+gqZePC3pfVoEclUSrtVPwGwgwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHQYDVR0RAQH/BBMwEYEPYXBwdUBnb29nbGUuY29tMCkGCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgorBgEEAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABix8jOOQAAAQDAEcwRQIhANeveOTEE7bNqX0UyaSYlSoOXtOntGNsEAO9FUaa/p/XAiBaMNu8aUovaFnoY+e3VIwsLfKoQls17tZbbH6sY+RZQjAKBggqhkjOPQQDAwNoADBlAjB63/BTZjqtZFoKpilCsvEzz4a6eBGf0dbnqhhDS/9ELuKjj2H7jQuNICxqED5p9vECMQDxUugt9pitEpNkOR4gnHdEAS726LUKhCic2QUhwbNJt/+Th+O7MyvyfoDjdlgGGMI="}]},"tlogEntries":[{"logIndex":"41958702","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"hashedrekord","version":"0.0.1"},"integratedTime":"1697034484","inclusionPromise":{"signedEntryTimestamp":"MEUCIFJST52F75FnhiyWApcgiQWgszyb/rf6J5wIRHEFb6LiAiEAgK/el1WsJLdpoRn0Pp9np9LPbY4ebQyyX03j8325Q48="},"inclusionProof":{"logIndex":"37795271","rootHash":"60ll7idWI1jYRZzxc+jKflYoW+4jWxgZaGR15ASsWt4=","treeSize":"37795272","hashes":["ZIU3md9RFYeb/QLydGOwhQ3ND+W4anIY65AcncCDATg=","FW20NiNv2Sqj0JVrVV0jxM2orMbQ9xh+VP1lTkSDl/Q=","S+iI/+iSshhUyd1Q7CAhJL1r0ztj4VmlB+Mz6EIYVnc=","IarfFu1ExpVVeg2h4fG1IWKYMs6BKCBnp46dlDN+iRA=","lMgRhGHIJoON6m0fp7dqo58UDMgHbflIjryXEw8/6GI=","wFBMBVQ5HXrKMuf5XOphsNlin7vSAfDFJnQq5YDxVrY=","FBAwNrNC3qhOjCcwdeB59P7bwQ40jtBUib0Y/j87xpE=","FTHlMv8QQyKAsmnkVAwWZSzP3mApzQXkiZrN+bQGg08=","5Lrnz5mdl9fOisdC44l0ljBJi9bwDSb/ArXsvCpgCiQ=","dA4IFz5UaTwkR83x1QUkZLq0UZJu9kRZJuSDsU7kVkE=","EqXDaDjlrIheY/2CQ/d0+pp4EBeqLlaq2/0ociyo5AY=","aWnEm9c/Gb8operqvTMd3WBQLe+yzT2W4Xt0HICt7Gw="],"checkpoint":{"envelope":"rekor.sigstore.dev - 2605736670972794746\n37795272\n60ll7idWI1jYRZzxc+jKflYoW+4jWxgZaGR15ASsWt4=\nTimestamp: 1697034484441201852\n\n— rekor.sigstore.dev wNI9ajBGAiEAlWUH2HSPa6IMIRBgFcIXph3Mj9xM70WR0VVADvGIl/oCIQCgfvUUjR/X5jewlqpAWI8NuJIicKpTG64vo6UM5fpSgQ==\n"}},"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJhMGNmYzcxMjcxZDZlMjc4ZTU3Y2QzMzJmZjk1N2MzZjcwNDNmZGRhMzU0YzRjYmIxOTBhMzBkNTZlZmEwMWJmIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJRG9xaGp4NWJ6Z1EwS3dNRDFtNzJDTGR4bTZMRHNhWU9oWE94L1NkS0NiRkFpRUExalY0V3kxUkhSN0pWS1FHUmZ5UGpLQTVzRUhWb0M0VUJnRHhpZjJ2Nmx3PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTjVSRU5EUVdzMlowRjNTVUpCWjBsVlpXNXdRMEZMVmxVeVFtTkhjV2h1YzBObE1HUnZlWE0yYVdKM2QwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcE5lRTFFUlhoTlZGRjVUMFJCZWxkb1kwNU5hazE0VFVSRmVFMVVVWHBQUkVGNlYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZETUU1T1p6bEhWVUo2UkVwNk1pdHBNMFptYkRGU2FVTjNRbVZyY1ZaUmRtRlpaR29LV0VwMU5rODNlbVF3VjA5dlpuVmpPWHBoUWxFelYyaEZPRzh6UlZoSU1GazFjSEpFTm1KSFlXcGtNbGhGWVUxWE5rdFBRMEZYTUhkblowWndUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZNVFN0bkNuRmFaVkJETTNCbVZtOUZZMnhWVTNKMFZsQjNSM2RuZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoUldVUldVakJTUVZGSUwwSkNUWGRGV1VWUVdWaENkMlJWUW01aU1qbHVZa2RWZFZreU9YUk5RMnRIUTJselIwRlJVVUpuTnpoM1FWRkZSUXBITW1nd1pFaENlazlwT0haWlYwNXFZak5XZFdSSVRYVmFNamwyV2pKNGJFeHRUblppVkVGeVFtZHZja0puUlVWQldVOHZUVUZGU1VKQ01FMUhNbWd3Q21SSVFucFBhVGgyV1ZkT2FtSXpWblZrU0UxMVdqSTVkbG95ZUd4TWJVNTJZbFJEUW1sbldVdExkMWxDUWtGSVYyVlJTVVZCWjFJNFFraHZRV1ZCUWpJS1FVNHdPVTFIY2tkNGVFVjVXWGhyWlVoS2JHNU9kMHRwVTJ3Mk5ETnFlWFF2TkdWTFkyOUJka3RsTms5QlFVRkNhWGc0YWs5UFVVRkJRVkZFUVVWamR3cFNVVWxvUVU1bGRtVlBWRVZGTjJKT2NWZ3dWWGxoVTFsc1UyOVBXSFJQYm5SSFRuTkZRVTg1UmxWaFlTOXdMMWhCYVVKaFRVNTFPR0ZWYjNaaFJtNXZDbGtyWlROV1NYZHpUR1pMYjFGc2N6RTNkRnBpWWtnMmMxa3JVbHBSYWtGTFFtZG5jV2hyYWs5UVVWRkVRWGRPYjBGRVFteEJha0kyTXk5Q1ZGcHFjWFFLV2tadlMzQnBiRU56ZGtWNmVqUmhObVZDUjJZd1pHSnVjV2hvUkZNdk9VVk1kVXRxYWpKSU4ycFJkVTVKUTNoeFJVUTFjRGwyUlVOTlVVUjRWWFZuZEFvNWNHbDBSWEJPYTA5U05HZHVTR1JGUVZNM01qWk1WVXRvUTJsak1sRlZhSGRpVGtwMEx5dFVhQ3RQTjAxNWRubG1iMFJxWkd4blIwZE5TVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In19fX0="}]},"messageSignature":{"signature":"MEUCIDoqhjx5bzgQ0KwMD1m72CLdxm6LDsaYOhXOx/SdKCbFAiEA1jV4Wy1RHR7JVKQGRfyPjKA5sEHVoC4UBgDxif2v6lw="}}

0 commit comments

Comments
 (0)