Skip to content

fix(sdk): option to disable assertion verification #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 29, 2024
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
8 changes: 7 additions & 1 deletion sdk/src/main/java/io/opentdf/platform/sdk/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<TDFReaderConfig>... options) {
TDFReaderConfig config = new TDFReaderConfig();
config.disableAssertionVerification = false;
for (Consumer<TDFReaderConfig> option : options) {
option.accept(config);
}
Expand All @@ -108,6 +110,10 @@ public static Consumer<TDFReaderConfig> withAssertionVerificationKeys(
return (TDFReaderConfig config) -> config.assertionVerificationKeys = assertionVerificationKeys;
}

public static Consumer<TDFReaderConfig> withDisableAssertionVerification(boolean disable) {
return (TDFReaderConfig config) -> config.disableAssertionVerification = disable;
}

public static class TDFConfig {
public Boolean autoconfigure;
public int defaultSegmentSize;
Expand Down
18 changes: 15 additions & 3 deletions sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
Original file line number Diff line number Diff line change
Expand Up @@ -550,8 +550,14 @@ public List<String> 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 NotValidateRootSignature, SegmentSizeMismatch,
IOException, FailedToCreateGMAC, JOSEException, ParseException, NoSuchAlgorithmException, DecoderException {

Expand Down Expand Up @@ -672,10 +678,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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
67 changes: 64 additions & 3 deletions sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -192,15 +195,72 @@ 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))
.withFailMessage("extracted data does not match")
.isEqualTo(plainText);
}

@Test
void testWithAssertionVerificationDisabled() throws Exception {

ListenableFuture<GetAttributeValuesByFqnsResponse> 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 {

Expand Down Expand Up @@ -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))
Expand Down