diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java index 0307566..479d7b0 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java @@ -41,7 +41,9 @@ public class Manifest { private static final String kAssertionHash = "assertionHash"; private static final String kAssertionSignature = "assertionSig"; - private static final Gson gson = new Gson(); + private static final Gson gson = new GsonBuilder() + .registerTypeAdapter(Manifest.class, new ManifestDeserializer()) + .create(); @Override public boolean equals(Object o) { @@ -458,6 +460,19 @@ private JWSVerifier createVerifier(AssertionConfig.AssertionKey assertionKey) th public Payload payload; public List assertions = new ArrayList<>(); + public static class ManifestDeserializer implements JsonDeserializer { + @Override + public Manifest deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + // Let Gson handle the default deserialization of the object first + Manifest manifest = new Gson().fromJson(json, typeOfT); + // Now check if the `assertions` field is null and replace it with an empty list if necessary + if (manifest.assertions == null) { + manifest.assertions = new ArrayList<>(); // Replace null with empty list + } + return manifest; + } + } + private static Manifest readManifest(Reader reader) { return gson.fromJson(reader, Manifest.class); } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java index 4c6eb6d..124ebe0 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -1,11 +1,13 @@ package io.opentdf.platform.sdk; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.nimbusds.jose.*; import io.opentdf.platform.policy.Value; import io.opentdf.platform.policy.attributes.AttributesServiceGrpc.AttributesServiceFutureStub; import io.opentdf.platform.sdk.Config.TDFConfig; +import io.opentdf.platform.sdk.Manifest.ManifestDeserializer; import io.opentdf.platform.sdk.Autoconfigure.AttributeValueFQN; import io.opentdf.platform.sdk.Config.KASInfo; @@ -75,7 +77,9 @@ public TDF() { private static final SecureRandom sRandom = new SecureRandom(); - private static final Gson gson = new Gson(); + private static final Gson gson = new GsonBuilder() + .registerTypeAdapter(Manifest.class, new ManifestDeserializer()) + .create(); public class SplitKeyException extends IOException { public SplitKeyException(String errorMessage) { diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/ManifestTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/ManifestTest.java index cc169d2..7a3a9c7 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/ManifestTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/ManifestTest.java @@ -4,11 +4,14 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import io.opentdf.platform.sdk.Manifest.ManifestDeserializer; + import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; +import static org.junit.Assert.assertNotNull; import static org.junit.jupiter.api.Assertions.assertEquals; public class ManifestTest { @@ -62,7 +65,9 @@ void testManifestMarshalAndUnMarshal() { "}"; GsonBuilder gsonBuilder = new GsonBuilder(); - Gson gson = gsonBuilder.setPrettyPrinting().create(); + Gson gson = gsonBuilder.setPrettyPrinting() + .registerTypeAdapter(Manifest.class, new ManifestDeserializer()) + .create(); Manifest manifest = gson.fromJson(kManifestJsonFromTDF, Manifest.class); // Test payload @@ -90,4 +95,69 @@ void testManifestMarshalAndUnMarshal() { assertEquals(manifest, deserializedAgain, "something changed when we deserialized -> serialized -> deserialized"); } + + @Test + void testAssertionNull() { + String kManifestJsonFromTDF = "{\n" + + " \"encryptionInformation\": {\n" + + " \"integrityInformation\": {\n" + + " \"encryptedSegmentSizeDefault\": 1048604,\n" + + " \"rootSignature\": {\n" + + " \"alg\": \"HS256\",\n" + + " \"sig\": \"N2Y1ZjJlYWE4N2EzNjc2Nzc3NzgxNGU2ZGE1NmI4NDNhZTI5ZWY5NDc2OGI1ZTMzYTIyMTU4MDBlZTY3NzQzNA==\"\n" + + " },\n" + + " \"segmentHashAlg\": \"GMAC\",\n" + + " \"segmentSizeDefault\": 1048576,\n" + + " \"segments\": [\n" + + " {\n" + + " \"encryptedSegmentSize\": 41,\n" + + " \"hash\": \"ZWEyZTkwYjZiZThmYWZhNzg5ZmNjOWIyZTA2Njg5OTQ=\",\n" + + " \"segmentSize\": 1048576\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"keyAccess\": [\n" + + " {\n" + + " \"policyBinding\": {\n" + + " \"alg\": \"HS256\",\n" + + " \"hash\": \"YTgzNThhNzc5NWRhMjdjYThlYjk4ZmNmODliNzc2Y2E5ZmZiZDExZDQ3OTM5ODFjZTRjNmE3MmVjOTUzZTFlMA==\"\n" + + " },\n" + + " \"protocol\": \"kas\",\n" + + " \"type\": \"wrapped\",\n" + + " \"url\": \"http://localhost:65432/kas\",\n" + + " \"wrappedKey\": \"dJ3PdscXWvLv/juSkL7EMhl4lgLSBfI9EeoG2ct6NeSwPkPm/ieMF6ryDQjGeqZttoLlx2qBCVpik/BooGd/FtpYMIF/7a5RFTJ3G+o4Lww/zG6zIgV2APEPO+Gp7ORlFyMNJfn6Tj8ChTweKBqfXEXLihTV6sTZFtsWjdV96Z4KXbLe8tGpkXBpUAsSlmjcDJ920vrqnp3dvt2GwfmAiRWYCMXxnqUECqN5kVXMJywcvHatv2ZJSA/ixjDOrix+MocDJ69K/yFA17DXgfjf5X4SLyS0XgaZcXsdACBb+ogBlPw6vAbBrAyqI0Vi1msMRYNDS+FTl1yWEXl1HpyyCw==\"\n" + + " }\n" + + " ],\n" + + " \"method\": {\n" + + " \"algorithm\": \"AES-256-GCM\",\n" + + " \"isStreamable\": true,\n" + + " \"iv\": \"tozen81HLtZktNOP\"\n" + + " },\n" + + " \"policy\": \"eyJib2R5Ijp7ImRhdGFBdHRyaWJ1dGVzIjpbXSwiZGlzc2VtIjpbXX0sInV1aWQiOiJiNTM3MDllMy03NmE3LTRmYzctOGEwZi1mZDBhNjcyNmVhM2YifQ==\",\n" + + " \"type\": \"split\"\n" + + " },\n" + + " \"payload\": {\n" + + " \"isEncrypted\": true,\n" + + " \"mimeType\": \"application/octet-stream\",\n" + + " \"protocol\": \"zip\",\n" + + " \"type\": \"reference\",\n" + + " \"url\": \"0.payload\"\n" + + " },\n" + + " \"assertions\": null\n"+ + "}"; + + GsonBuilder gsonBuilder = new GsonBuilder(); + Gson gson = gsonBuilder.setPrettyPrinting() + .registerTypeAdapter(Manifest.class, new ManifestDeserializer()) + .create(); + Manifest manifest = gson.fromJson(kManifestJsonFromTDF, Manifest.class); + + // Test payload for sanity check + assertEquals(manifest.payload.url, "0.payload"); + assertEquals(manifest.payload.isEncrypted, true); + // Test assertion deserialization + assertNotNull(manifest.assertions); + assertEquals(manifest.assertions.size(), 0); + + } } diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/ZipReaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/ZipReaderTest.java index 1eb5530..5c47710 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/ZipReaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/ZipReaderTest.java @@ -1,5 +1,9 @@ package io.opentdf.platform.sdk; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import io.opentdf.platform.sdk.Manifest.ManifestDeserializer; + import org.apache.commons.compress.archivers.zip.Zip64Mode; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; @@ -36,7 +40,11 @@ public void testReadingExistingZip() throws Exception { if (entry.getName().endsWith(".json")) { entry.getData().transferTo(stream); var data = stream.toString(StandardCharsets.UTF_8); - var map = new Gson().fromJson(data, Map.class); + var gson = new GsonBuilder() + .registerTypeAdapter(Manifest.class, new ManifestDeserializer()) + .create(); + var map = gson.fromJson(data, Map.class); + assertThat(map.get("encryptionInformation")).isNotNull(); } }