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
1 change: 1 addition & 0 deletions config/forbiddenApis.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
com.google.protobuf.util.JsonFormat#parser() @ Use dev.sigstore.json.ProtoJson#parser() instead
dev.sigstore.http.HttpClients#newHttpTransport(dev.sigstore.http.HttpParams) @ Use dev.sigstore.http.HttpClients#newRequestFactory(...) instead
com.google.gson.GsonBuilder @ Use dev.sigstore.json.GsonSupplier.GSON instead
4 changes: 2 additions & 2 deletions sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import com.google.gson.JsonSyntaxException;
import com.google.protobuf.ByteString;
import dev.sigstore.bundle.Bundle;
import dev.sigstore.bundle.Bundle.MessageSignature;
Expand All @@ -40,6 +39,7 @@
import dev.sigstore.fulcio.client.FulcioVerificationException;
import dev.sigstore.fulcio.client.FulcioVerifier;
import dev.sigstore.fulcio.client.UnsupportedAlgorithmException;
import dev.sigstore.json.JsonParseException;
import dev.sigstore.oidc.client.OidcClients;
import dev.sigstore.oidc.client.OidcException;
import dev.sigstore.oidc.client.OidcToken;
Expand Down Expand Up @@ -697,7 +697,7 @@ public Bundle attest(String payload) throws KeylessSignerException {
InTotoPayload inTotoPayload;
try {
inTotoPayload = InTotoPayload.from(payload);
} catch (JsonSyntaxException jse) {
} catch (JsonParseException jse) {
throw new IllegalArgumentException("Payload is not a valid in-toto statement");
}

Expand Down
23 changes: 20 additions & 3 deletions sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import dev.sigstore.encryption.signers.Verifiers;
import dev.sigstore.fulcio.client.FulcioVerificationException;
import dev.sigstore.fulcio.client.FulcioVerifier;
import dev.sigstore.json.JsonParseException;
import dev.sigstore.proto.common.v1.HashAlgorithm;
import dev.sigstore.proto.rekor.v2.DSSELogEntryV002;
import dev.sigstore.proto.rekor.v2.HashedRekordLogEntryV002;
Expand Down Expand Up @@ -267,7 +268,12 @@ private void checkMessageSignature(
}

// recreate the log entry and check if it matches what was provided in the entry
String version = rekorEntry.getBodyDecoded().getApiVersion();
String version;
try {
version = rekorEntry.getBodyDecoded().getApiVersion();
} catch (JsonParseException ex) {
throw new KeylessVerificationException("Could not extract body from log entry");
}
if ("0.0.1".equals(version)) {
try {
RekorTypes.getHashedRekordV001(rekorEntry);
Expand Down Expand Up @@ -346,7 +352,13 @@ private void checkDsseEnvelope(
+ dsseEnvelope.getPayloadType()
+ "'");
}
InTotoPayload payload = InTotoPayload.from(dsseEnvelope);

InTotoPayload payload;
try {
payload = InTotoPayload.from(dsseEnvelope);
} catch (JsonParseException jpe) {
throw new KeylessVerificationException("Could not parse DSSE payload", jpe);
}

// find one sha256 hash in the subject list that matches the artifact hash
if (payload.getSubject().stream()
Expand Down Expand Up @@ -392,7 +404,12 @@ private void checkDsseEnvelope(
throw new KeylessVerificationException("Signature could not be processed", se);
}

String version = rekorEntry.getBodyDecoded().getApiVersion();
String version;
try {
version = rekorEntry.getBodyDecoded().getApiVersion();
} catch (JsonParseException ex) {
throw new KeylessVerificationException("Could not extract body from log entry");
}
if ("0.0.1".equals(version)) {
Dsse rekorDsse;
try {
Expand Down
48 changes: 29 additions & 19 deletions sigstore-java/src/main/java/dev/sigstore/bundle/BundleWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import dev.sigstore.json.JsonParseException;
import dev.sigstore.proto.ProtoMutators;
import dev.sigstore.proto.bundle.v1.TimestampVerificationData;
import dev.sigstore.proto.bundle.v1.VerificationMaterial;
Expand Down Expand Up @@ -146,26 +147,35 @@ private static VerificationMaterial.Builder buildVerificationMaterial(Bundle bun
}

private static TransparencyLogEntry.Builder buildTlogEntries(RekorEntry entry) {
TransparencyLogEntry.Builder transparencyLogEntry =
TransparencyLogEntry.newBuilder()
.setLogIndex(entry.getLogIndex())
.setLogId(LogId.newBuilder().setKeyId(ByteString.fromHex(entry.getLogID())))
.setKindVersion(
KindVersion.newBuilder()
.setKind(entry.getBodyDecoded().getKind())
.setVersion(entry.getBodyDecoded().getApiVersion()))
.setIntegratedTime(entry.getIntegratedTime())
.setCanonicalizedBody(ByteString.copyFrom(Base64.getDecoder().decode(entry.getBody())));
if (entry.getVerification().getSignedEntryTimestamp() != null) {
transparencyLogEntry.setInclusionPromise(
InclusionPromise.newBuilder()
.setSignedEntryTimestamp(
ByteString.copyFrom(
Base64.getDecoder()
.decode(entry.getVerification().getSignedEntryTimestamp()))));
try {
TransparencyLogEntry.Builder transparencyLogEntry =
TransparencyLogEntry.newBuilder()
.setLogIndex(entry.getLogIndex())
.setLogId(LogId.newBuilder().setKeyId(ByteString.fromHex(entry.getLogID())))
.setKindVersion(
KindVersion.newBuilder()
.setKind(entry.getBodyDecoded().getKind())
.setVersion(entry.getBodyDecoded().getApiVersion()))
.setIntegratedTime(entry.getIntegratedTime())
.setCanonicalizedBody(
ByteString.copyFrom(Base64.getDecoder().decode(entry.getBody())));
if (entry.getVerification().getSignedEntryTimestamp() != null) {
transparencyLogEntry.setInclusionPromise(
InclusionPromise.newBuilder()
.setSignedEntryTimestamp(
ByteString.copyFrom(
Base64.getDecoder()
.decode(entry.getVerification().getSignedEntryTimestamp()))));
}
addInclusionProof(transparencyLogEntry, entry);
return transparencyLogEntry;
} catch (JsonParseException jpe) {
throw new IllegalStateException(
"Trying to serialize a bad bundle: rekor entry body is invalid: '"
+ entry.getBody()
+ "'",
jpe);
}
addInclusionProof(transparencyLogEntry, entry);
return transparencyLogEntry;
}

private static void addInclusionProof(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.google.gson.JsonElement;
import dev.sigstore.bundle.Bundle.DsseEnvelope;
import dev.sigstore.json.JsonParseException;
import java.util.List;
import java.util.Map;
import org.immutables.gson.Gson;
Expand Down Expand Up @@ -51,11 +52,11 @@ interface Subject {
Map<String, String> getDigest();
}

static InTotoPayload from(String payload) {
static InTotoPayload from(String payload) throws JsonParseException {
return GSON.get().fromJson(payload, InTotoPayload.class);
}

static InTotoPayload from(DsseEnvelope dsseEnvelope) {
static InTotoPayload from(DsseEnvelope dsseEnvelope) throws JsonParseException {
return from(dsseEnvelope.getPayloadAsString());
}
}
71 changes: 71 additions & 0 deletions sigstore-java/src/main/java/dev/sigstore/json/GsonChecked.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2025 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.json;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import java.io.Reader;
import java.lang.reflect.Type;

/** A Gson wrapper that catches all runtime exceptions. */
public final class GsonChecked {

Gson gson;

GsonChecked(Gson gson) {
this.gson = gson;
}

public <T> T fromJson(JsonElement element, Class<T> classOfT) throws JsonParseException {
try {
return gson.fromJson(element, classOfT);
} catch (RuntimeException e) {
throw new JsonParseException(e);
}
}

public <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException {
try {
return gson.fromJson(json, classOfT);
} catch (RuntimeException e) {
throw new JsonParseException(e);
}
}

public <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
try {
return gson.fromJson(json, typeOfT);
} catch (RuntimeException e) {
throw new JsonParseException(e);
}
}

public <T> T fromJson(Reader reader, Class<T> classOfT) throws JsonParseException {
try {
return gson.fromJson(reader, classOfT);
} catch (RuntimeException e) {
throw new JsonParseException(e);
}
}

public <T> String toJson(T src) {
return gson.toJson(src);
}

public <T> void toJson(T src, Appendable writer) {
gson.toJson(src, writer);
}
}
65 changes: 34 additions & 31 deletions sigstore-java/src/main/java/dev/sigstore/json/GsonSupplier.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.google.gson.*;
import dev.sigstore.dsse.GsonAdaptersInTotoPayload;
import dev.sigstore.forbidden.SuppressForbidden;
import dev.sigstore.rekor.client.GsonAdaptersRekorEntry;
import dev.sigstore.rekor.client.GsonAdaptersRekorEntryBody;
import dev.sigstore.tuf.model.*;
Expand All @@ -30,42 +31,44 @@
* requests between sigstore and this client -- and should probably be used for any api call to
* sigstore that expects JSON.
*/
public enum GsonSupplier implements Supplier<Gson> {
@SuppressForbidden(reason = "GsonBuilder")
public enum GsonSupplier implements Supplier<GsonChecked> {
GSON;

@SuppressWarnings("ImmutableEnumChecker")
private final Gson gson =
new GsonBuilder()
.registerTypeAdapter(byte[].class, new GsonByteArrayAdapter())
.registerTypeAdapter(
LocalDateTime.class,
(JsonDeserializer<LocalDateTime>)
(json, type, jsonDeserializationContext) ->
ZonedDateTime.parse(json.getAsJsonPrimitive().getAsString())
.toLocalDateTime())
// Immutables generated GSON Adapters in alphabetical order
.registerTypeAdapterFactory(new GsonAdaptersDelegations())
.registerTypeAdapterFactory(new GsonAdaptersDelegationRole())
.registerTypeAdapterFactory(new GsonAdaptersHashes())
.registerTypeAdapterFactory(new GsonAdaptersKey())
.registerTypeAdapterFactory(new GsonAdaptersRekorEntry())
.registerTypeAdapterFactory(new GsonAdaptersRekorEntryBody())
.registerTypeAdapterFactory(new GsonAdaptersRoot())
.registerTypeAdapterFactory(new GsonAdaptersRootMeta())
.registerTypeAdapterFactory(new GsonAdaptersRootRole())
.registerTypeAdapterFactory(new GsonAdaptersSignature())
.registerTypeAdapterFactory(new GsonAdaptersSnapshot())
.registerTypeAdapterFactory(new GsonAdaptersSnapshotMeta())
.registerTypeAdapterFactory(new GsonAdaptersTargets())
.registerTypeAdapterFactory(new GsonAdaptersTargetMeta())
.registerTypeAdapterFactory(new GsonAdaptersTimestamp())
.registerTypeAdapterFactory(new GsonAdaptersTimestampMeta())
.registerTypeAdapterFactory(new GsonAdaptersInTotoPayload())
.disableHtmlEscaping()
.create();
private final GsonChecked gson =
new GsonChecked(
new GsonBuilder()
.registerTypeAdapter(byte[].class, new GsonByteArrayAdapter())
.registerTypeAdapter(
LocalDateTime.class,
(JsonDeserializer<LocalDateTime>)
(json, type, jsonDeserializationContext) ->
ZonedDateTime.parse(json.getAsJsonPrimitive().getAsString())
.toLocalDateTime())
// Immutables generated GSON Adapters in alphabetical order
.registerTypeAdapterFactory(new GsonAdaptersDelegations())
.registerTypeAdapterFactory(new GsonAdaptersDelegationRole())
.registerTypeAdapterFactory(new GsonAdaptersHashes())
.registerTypeAdapterFactory(new GsonAdaptersKey())
.registerTypeAdapterFactory(new GsonAdaptersRekorEntry())
.registerTypeAdapterFactory(new GsonAdaptersRekorEntryBody())
.registerTypeAdapterFactory(new GsonAdaptersRoot())
.registerTypeAdapterFactory(new GsonAdaptersRootMeta())
.registerTypeAdapterFactory(new GsonAdaptersRootRole())
.registerTypeAdapterFactory(new GsonAdaptersSignature())
.registerTypeAdapterFactory(new GsonAdaptersSnapshot())
.registerTypeAdapterFactory(new GsonAdaptersSnapshotMeta())
.registerTypeAdapterFactory(new GsonAdaptersTargets())
.registerTypeAdapterFactory(new GsonAdaptersTargetMeta())
.registerTypeAdapterFactory(new GsonAdaptersTimestamp())
.registerTypeAdapterFactory(new GsonAdaptersTimestampMeta())
.registerTypeAdapterFactory(new GsonAdaptersInTotoPayload())
.disableHtmlEscaping()
.create());

@Override
public Gson get() {
public GsonChecked get() {
return gson;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2025 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.json;

public class JsonParseException extends Exception {
public JsonParseException(Throwable cause) {
super(cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package dev.sigstore.rekor.client;

import dev.sigstore.json.JsonParseException;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -57,5 +58,5 @@ Optional<RekorEntry> getEntry(HashedRekordRequest hashedRekordRequest)
*/
List<String> searchEntry(
String email, String hash, String publicKeyFormat, String publicKeyContent)
throws IOException;
throws IOException, JsonParseException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.google.api.client.util.Preconditions;
import dev.sigstore.http.HttpClients;
import dev.sigstore.http.HttpParams;
import dev.sigstore.json.JsonParseException;
import dev.sigstore.trustroot.Service;
import java.io.IOException;
import java.net.URI;
Expand Down Expand Up @@ -130,7 +131,7 @@ public Optional<RekorEntry> getEntry(String UUID) throws IOException, RekorParse
@Override
public List<String> searchEntry(
String email, String hash, String publicKeyFormat, String publicKeyContent)
throws IOException {
throws IOException, JsonParseException {
URI rekorSearchEndpoint = uri.resolve(REKOR_INDEX_SEARCH_PATH);

HashMap<String, Object> publicKeyParams = null;
Expand Down
Loading
Loading