cache;
@@ -50,6 +54,17 @@ public void store(Config.KASInfo kasInfo) {
}
}
+/**
+ * A class representing Key Aggregation Service (KAS) information along with a timestamp.
+ *
+ * This class holds information related to KAS, as well as a timestamp indicating when the
+ * information was recorded or generated. It encapsulates two main attributes: {@code kasInfo}
+ * and {@code timestamp}.
+ *
+ * The {@code kasInfo} field is an instance of {@code Config.KASInfo}, which contains the KAS-specific
+ * data. The {@code timestamp} field is an instance of {@code LocalDateTime}, representing
+ * the date and time when the KAS information was recorded.
+ */
class TimeStampedKASInfo {
Config.KASInfo kasInfo;
LocalDateTime timestamp;
@@ -60,6 +75,13 @@ public TimeStampedKASInfo(Config.KASInfo kasInfo, LocalDateTime timestamp) {
}
}
+/**
+ * Represents a request for a Key Access Server (KAS) key.
+ * This class is used to request keys using a specified URL and algorithm.
+ *
+ * This class also overrides equals and hashCode methods
+ * to ensure proper functioning within hash-based collections.
+ */
class KASKeyRequest {
private String url;
private String algorithm;
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 f9434c6b..aca6d1cd 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java
@@ -10,6 +10,9 @@
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
+
+import io.opentdf.platform.sdk.TDF.AssertionException;
+
import org.apache.commons.codec.binary.Hex;
import org.erdtman.jcs.JsonCanonicalizer;
@@ -27,6 +30,10 @@
import java.util.List;
import java.util.Objects;
+/**
+ * The Manifest class represents a detailed structure encapsulating various aspects
+ * of data integrity, encryption, payload, and assertions within a certain context.
+ */
public class Manifest {
private static final String kAssertionHash = "assertionHash";
@@ -377,7 +384,7 @@ public void sign(final HashValues hashValues, final AssertionConfig.AssertionKey
public Assertion.HashValues verify(AssertionConfig.AssertionKey assertionKey)
throws ParseException, JOSEException {
if (binding == null) {
- throw new SDKException("Binding is null in assertion");
+ throw new AssertionException("Binding is null in assertion", this.id);
}
String signatureString = binding.signature;
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 9132e570..5695713a 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java
@@ -16,6 +16,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * The NanoTDF class provides methods to create and read NanoTDF (Tiny Data Format) files.
+ * The NanoTDF format is intended for securely encrypting small data payloads using elliptic-curve cryptography
+ * and authenticated encryption.
+ */
public class NanoTDF {
public static Logger logger = LoggerFactory.getLogger(NanoTDF.class);
@@ -83,7 +88,6 @@ public int createNanoTDF(ByteBuffer data, OutputStream outputStream,
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashOfSalt = digest.digest(MAGIC_NUMBER_AND_VERSION);
byte[] key = ECKeyPair.calculateHKDF(hashOfSalt, symmetricKey);
- logger.debug("createNanoTDF key is - {}", Base64.getEncoder().encodeToString(key));
// Encrypt policy
PolicyObject policyObject = createPolicyObject(nanoTDFConfig.attributes);
@@ -130,11 +134,13 @@ public int createNanoTDF(ByteBuffer data, OutputStream outputStream,
// Encrypt the data
byte[] actualIV = new byte[kIvPadding + kNanoTDFIvSize];
- byte[] iv = new byte[kNanoTDFIvSize];
- SecureRandom.getInstanceStrong().nextBytes(iv);
- System.arraycopy(iv, 0, actualIV, kIvPadding, iv.length);
+ do {
+ byte[] iv = new byte[kNanoTDFIvSize];
+ SecureRandom.getInstanceStrong().nextBytes(iv);
+ System.arraycopy(iv, 0, actualIV, kIvPadding, iv.length);
+ } while (Arrays.equals(actualIV, kEmptyIV)); // if match, we need to retry to prevent key + iv reuse with the policy
- byte[] cipherData = gcm.encrypt(actualIV, authTagSize, data.array(), 0, dataSize);
+ byte[] cipherData = gcm.encrypt(actualIV, authTagSize, data.array(), data.arrayOffset(), dataSize);
// Write the length of the payload as int24
int cipherDataLengthWithoutPadding = cipherData.length - kIvPadding;
@@ -168,7 +174,6 @@ public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream,
byte[] key = kas.unwrapNanoTDF(header.getECCMode().getEllipticCurveType(),
base64HeaderData,
kasUrl);
- logger.debug("readNanoTDF key is {}", Base64.getEncoder().encodeToString(key));
byte[] payloadLengthBuf = new byte[4];
nanoTDF.get(payloadLengthBuf, 1, 3);
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/PolicyObject.java b/sdk/src/main/java/io/opentdf/platform/sdk/PolicyObject.java
index 15d2d0bb..9d708699 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/PolicyObject.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/PolicyObject.java
@@ -2,6 +2,9 @@
import java.util.List;
+/**
+ * The PolicyObject class represents a policy with a unique identifier and a body containing data attributes.
+ */
public class PolicyObject {
static public class AttributeObject {
public String attribute;
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java
index fe2a8005..14affa4c 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java
@@ -34,11 +34,20 @@ public class SDK implements AutoCloseable {
private static final Logger log = LoggerFactory.getLogger(SDK.class);
+ /**
+ * Closes the SDK, including its associated services.
+ *
+ * @throws Exception if an error occurs while closing the services.
+ */
@Override
public void close() throws Exception {
services.close();
}
+ /**
+ * KAS (Key Access Service) interface to define methods related to key access and management.
+ * This interface extends AutoCloseable to allow for resource management during close operations.
+ */
public interface KAS extends AutoCloseable {
Config.KASInfo getPublicKey(Config.KASInfo kasInfo);
@@ -51,7 +60,10 @@ public interface KAS extends AutoCloseable {
KASKeyCache getKeyCache();
}
- // TODO: add KAS
+ /**
+ * The Services interface provides access to various gRPC service stubs and a Key Authority Service (KAS).
+ * It extends the AutoCloseable interface, allowing for the release of resources when no longer needed.
+ */
public interface Services extends AutoCloseable {
AuthorizationServiceFutureStub authorization();
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 4068b5e0..8c6b1804 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java
@@ -69,7 +69,6 @@ public SDKBuilder sslFactory(SSLFactory sslFactory) {
* Add SSL Context with trusted certs from certDirPath
*
* @param certsDirPath Path to a directory containing .pem or .crt trusted certs
- * @return
*/
public SDKBuilder sslFactoryFromDirectory(String certsDirPath) throws Exception {
File certsDir = new File(certsDirPath);
@@ -91,7 +90,6 @@ public SDKBuilder sslFactoryFromDirectory(String certsDirPath) throws Exception
*
* @param keystorePath Path to keystore
* @param keystorePassword Password to keystore
- * @return
*/
public SDKBuilder sslFactoryFromKeyStore(String keystorePath, String keystorePassword) {
this.sslFactory = SSLFactory.builder().withDefaultTrustMaterial().withSystemTrustMaterial()
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SDKException.java b/sdk/src/main/java/io/opentdf/platform/sdk/SDKException.java
index 2d0b390d..58e7adbe 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/SDKException.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDKException.java
@@ -1,5 +1,10 @@
package io.opentdf.platform.sdk;
+/**
+ * SDKException serves as a custom exception class for handling errors
+ * specific to the SDK's operations and processes. It extends
+ * RuntimeException, making it an unchecked exception.
+ */
public class SDKException extends RuntimeException {
public SDKException(String message, Exception reason) {
super(message, reason);
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 ba7c5b97..4c6eb6da 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
@@ -28,10 +28,23 @@
import java.util.*;
import java.util.concurrent.ExecutionException;
+/**
+ * The TDF class is responsible for handling operations related to
+ * Trusted Data Format (TDF). It includes methods to create and load
+ * TDF objects, as well as utility functions to handle cryptographic
+ * operations and configurations.
+ */
public class TDF {
private final long maximumSize;
+ /**
+ * Constructs a new TDF instance using the default maximum input size defined by MAX_TDF_INPUT_SIZE.
+ *
+ * This constructor is primarily used to initialize the TDF object with the standard maximum
+ * input size, which controls the maximum size of the input data that can be processed.
+ * For test purposes, an alternative constructor allows for setting a custom maximum input size.
+ */
public TDF() {
this(MAX_TDF_INPUT_SIZE);
}
@@ -106,30 +119,48 @@ public FailedToCreateGMAC(String errorMessage) {
}
}
- public static class NotValidateRootSignature extends RuntimeException {
- public NotValidateRootSignature(String errorMessage) {
+ public static class TDFReadFailed extends RuntimeException {
+ public TDFReadFailed(String errorMessage) {
+ super(errorMessage);
+ }
+ }
+
+ public static class TamperException extends SDKException {
+ public TamperException(String errorMessage) {
+ super("[tamper detected] "+errorMessage);
+ }
+ }
+
+ public static class RootSignatureValidationException extends TamperException {
+ public RootSignatureValidationException(String errorMessage) {
super(errorMessage);
}
}
- public static class SegmentSizeMismatch extends RuntimeException {
+ public static class SegmentSizeMismatch extends TamperException {
public SegmentSizeMismatch(String errorMessage) {
super(errorMessage);
}
}
- public static class SegmentSignatureMismatch extends RuntimeException {
+ public static class SegmentSignatureMismatch extends TamperException {
public SegmentSignatureMismatch(String errorMessage) {
super(errorMessage);
}
}
- public static class TDFReadFailed extends RuntimeException {
- public TDFReadFailed(String errorMessage) {
+ public static class KasBadRequestException extends TamperException {
+ public KasBadRequestException(String errorMessage) {
super(errorMessage);
}
}
+ public static class AssertionException extends TamperException {
+ public AssertionException(String errorMessage, String id) {
+ super("assertion id: "+ id + "; " + errorMessage);
+ }
+ }
+
public static class EncryptedMetadata {
private String ciphertext;
private String iv;
@@ -537,9 +568,15 @@ public List defaultKases(TDFConfig config) {
return defk;
}
+ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas)
+ throws DecoderException, IOException, ParseException, NoSuchAlgorithmException, JOSEException {
+ return loadTDF(tdf, kas, new Config.TDFReaderConfig());
+ }
+
+
public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
- Config.AssertionVerificationKeys... assertionVerificationKeys)
- throws NotValidateRootSignature, SegmentSizeMismatch,
+ Config.TDFReaderConfig tdfReaderConfig)
+ throws RootSignatureValidationException, SegmentSizeMismatch,
IOException, FailedToCreateGMAC, JOSEException, ParseException, NoSuchAlgorithmException, DecoderException {
TDFReader tdfReader = new TDFReader(tdf);
@@ -647,7 +684,7 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
}
if (rootSignature.compareTo(rootSigValue) != 0) {
- throw new NotValidateRootSignature("root signature validation failed");
+ throw new RootSignatureValidationException("root signature validation failed");
}
int segmentSize = manifest.encryptionInformation.integrityInformation.segmentSizeDefault;
@@ -659,10 +696,16 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
// Validate assertions
for (var assertion : manifest.assertions) {
+ // Skip assertion verification if disabled
+ if (tdfReaderConfig.disableAssertionVerification) {
+ break;
+ }
+
// Set default to HS256
var assertionKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, payloadKey);
- if (assertionVerificationKeys != null && assertionVerificationKeys.length > 0) {
- var keyForAssertion = assertionVerificationKeys[0].getKey(assertion.id);
+ Config.AssertionVerificationKeys assertionVerificationKeys = tdfReaderConfig.assertionVerificationKeys;
+ if (!assertionVerificationKeys.isEmpty()) {
+ var keyForAssertion = assertionVerificationKeys.getKey(assertion.id);
if (keyForAssertion != null) {
assertionKey = keyForAssertion;
}
@@ -676,11 +719,11 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
var encodeSignature = Base64.getEncoder().encodeToString(signature.getBytes());
if (!Objects.equals(hashOfAssertion, hashValues.getAssertionHash())) {
- throw new SDKException("assertion hash mismatch");
+ throw new AssertionException("assertion hash mismatch", assertion.id);
}
if (!Objects.equals(encodeSignature, hashValues.getSignature())) {
- throw new SDKException("failed integrity check on assertion signature");
+ throw new AssertionException("failed integrity check on assertion signature", assertion.id);
}
}
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java
index 24b05940..6691888a 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java
@@ -12,6 +12,11 @@
import static io.opentdf.platform.sdk.TDFWriter.TDF_MANIFEST_FILE_NAME;
import static io.opentdf.platform.sdk.TDFWriter.TDF_PAYLOAD_FILE_NAME;
+/**
+ * TDFReader is responsible for reading and processing Trusted Data Format (TDF) files.
+ * The class initializes with a TDF file channel, extracts the manifest and payload entries,
+ * and provides methods to retrieve the manifest content, read payload bytes, and read policy objects.
+ */
public class TDFReader {
private final ZipReader.Entry manifestEntry;
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDFWriter.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDFWriter.java
index a5f3509b..048822f6 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/TDFWriter.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDFWriter.java
@@ -4,6 +4,10 @@
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+/**
+ * The TDFWriter class provides functionalities for creating a TDF (Trusted Data Format) archive.
+ * This includes appending a manifest file and appending payload data to the archive.
+ */
public class TDFWriter {
public static final String TDF_PAYLOAD_FILE_NAME = "0.payload";
public static final String TDF_MANIFEST_FILE_NAME = "0.manifest.json";
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 17bc51c5..cb6d5ffd 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java
@@ -12,6 +12,12 @@
import java.util.ArrayList;
import java.util.List;
+/**
+ * The ZipReader class provides functionality to read basic ZIP file
+ * structures, such as the End of Central Directory Record and the
+ * Local File Header. This class supports standard ZIP archives as well
+ * as ZIP64 format.
+ */
public class ZipReader {
public static final Logger logger = LoggerFactory.getLogger(ZipReader.class);
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ZipWriter.java b/sdk/src/main/java/io/opentdf/platform/sdk/ZipWriter.java
index d775a0a8..71aea34c 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/ZipWriter.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/ZipWriter.java
@@ -9,6 +9,10 @@
import java.util.ArrayList;
import java.util.zip.CRC32;
+/**
+ * The ZipWriter class provides functionalities to create ZIP archive files.
+ * It writes files and data to an underlying output stream in the ZIP file format.
+ */
public class ZipWriter {
private static final int ZIP_VERSION = 0x2D;
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/package-info.java b/sdk/src/main/java/io/opentdf/platform/sdk/package-info.java
new file mode 100644
index 00000000..cd3ae44a
--- /dev/null
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/package-info.java
@@ -0,0 +1,19 @@
+/**
+ * The io.opentdf.platform.sdk package provides a comprehensive set of
+ * classes, interfaces, enums, and exceptions designed for interacting with
+ * the OpenTDF platform. At its core, the {@link io.opentdf.platform.sdk.SDK} class
+ * serves as the centerpiece for this package, representing a software development kit
+ * that facilitates integration with the OpenTDF services. The package also includes
+ * classes for various encryption and decryption operations such as {@link io.opentdf.platform.sdk.AesGcm}
+ * and its inner classes, as well as support for assertions and configurations through
+ * classes like {@link io.opentdf.platform.sdk.AssertionConfig} and {@link io.opentdf.platform.sdk.Config}.
+ * Additionally, utility classes such as {@link io.opentdf.platform.sdk.CryptoUtils} and
+ * functionality for reading and writing TDF files are provided via {@link io.opentdf.platform.sdk.TDFReader}
+ * and {@link io.opentdf.platform.sdk.TDFWriter}. The package also defines a collection of
+ * enums for various configurations and state definitions, alongside a comprehensive
+ * set of exceptions to handle different error scenarios, ensuring robust and secure
+ * operations within the OpenTDF ecosystem.
+ *
+ * @since 1.0
+ */
+package io.opentdf.platform.sdk;
\ No newline at end of file
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java b/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java
index fb44663c..11a41bc8 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java
@@ -38,7 +38,7 @@ public void createAndDecryptTdfIT() throws Exception {
tdf.createTDF(plainTextInputStream, tdfOutputStream, config, sdk.kas(), sdk.attributes());
var unwrappedData = new java.io.ByteArrayOutputStream();
- var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), sdk.kas());
+ var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), sdk.kas());
reader.readPayload(unwrappedData);
assertThat(unwrappedData.toString(StandardCharsets.UTF_8)).isEqualTo("text");
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 e4fed930..acd1fd15 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
@@ -2,10 +2,12 @@
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;
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
import org.junit.jupiter.api.BeforeAll;
@@ -29,7 +31,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
-
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -136,8 +137,10 @@ void testSimpleTDFEncryptAndDecrypt() throws Exception {
key);
var unwrappedData = new ByteArrayOutputStream();
+ Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig(
+ Config.withAssertionVerificationKeys(assertionVerificationKeys));
var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas,
- assertionVerificationKeys);
+ readerConfig);
assertThat(reader.getManifest().payload.mimeType).isEqualTo("application/octet-stream");
reader.readPayload(unwrappedData);
@@ -192,8 +195,10 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception {
new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.RS256, keypair.getPublic()));
var unwrappedData = new ByteArrayOutputStream();
+ Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig(
+ Config.withAssertionVerificationKeys(assertionVerificationKeys));
var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas,
- assertionVerificationKeys);
+ readerConfig);
reader.readPayload(unwrappedData);
assertThat(unwrappedData.toString(StandardCharsets.UTF_8))
@@ -201,6 +206,61 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception {
.isEqualTo(plainText);
}
+ @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();
+ assertionConfig.id = assertion1Id;
+ assertionConfig.type = AssertionConfig.Type.BaseAssertion;
+ assertionConfig.scope = AssertionConfig.Scope.TrustedDataObj;
+ assertionConfig.appliesToState = AssertionConfig.AppliesToState.Unencrypted;
+ assertionConfig.statement = new AssertionConfig.Statement();
+ assertionConfig.statement.format = "base64binary";
+ assertionConfig.statement.schema = "text";
+ assertionConfig.statement.value = "ICAgIDxlZGoOkVkaD4=";
+ assertionConfig.assertionKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.RS256,
+ keypair.getPrivate());
+
+ Config.TDFConfig config = Config.newTDFConfig(
+ Config.withAutoconfigure(false),
+ Config.withKasInformation(getKASInfos()),
+ Config.withAssertionConfig(assertionConfig));
+
+ String plainText = "this is extremely sensitive stuff!!!";
+ InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes());
+ ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
+
+ TDF tdf = new TDF();
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+
+ var assertionVerificationKeys = new Config.AssertionVerificationKeys();
+ assertionVerificationKeys.keys.put(assertion1Id,
+ new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.RS256, keypair.getPublic()));
+
+ var unwrappedData = new ByteArrayOutputStream();
+ assertThrows(JOSEException.class, () -> {
+ tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas,
+ new Config.TDFReaderConfig());
+ });
+
+ // try with assertion verification disabled and not passing the assertion verification keys
+ Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig(
+ Config.withDisableAssertionVerification(true));
+ var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas,
+ readerConfig);
+ reader.readPayload(unwrappedData);
+
+ assertThat(unwrappedData.toString(StandardCharsets.UTF_8))
+ .withFailMessage("extracted data does not match")
+ .isEqualTo(plainText);
+ }
@Test
void testSimpleTDFWithAssertionWithHS256() throws Exception {
@@ -244,7 +304,8 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception {
tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
var unwrappedData = new ByteArrayOutputStream();
- var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas);
+ var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()),
+ kas, new Config.TDFReaderConfig());
reader.readPayload(unwrappedData);
assertThat(unwrappedData.toString(StandardCharsets.UTF_8))
@@ -272,6 +333,62 @@ 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];
+ secureRandom.nextBytes(key);
+
+ String assertion1Id = "assertion1";
+ var assertionConfig1 = new AssertionConfig();
+ assertionConfig1.id = assertion1Id;
+ assertionConfig1.type = AssertionConfig.Type.BaseAssertion;
+ assertionConfig1.scope = AssertionConfig.Scope.TrustedDataObj;
+ assertionConfig1.appliesToState = AssertionConfig.AppliesToState.Unencrypted;
+ assertionConfig1.statement = new AssertionConfig.Statement();
+ assertionConfig1.statement.format = "base64binary";
+ assertionConfig1.statement.schema = "text";
+ assertionConfig1.statement.value = "ICAgIDxlZGoOkVkaD4=";
+ assertionConfig1.assertionKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key);
+
+ Config.TDFConfig config = Config.newTDFConfig(
+ Config.withAutoconfigure(false),
+ Config.withKasInformation(getKASInfos()),
+ Config.withAssertionConfig(assertionConfig1));
+
+ String plainText = "this is extremely sensitive stuff!!!";
+ InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes());
+ ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
+
+ TDF tdf = new TDF();
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub);
+
+ byte[] notkey = new byte[32];
+ secureRandom.nextBytes(notkey);
+ var assertionVerificationKeys = new Config.AssertionVerificationKeys();
+ assertionVerificationKeys.defaultKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256,
+ notkey);
+ Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig(
+ Config.withAssertionVerificationKeys(assertionVerificationKeys));
+
+ var unwrappedData = new ByteArrayOutputStream();
+ Reader reader;
+ try {
+ reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, readerConfig);
+ throw new RuntimeException("assertion verify key error thrown");
+
+ } catch (SDKException e) {
+ assertThat(e).hasMessageContaining("verify");
+ }
+ }
+
@Test
public void testCreatingTDFWithMultipleSegments() throws Exception {