diff --git a/cmdline/pom.xml b/cmdline/pom.xml index 897f5658..55916094 100644 --- a/cmdline/pom.xml +++ b/cmdline/pom.xml @@ -4,7 +4,7 @@ io.opentdf.platform sdk-pom - 0.7.4 + 0.7.5-SNAPSHOT cmdline diff --git a/examples/pom.xml b/examples/pom.xml index 28915c44..922f6b8b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -4,12 +4,12 @@ sdk-pom io.opentdf.platform - 0.7.4 + 0.7.5-SNAPSHOT io.opentdf.platform examples - 0.7.4 + 0.7.5-SNAPSHOT examples diff --git a/pom.xml b/pom.xml index e1b0ec0f..d37b49cf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.opentdf.platform sdk-pom - 0.7.4 + 0.7.5-SNAPSHOT io.opentdf.platform:sdk-pom OpenTDF Java SDK https://github.com/opentdf/java-sdk diff --git a/sdk/pom.xml b/sdk/pom.xml index db01dbc2..5dfe5539 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -6,7 +6,7 @@ sdk-pom io.opentdf.platform - 0.7.4 + 0.7.5-SNAPSHOT jar 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 c7d4f092..1f44854a 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java @@ -91,12 +91,14 @@ AssertionConfig.AssertionKey getKey(String key) { public static class TDFReaderConfig { // Optional Map of Assertion Verification Keys - AssertionVerificationKeys assertionVerificationKeys; + AssertionVerificationKeys assertionVerificationKeys = new AssertionVerificationKeys(); + boolean disableAssertionVerification; } @SafeVarargs public static TDFReaderConfig newTDFReaderConfig(Consumer... options) { TDFReaderConfig config = new TDFReaderConfig(); + config.disableAssertionVerification = false; for (Consumer option : options) { option.accept(config); } @@ -108,6 +110,10 @@ public static Consumer withAssertionVerificationKeys( return (TDFReaderConfig config) -> config.assertionVerificationKeys = assertionVerificationKeys; } + public static Consumer withDisableAssertionVerification(boolean disable) { + return (TDFReaderConfig config) -> config.disableAssertionVerification = disable; + } + public static class TDFConfig { public Boolean autoconfigure; public int defaultSegmentSize; 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 41f1eb40..4c6eb6da 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -568,8 +568,14 @@ 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) + Config.TDFReaderConfig tdfReaderConfig) throws RootSignatureValidationException, SegmentSizeMismatch, IOException, FailedToCreateGMAC, JOSEException, ParseException, NoSuchAlgorithmException, DecoderException { @@ -690,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; } 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 48264b5c..ae4746b2 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java @@ -2,6 +2,7 @@ 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; @@ -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))