diff --git a/sdk/fuzz.sh b/sdk/fuzz.sh
new file mode 100755
index 00000000..88a803ae
--- /dev/null
+++ b/sdk/fuzz.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+set -e
+
+tests=("fuzzNanoTDF", "fuzzTDF", "fuzzZipRead")
+base_seed_dir="src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/"
+
+for test in "${tests[@]}"; do
+ seed_dir="${base_seed_dir}${test}"
+ echo "Running $test fuzzing with seeds from $seed_dir"
+ mvn verify -P fuzz -Djazzer.testDir=$seed_dir
+done
diff --git a/sdk/pom.xml b/sdk/pom.xml
index 678c78b0..6685f513 100644
--- a/sdk/pom.xml
+++ b/sdk/pom.xml
@@ -9,6 +9,10 @@
0.7.5
jar
+
+ 0.22.1
+ https://github.com/CodeIntelligenceTesting/jazzer/releases/download/v${jazzer.version}
+
@@ -121,6 +125,18 @@
4.13.2
test
+
+ com.code-intelligence
+ jazzer-api
+ ${jazzer.version}
+ test
+
+
+ com.code-intelligence
+ jazzer-junit
+ ${jazzer.version}
+ test
+
org.apache.commons
commons-compress
@@ -307,4 +323,110 @@
-
\ No newline at end of file
+
+
+
+ fuzz
+
+ false
+
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 3.1.0
+
+
+ download-and-unpack-jazzer
+ process-test-classes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.4.0
+
+
+ copy-dependencies
+ process-test-classes
+
+ copy-dependencies
+
+
+ ${project.build.directory}/dependency-jars
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 3.1.0
+
+
+ run-jazzer-fuzzing
+ verify
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run
+
+
+
+
+
+
+
+
+
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java
index 7b7d824f..a8b58c4b 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java
@@ -21,6 +21,8 @@ public class Config {
public static final int TDF3_KEY_SIZE = 2048;
public static final int DEFAULT_SEGMENT_SIZE = 2 * 1024 * 1024; // 2mb
+ public static final int MAX_SEGMENT_SIZE = DEFAULT_SEGMENT_SIZE * 2;
+ public static final int MIN_SEGMENT_SIZE = 16 * 1024; // not currently enforced in parsing due to existing payloads in testing
public static final String KAS_PUBLIC_KEY_PATH = "/kas_public_key";
public static final String DEFAULT_MIME_TYPE = "application/octet-stream";
public static final int MAX_COLLECTION_ITERATION = (1 << 24) - 1;
@@ -228,6 +230,12 @@ public static Consumer withMetaData(String metaData) {
}
public static Consumer withSegmentSize(int size) {
+ if (size > MAX_SEGMENT_SIZE) {
+ throw new IllegalArgumentException("Segment size " + size + " exceeds the maximum " + MAX_SEGMENT_SIZE);
+ } else if (size < MIN_SEGMENT_SIZE) {
+ throw new IllegalArgumentException("Segment size " + size + " is under the minimum " + MIN_SEGMENT_SIZE);
+ }
+
return (TDFConfig config) -> config.defaultSegmentSize = size;
}
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 479d7b0e..3f7fd8e8 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java
@@ -260,7 +260,7 @@ static public class Payload {
public String url;
public String protocol;
public String mimeType;
- public Boolean isEncrypted;
+ public boolean isEncrypted;
@Override
public boolean equals(Object o) {
@@ -473,8 +473,41 @@ public Manifest deserialize(JsonElement json, Type typeOfT, JsonDeserializationC
}
}
- private static Manifest readManifest(Reader reader) {
- return gson.fromJson(reader, Manifest.class);
+ protected static Manifest readManifest(Reader reader) {
+ Manifest result = gson.fromJson(reader, Manifest.class);
+ if (result == null) {
+ throw new IllegalArgumentException("Manifest is null");
+ } else if (result.payload == null) {
+ throw new IllegalArgumentException("Manifest with null payload");
+ } else if (result.encryptionInformation == null) {
+ throw new IllegalArgumentException("Manifest with null encryptionInformation");
+ } else if (result.encryptionInformation.integrityInformation == null) {
+ throw new IllegalArgumentException("Manifest with null integrityInformation");
+ } else if (result.encryptionInformation.integrityInformation.rootSignature == null) {
+ throw new IllegalArgumentException("Manifest with null rootSignature");
+ } else if (result.encryptionInformation.integrityInformation.rootSignature.algorithm == null
+ || result.encryptionInformation.integrityInformation.rootSignature.signature == null) {
+ throw new IllegalArgumentException("Manifest with invalid rootSignature");
+ } else if (result.encryptionInformation.integrityInformation.segments == null) {
+ throw new IllegalArgumentException("Manifest with null segments");
+ } else if (result.encryptionInformation.keyAccessObj == null) {
+ throw new IllegalArgumentException("Manifest with null keyAccessObj");
+ } else if (result.encryptionInformation.policy == null) {
+ throw new IllegalArgumentException("Manifest with null policy");
+ }
+
+ for (Manifest.Segment segment : result.encryptionInformation.integrityInformation.segments) {
+ if (segment == null || segment.hash == null) {
+ throw new IllegalArgumentException("Invalid integrity segment");
+ }
+ }
+ for (Manifest.KeyAccess keyAccess : result.encryptionInformation.keyAccessObj) {
+ if (keyAccess == null) {
+ throw new IllegalArgumentException("Invalid null KeyAccess in manifest");
+ }
+ }
+
+ return result;
}
static PolicyObject readPolicyObject(Reader reader) {
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java
index f893762a..5f92069c 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java
@@ -252,7 +252,7 @@ PolicyObject createPolicyObject(List attributes) {
PolicyObject policyObject = new PolicyObject();
policyObject.body = new PolicyObject.Body();
policyObject.uuid = UUID.randomUUID().toString();
- policyObject.body.dataAttributes = new ArrayList<>();
+ policyObject.body.dataAttributes = new ArrayList<>(attributes.size());
policyObject.body.dissem = new ArrayList<>();
for (String attribute : attributes) {
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java b/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java
index 8c6b1804..9f4e2cd9 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java
@@ -74,7 +74,7 @@ public SDKBuilder sslFactoryFromDirectory(String certsDirPath) throws Exception
File certsDir = new File(certsDirPath);
File[] certFiles = certsDir.listFiles((dir, name) -> name.endsWith(".pem") || name.endsWith(".crt"));
logger.info("Loading certificates from: " + certsDir.getAbsolutePath());
- List certStreams = new ArrayList<>();
+ List certStreams = new ArrayList<>(certFiles.length);
for (File certFile : certFiles) {
certStreams.add(new FileInputStream(certFile));
}
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 124ebe0f..4bbd8008 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
@@ -23,6 +23,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.StringReader;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.security.*;
@@ -188,7 +189,7 @@ PolicyObject createPolicyObject(List attributes
PolicyObject policyObject = new PolicyObject();
policyObject.body = new PolicyObject.Body();
policyObject.uuid = UUID.randomUUID().toString();
- policyObject.body.dataAttributes = new ArrayList<>();
+ policyObject.body.dataAttributes = new ArrayList<>(attributes.size());
policyObject.body.dissem = new ArrayList<>();
for (Autoconfigure.AttributeValueFQN attribute : attributes) {
@@ -208,7 +209,6 @@ private void prepareManifest(Config.TDFConfig tdfConfig, SDK.KAS kas) {
PolicyObject policyObject = createPolicyObject(tdfConfig.attributes);
String base64PolicyObject = encoder
.encodeToString(gson.toJson(policyObject).getBytes(StandardCharsets.UTF_8));
- List symKeys = new ArrayList<>();
Map latestKASInfo = new HashMap<>();
if (tdfConfig.splitPlan == null || tdfConfig.splitPlan.isEmpty()) {
// Default split plan: Split keys across all KASes
@@ -261,6 +261,7 @@ private void prepareManifest(Config.TDFConfig tdfConfig, SDK.KAS kas) {
}
}
+ List symKeys = new ArrayList<>(splitIDs.size());
for (String splitID : splitIDs) {
// Symmetric key
byte[] symKey = new byte[GCM_KEY_SIZE];
@@ -358,6 +359,10 @@ public void readPayload(OutputStream outputStream) throws TDFReadFailed,
MessageDigest digest = MessageDigest.getInstance("SHA-256");
for (Manifest.Segment segment : manifest.encryptionInformation.integrityInformation.segments) {
+ if (segment.encryptedSegmentSize > Config.MAX_SEGMENT_SIZE) {
+ throw new IllegalStateException("Segment size " + segment.encryptedSegmentSize + " exceeded limit " + Config.MAX_SEGMENT_SIZE);
+ } // MIN_SEGMENT_SIZE NOT validated out due to tests needing small segment sizes with existing payloads
+
byte[] readBuf = new byte[(int) segment.encryptedSegmentSize];
int bytesRead = tdfReader.readPayloadBytes(readBuf);
@@ -520,8 +525,7 @@ public TDFObject createTDF(InputStream payload,
tdfObject.manifest.payload.url = TDFWriter.TDF_PAYLOAD_FILE_NAME;
tdfObject.manifest.payload.isEncrypted = true;
- List signedAssertions = new ArrayList<>();
-
+ List signedAssertions = new ArrayList<>(tdfConfig.assertionConfigList.size());
for (var assertionConfig : tdfConfig.assertionConfigList) {
var assertion = new Manifest.Assertion();
assertion.id = assertionConfig.id;
@@ -585,7 +589,8 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
TDFReader tdfReader = new TDFReader(tdf);
String manifestJson = tdfReader.manifest();
- Manifest manifest = gson.fromJson(manifestJson, Manifest.class);
+ // use Manifest.readManifest in order to validate the Manifest input
+ Manifest manifest = Manifest.readManifest(new StringReader(manifestJson));
byte[] payloadKey = new byte[GCM_KEY_SIZE];
String unencryptedMetadata = null;
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java b/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java
index cb6d5ffd..cf0b5772 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java
@@ -3,6 +3,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
@@ -25,17 +26,17 @@ public class ZipReader {
public static final int ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIZE = 20;
final ByteBuffer longBuf = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN);
- private Long readLong() throws IOException {
+ private long readLong() throws IOException {
longBuf.clear();
if (this.zipChannel.read(longBuf) != 8) {
- return null;
+ throw new InvalidZipException("Expected long value");
}
longBuf.flip();
return longBuf.getLong();
}
final ByteBuffer intBuf = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN);
- private Integer readInt() throws IOException {
+ private Integer readInteger() throws IOException {
intBuf.clear();
if (this.zipChannel.read(intBuf) != 4) {
return null;
@@ -43,13 +44,20 @@ private Integer readInt() throws IOException {
intBuf.flip();
return intBuf.getInt();
}
+ private int readInt() throws IOException {
+ Integer result = readInteger();
+ if (result == null) {
+ throw new InvalidZipException("Expected int value");
+ }
+ return result.intValue();
+ }
final ByteBuffer shortBuf = ByteBuffer.allocate(Short.BYTES).order(ByteOrder.LITTLE_ENDIAN);
- private Short readShort() throws IOException {
+ private short readShort() throws IOException {
shortBuf.clear();
if (this.zipChannel.read(shortBuf) != 2) {
- return null;
+ throw new InvalidZipException("Expected short value");
}
shortBuf.flip();
return shortBuf.getShort();
@@ -79,8 +87,8 @@ CentralDirectoryRecord readEndOfCentralDirectory() throws IOException {
while (eoCDRStart >= 0) {
zipChannel.position(eoCDRStart);
- int signature = readInt();
- if (signature == END_OF_CENTRAL_DIRECTORY_SIGNATURE) {
+ Integer signature = readInteger();
+ if (signature == null || signature == END_OF_CENTRAL_DIRECTORY_SIGNATURE) {
if (logger.isDebugEnabled()) {
logger.debug("Found end of central directory signature at {}", zipChannel.position() - Integer.BYTES);
}
@@ -113,8 +121,8 @@ CentralDirectoryRecord readEndOfCentralDirectory() throws IOException {
private CentralDirectoryRecord extractZIP64CentralDirectoryInfo() throws IOException {
// buffer's position at the start of the Central Directory
- int signature = readInt();
- if (signature != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE) {
+ Integer signature = readInteger();
+ if (signature == null || signature != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE) {
throw new InvalidZipException("Invalid Zip64 End of Central Directory Record Signature");
}
@@ -157,7 +165,8 @@ public String getName() {
public InputStream getData() throws IOException {
zipChannel.position(offsetToLocalHeader);
- if (readInt() != LOCAL_FILE_HEADER_SIGNATURE) {
+ Integer signature = readInteger();
+ if (signature == null || signature != LOCAL_FILE_HEADER_SIGNATURE) {
throw new InvalidZipException("Invalid Local Header Signature");
}
zipChannel.position(zipChannel.position()
@@ -184,8 +193,8 @@ public int read() throws IOException {
return -1;
}
setChannelPosition();
- while (buf.position() != buf.capacity()) {
- if (zipChannel.read(buf) < 0) {
+ while (buf.hasRemaining()) {
+ if (zipChannel.read(buf) <= 0) {
return -1;
}
}
@@ -222,8 +231,8 @@ public int read(byte[] b, int off, int len) throws IOException {
}
}
public Entry readCentralDirectoryFileHeader() throws IOException {
- int signature = readInt();
- if (signature != CENTRAL_FILE_HEADER_SIGNATURE) {
+ Integer signature = readInteger();
+ if (signature == null || signature != CENTRAL_FILE_HEADER_SIGNATURE) {
throw new InvalidZipException("Invalid Central Directory File Header Signature");
}
short versionMadeBy = readShort();
@@ -244,8 +253,10 @@ public Entry readCentralDirectoryFileHeader() throws IOException {
long relativeOffsetOfLocalHeader = readInt();
ByteBuffer fileName = ByteBuffer.allocate(fileNameLength);
- while (fileName.position() != fileName.capacity()) {
- zipChannel.read(fileName);
+ while (fileName.hasRemaining()) {
+ if (zipChannel.read(fileName) <= 0) {
+ throw new EOFException("Unexpected EOF when reading filename of length: " + fileNameLength);
+ }
}
// Parse the extra field
@@ -277,7 +288,6 @@ public Entry readCentralDirectoryFileHeader() throws IOException {
return new Entry(fileName.array(), relativeOffsetOfLocalHeader, uncompressedSize);
}
-
public ZipReader(SeekableByteChannel channel) throws IOException {
zipChannel = channel;
var centralDirectoryRecord = readEndOfCentralDirectory();
@@ -286,11 +296,11 @@ public ZipReader(SeekableByteChannel channel) throws IOException {
entries.add(readCentralDirectoryFileHeader());
}
}
-
+
final SeekableByteChannel zipChannel;
final ArrayList entries = new ArrayList<>();
public List getEntries() {
return entries;
}
-}
\ No newline at end of file
+}
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/nanotdf/PolicyInfo.java b/sdk/src/main/java/io/opentdf/platform/sdk/nanotdf/PolicyInfo.java
index c4b93f34..78b96aea 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/nanotdf/PolicyInfo.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/nanotdf/PolicyInfo.java
@@ -29,7 +29,8 @@ public PolicyInfo(ByteBuffer buffer, ECCMode eccMode) {
byte[] policyLengthBuf = new byte[Short.BYTES];
buffer.get(policyLengthBuf);
- short policyLength = ByteBuffer.wrap(policyLengthBuf).getShort();
+ // read short value into int to prevent possible overflow resulting in negative length
+ int policyLength = ByteBuffer.wrap(policyLengthBuf).getShort();
if (this.type == NanoTDFType.PolicyType.EMBEDDED_POLICY_PLAIN_TEXT ||
this.type == NanoTDFType.PolicyType.EMBEDDED_POLICY_ENCRYPTED) {
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/ConfigTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/ConfigTest.java
index cae23434..b3bc3f77 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/ConfigTest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/ConfigTest.java
@@ -4,6 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
class ConfigTest {
@@ -46,8 +47,18 @@ void withMetaData_shouldSetMetaData() {
@Test
void withSegmentSize_shouldSetSegmentSize() {
- Config.TDFConfig config = Config.newTDFConfig(Config.withSegmentSize(1024));
- assertEquals(1024, config.defaultSegmentSize);
+ Config.TDFConfig config = Config.newTDFConfig(Config.withSegmentSize(Config.MIN_SEGMENT_SIZE));
+ assertEquals(Config.MIN_SEGMENT_SIZE, config.defaultSegmentSize);
+ }
+
+ @Test
+ void withSegmentSize_shouldIgnoreSegmentSize() {
+ try {
+ Config.withSegmentSize(1024);
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
}
@Test
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/Fuzzing.java b/sdk/src/test/java/io/opentdf/platform/sdk/Fuzzing.java
new file mode 100644
index 00000000..5fcb90fc
--- /dev/null
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/Fuzzing.java
@@ -0,0 +1,69 @@
+package io.opentdf.platform.sdk;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.security.NoSuchAlgorithmException;
+import java.text.ParseException;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
+
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+import com.code_intelligence.jazzer.junit.FuzzTest;
+import com.google.gson.JsonParseException;
+import com.nimbusds.jose.JOSEException;
+
+import io.opentdf.platform.sdk.TDF.FailedToCreateGMAC;
+import io.opentdf.platform.sdk.TDF.Reader;
+
+public class Fuzzing {
+ private static final String TEST_DURATION = "600s";
+ private static final OutputStream IGNORE_OUTPUT_STREAM = new OutputStream() {
+ @Override
+ public void write(int b) {
+ // ignored
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) {
+ // ignored
+ }
+ };
+
+ @FuzzTest(maxDuration=TEST_DURATION)
+ public void fuzzNanoTDF(FuzzedDataProvider data) throws IOException {
+ byte[] fuzzBytes = data.consumeRemainingAsBytes();
+ NanoTDF nanoTDF = new NanoTDF();
+ nanoTDF.readNanoTDF(ByteBuffer.wrap(fuzzBytes), IGNORE_OUTPUT_STREAM, NanoTDFTest.kas);
+ }
+
+ @FuzzTest(maxDuration=TEST_DURATION)
+ public void fuzzTDF(FuzzedDataProvider data) throws FailedToCreateGMAC, NoSuchAlgorithmException, IOException, JOSEException, ParseException, DecoderException {
+ byte[] fuzzBytes = data.consumeRemainingAsBytes();
+ byte[] key = new byte[32]; // use consistent zero key for performance and so fuzz can relate to seed
+ var assertionVerificationKeys = new Config.AssertionVerificationKeys();
+ assertionVerificationKeys.defaultKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key);
+ Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig(
+ Config.withAssertionVerificationKeys(assertionVerificationKeys));
+ TDF tdf = new TDF();
+
+ try {
+ Reader reader = tdf.loadTDF(new SeekableInMemoryByteChannel(fuzzBytes), TDFTest.kas, readerConfig);
+
+ reader.readPayload(IGNORE_OUTPUT_STREAM);
+ } catch (SDKException | InvalidZipException | JsonParseException | IOException | IllegalArgumentException e) {
+ // expected failure cases
+ }
+ }
+
+ @FuzzTest(maxDuration=TEST_DURATION)
+ public void fuzzZipRead(FuzzedDataProvider data) {
+ byte[] fuzzBytes = data.consumeRemainingAsBytes();
+ try {
+ ZipReaderTest.testReadingZipChannel(new SeekableInMemoryByteChannel(fuzzBytes), false);
+ } catch (InvalidZipException | IllegalArgumentException | JsonParseException | IOException e) {
+ // cases which are expected with invalid fuzzed inputs
+ }
+ }
+}
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java
index 87bae33b..cd4c056d 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java
@@ -37,7 +37,7 @@ public class NanoTDFTest {
private static final String KID = "r1";
- private static SDK.KAS kas = new SDK.KAS() {
+ protected static SDK.KAS kas = new SDK.KAS() {
@Override
public void close() throws Exception {
}
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
index acd1fd15..326809f3 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
@@ -1,11 +1,6 @@
package io.opentdf.platform.sdk;
-import com.google.common.util.concurrent.ListenableFuture;
-
import com.nimbusds.jose.JOSEException;
-import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsRequest;
-import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsResponse;
-import io.opentdf.platform.policy.attributes.AttributesServiceGrpc;
import io.opentdf.platform.sdk.Config.KASInfo;
import io.opentdf.platform.sdk.TDF.Reader;
import io.opentdf.platform.sdk.nanotdf.NanoTDFType;
@@ -35,13 +30,7 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TDFTest {
-
- @BeforeEach
- public void setup() {
- attributeGrpcStub = mock(AttributesServiceGrpc.AttributesServiceFutureStub.class);
- }
-
- private static SDK.KAS kas = new SDK.KAS() {
+ protected static SDK.KAS kas = new SDK.KAS() {
@Override
public void close() {
}
@@ -84,8 +73,6 @@ public KASKeyCache getKeyCache() {
}
};
- AttributesServiceGrpc.AttributesServiceFutureStub attributeGrpcStub;
-
private static ArrayList keypairs = new ArrayList<>();
@BeforeAll
@@ -97,12 +84,6 @@ static void createKeypairs() {
@Test
void testSimpleTDFEncryptAndDecrypt() throws Exception {
-
- ListenableFuture resp1 = mock(ListenableFuture.class);
- lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build());
- lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class)))
- .thenReturn(resp1);
-
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[32];
secureRandom.nextBytes(key);
@@ -130,7 +111,7 @@ void testSimpleTDFEncryptAndDecrypt() throws Exception {
ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
TDF tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null);
var assertionVerificationKeys = new Config.AssertionVerificationKeys();
assertionVerificationKeys.defaultKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256,
@@ -139,6 +120,7 @@ void testSimpleTDFEncryptAndDecrypt() throws Exception {
var unwrappedData = new ByteArrayOutputStream();
Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig(
Config.withAssertionVerificationKeys(assertionVerificationKeys));
+
var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas,
readerConfig);
assertThat(reader.getManifest().payload.mimeType).isEqualTo("application/octet-stream");
@@ -158,12 +140,6 @@ void testSimpleTDFEncryptAndDecrypt() throws Exception {
@Test
void testSimpleTDFWithAssertionWithRS256() throws Exception {
-
- ListenableFuture resp1 = mock(ListenableFuture.class);
- lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build());
- lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class)))
- .thenReturn(resp1);
-
String assertion1Id = "assertion1";
var keypair = CryptoUtils.generateRSAKeypair();
var assertionConfig = new AssertionConfig();
@@ -188,7 +164,7 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception {
ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
TDF tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null);
var assertionVerificationKeys = new Config.AssertionVerificationKeys();
assertionVerificationKeys.keys.put(assertion1Id,
@@ -208,12 +184,6 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception {
@Test
void testWithAssertionVerificationDisabled() throws Exception {
-
- ListenableFuture resp1 = mock(ListenableFuture.class);
- lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build());
- lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class)))
- .thenReturn(resp1);
-
String assertion1Id = "assertion1";
var keypair = CryptoUtils.generateRSAKeypair();
var assertionConfig = new AssertionConfig();
@@ -238,7 +208,7 @@ void testWithAssertionVerificationDisabled() throws Exception {
ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
TDF tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null);
var assertionVerificationKeys = new Config.AssertionVerificationKeys();
assertionVerificationKeys.keys.put(assertion1Id,
@@ -263,12 +233,6 @@ void testWithAssertionVerificationDisabled() throws Exception {
}
@Test
void testSimpleTDFWithAssertionWithHS256() throws Exception {
-
- ListenableFuture resp1 = mock(ListenableFuture.class);
- lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build());
- lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class)))
- .thenReturn(resp1);
-
String assertion1Id = "assertion1";
var assertionConfig1 = new AssertionConfig();
assertionConfig1.id = assertion1Id;
@@ -301,7 +265,7 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception {
ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
TDF tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null);
var unwrappedData = new ByteArrayOutputStream();
var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()),
@@ -335,12 +299,6 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception {
@Test
void testSimpleTDFWithAssertionWithHS256Failure() throws Exception {
-
- ListenableFuture resp1 = mock(ListenableFuture.class);
- lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build());
- lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class)))
- .thenReturn(resp1);
-
// var keypair = CryptoUtils.generateRSAKeypair();
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[32];
@@ -368,7 +326,7 @@ void testSimpleTDFWithAssertionWithHS256Failure() throws Exception {
ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
TDF tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null);
byte[] notkey = new byte[32];
secureRandom.nextBytes(notkey);
@@ -391,27 +349,20 @@ void testSimpleTDFWithAssertionWithHS256Failure() throws Exception {
@Test
public void testCreatingTDFWithMultipleSegments() throws Exception {
-
- ListenableFuture resp1 = mock(ListenableFuture.class);
- lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build());
- lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class)))
- .thenReturn(resp1);
-
var random = new Random();
Config.TDFConfig config = Config.newTDFConfig(
Config.withAutoconfigure(false),
Config.withKasInformation(getKASInfos()),
- // use a random segment size that makes sure that we will use multiple segments
- Config.withSegmentSize(1 + random.nextInt(20)));
+ Config.withSegmentSize(Config.MIN_SEGMENT_SIZE));
- // data should be bigger than the largest segment
- var data = new byte[21 + random.nextInt(2048)];
+ // data should be large enough to have multiple complete and a partial segment
+ var data = new byte[(int)(Config.MIN_SEGMENT_SIZE * 2.8)];
random.nextBytes(data);
var plainTextInputStream = new ByteArrayInputStream(data);
var tdfOutputStream = new ByteArrayOutputStream();
var tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null);
var unwrappedData = new ByteArrayOutputStream();
var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas);
reader.readPayload(unwrappedData);
@@ -424,11 +375,6 @@ public void testCreatingTDFWithMultipleSegments() throws Exception {
@Test
public void testCreatingTooLargeTDF() throws Exception {
- ListenableFuture resp1 = mock(ListenableFuture.class);
- lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build());
- lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class)))
- .thenReturn(resp1);
-
var random = new Random();
var maxSize = random.nextInt(1024);
var numReturned = new AtomicInteger(0);
@@ -466,9 +412,9 @@ public void write(byte[] b, int off, int len) {
var tdfConfig = Config.newTDFConfig(
Config.withAutoconfigure(false),
Config.withKasInformation(getKASInfos()),
- Config.withSegmentSize(1 + random.nextInt(128)));
+ Config.withSegmentSize(Config.MIN_SEGMENT_SIZE));
assertThrows(TDF.DataSizeNotSupported.class,
- () -> tdf.createTDF(is, os, tdfConfig, kas, attributeGrpcStub),
+ () -> tdf.createTDF(is, os, tdfConfig, kas, null),
"didn't throw an exception when we created TDF that was too large");
assertThat(numReturned.get())
.withFailMessage("test returned the wrong number of bytes")
@@ -477,12 +423,6 @@ public void write(byte[] b, int off, int len) {
@Test
public void testCreateTDFWithMimeType() throws Exception {
-
- ListenableFuture resp1 = mock(ListenableFuture.class);
- lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build());
- lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class)))
- .thenReturn(resp1);
-
final String mimeType = "application/pdf";
Config.TDFConfig config = Config.newTDFConfig(
@@ -495,7 +435,7 @@ public void testCreateTDFWithMimeType() throws Exception {
ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
TDF tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null);
var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas);
assertThat(reader.getManifest().payload.mimeType).isEqualTo(mimeType);
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 5c47710c..f9819193 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,4 @@
package io.opentdf.platform.sdk;
-import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.opentdf.platform.sdk.Manifest.ManifestDeserializer;
@@ -14,6 +13,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@@ -32,21 +32,31 @@ public class ZipReaderTest {
public void testReadingExistingZip() throws Exception {
try (RandomAccessFile raf = new RandomAccessFile("src/test/resources/sample.txt.tdf", "r")) {
var fileChannel = raf.getChannel();
- var zipReader = new ZipReader(fileChannel);
- var entries = zipReader.getEntries();
+ ZipReaderTest.testReadingZipChannel(fileChannel, true);
+ }
+ }
+
+ protected static void testReadingZipChannel(SeekableByteChannel fileChannel, boolean test) throws IOException {
+ var zipReader = new ZipReader(fileChannel);
+ var entries = zipReader.getEntries();
+ if (test) {
assertThat(entries.size()).isEqualTo(2);
- for (var entry: entries) {
- var stream = new ByteArrayOutputStream();
- if (entry.getName().endsWith(".json")) {
- entry.getData().transferTo(stream);
- var data = stream.toString(StandardCharsets.UTF_8);
- var gson = new GsonBuilder()
- .registerTypeAdapter(Manifest.class, new ManifestDeserializer())
- .create();
- var map = gson.fromJson(data, Map.class);
-
+ }
+ for (var entry: entries) {
+ var stream = new ByteArrayOutputStream();
+ if (entry.getName().endsWith(".json")) {
+ entry.getData().transferTo(stream);
+ var data = stream.toString(StandardCharsets.UTF_8);
+ var gson = new GsonBuilder()
+ .registerTypeAdapter(Manifest.class, new ManifestDeserializer())
+ .create();
+ var map = gson.fromJson(data, Map.class);
+
+ if (test) {
assertThat(map.get("encryptionInformation")).isNotNull();
}
+ } else if (!test) {
+ entry.getData().transferTo(stream); // still invoke getData logic
}
}
}
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzNanoTDF/sample.ntdf b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzNanoTDF/sample.ntdf
new file mode 100644
index 00000000..7dd6dcbb
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzNanoTDF/sample.ntdf differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-1 b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-1
new file mode 100644
index 00000000..5e0b5c99
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-1 differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-2 b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-2
new file mode 100644
index 00000000..de7914cd
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-2 differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-3 b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-3
new file mode 100644
index 00000000..bfe79806
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-3 differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-4 b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-4
new file mode 100644
index 00000000..da6463a9
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-4 differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-NullKeyAccessObj b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-NullKeyAccessObj
new file mode 100644
index 00000000..d55d907b
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-NullKeyAccessObj differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-NullSegment b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-NullSegment
new file mode 100644
index 00000000..4537beda
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/crash-InvalidManifest-NullSegment differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/sample.tdf b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/sample.tdf
new file mode 100644
index 00000000..cc27081a
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzTDF/sample.tdf differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/crash-NullSignature b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/crash-NullSignature
new file mode 100644
index 00000000..24ea0b1c
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/crash-NullSignature differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/crash-f39ad8416aef7cf275f84683aaa0efd15f24272a b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/crash-f39ad8416aef7cf275f84683aaa0efd15f24272a
new file mode 100644
index 00000000..2fc1da22
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/crash-f39ad8416aef7cf275f84683aaa0efd15f24272a differ
diff --git a/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/sample.txt.tdf b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/sample.txt.tdf
new file mode 100644
index 00000000..2bb81265
Binary files /dev/null and b/sdk/src/test/resources/io/opentdf/platform/sdk/FuzzingInputs/fuzzZipRead/sample.txt.tdf differ