Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ jobs:
gcloud-auth: ${{ secrets.GCLOUD_AUTH }}
env-file-path: .env.integration
- name: integration test
run: env $(cat .env.integration) ./test-suites/integrationTest.sh
run: env $(cat .env.integration) ./test-suites/allTest.sh
env:
TENANT_ID: INTEGRATION-TEST-GCP
NEW_TENANT_ID: INTEGRATION-TEST-AWS

build_examples:
runs-on: ubuntu-22.04
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.ironcorelabs.tenantsecurity.kms.v1;

import java.util.Map;

import com.google.api.client.util.Key;

/**
* A map from a document ID to a either the wrapped or unwrapped version of a documents keys. Also
* includes a map of failures if any problems occurred when performing the batch wrap operation.
*/
public class BatchDocumentKeys<T> {
public class BatchDocumentKeys<T> extends NullParsingValidator {
@Key
private Map<String, T> keys;

Expand All @@ -22,4 +21,11 @@ public Map<String, T> getKeys() {
public Map<String, ErrorResponse> getFailures() {
return this.failures;
}

@Override
void ensureNoNullsOrThrow() throws IllegalArgumentException {
if (keys == null || failures == null)
throw new IllegalArgumentException(
"Batch response from the Tenant Security Proxy was not valid.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.google.api.client.util.Key;
import com.ironcorelabs.tenantsecurity.kms.v1.exception.TspServiceException;

public final class DeriveKeyResponse {
public final class DeriveKeyResponse extends NullParsingValidator {
@Key
private boolean hasPrimaryConfig;
@Key
Expand Down Expand Up @@ -37,4 +37,10 @@ CompletableFuture<DerivedKey[]> getDerivedKeys(String secretPath, String derivat
}
return CompletableFuture.completedFuture(derivedKeys);
}

@Override
void ensureNoNullsOrThrow() throws IllegalArgumentException {
if (derivedKeys == null)
throw new IllegalArgumentException("TSP failed to derive keys.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public DerivedKey() {}
byte[] getDerivedKeyBytes() {
try {
return Base64.getDecoder().decode(this.derivedKey);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw new IllegalArgumentException(
"Derive keys response from the Tenant Security Proxy was not valid base64.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static CompletableFuture<DeterministicPlaintextField> decryptField(
return key;
}).thenCompose(key -> decryptBytes(parts.getEncryptedBytes(), key.getDerivedKeyBytes())))
.thenApply(decrypted -> new DeterministicPlaintextField(decrypted,
encryptedField.getDerivationPath(), encryptedField.getSecretPath()));
encryptedField.getSecretPath(), encryptedField.getDerivationPath()));
Comment thread
giarc3 marked this conversation as resolved.
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ironcorelabs.tenantsecurity.kms.v1;

abstract class NullParsingValidator {
/**
* Throws an IllegalArgumentException if any of the fields were parsed as null.
*/
abstract void ensureNoNullsOrThrow() throws IllegalArgumentException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@
/**
* An EDEK made by wrapping an existing encrypted document with a tenant's KMS, in Base64 format.
*/
public class RekeyedDocumentKey {
public class RekeyedDocumentKey extends NullParsingValidator {
@Key
private String edek;

public String getEdek() {
return this.edek;
}

@Override
void ensureNoNullsOrThrow() throws IllegalArgumentException {
if (edek == null)
throw new IllegalArgumentException(
"Rekeyed document key response from the Tenant Security Proxy was not valid base64.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public Builder allowInsecureHttp(boolean allow) {
}

/**
* Construct the TenantSecurityClient fron the builder.
* Construct the TenantSecurityClient from the builder.
*
* @return The newly constructed TenantSecurityClient.
* @throws Exception If the tsp url isn't valid or if HTTPS is required and not provided.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,15 @@ enum SecretType {
* Generic method for making a request to the provided URL with the provided post data. Returns an
* instance of the provided generic JSON class or an error message with the provided error.
*/
private <T> CompletableFuture<T> makeRequestAndParseFailure(GenericUrl url,
Map<String, Object> postData, Class<T> jsonType, String errorMessage) {
private <T extends NullParsingValidator> CompletableFuture<T> makeRequestAndParseFailure(
GenericUrl url, Map<String, Object> postData, Class<T> jsonType, String errorMessage) {
return CompletableFuture.supplyAsync(() -> {
try {
HttpResponse resp = this.getApiRequest(postData, url).execute();
if (resp.isSuccessStatusCode()) {
return resp.parseAs(jsonType);
T parsed = resp.parseAs(jsonType);
parsed.ensureNoNullsOrThrow();
return parsed;
}
throw parseFailureFromRequest(resp);
} catch (Exception cause) {
Expand Down Expand Up @@ -261,7 +263,12 @@ CompletableFuture<Void> logSecurityEvent(SecurityEvent event, EventMetadata meta
String error = String.format(
"Unable to make request to Tenant Security Proxy security event endpoint. Endpoint requested: %s",
this.securityEventEndpoint);
return this.makeRequestAndParseFailure(this.securityEventEndpoint, postData, Void.class, error);
return this
.makeRequestAndParseFailure(this.securityEventEndpoint, postData,
VoidSecurityEventResponse.class, error)
// returns a Void
.thenRun(() -> {
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,23 @@
/**
* Represents the JSON response object from the document/unwrap endpoint which includes the dek.
*/
public class UnwrappedDocumentKey {
public class UnwrappedDocumentKey extends NullParsingValidator {
@Key
private String dek;

public byte[] getDekBytes() {
try {
return Base64.getDecoder().decode(this.dek);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw new IllegalArgumentException(
"Unwrap DEK response from the Tenant Security Proxy was not valid base64.");
}
}

@Override
void ensureNoNullsOrThrow() throws IllegalArgumentException {
if (dek == null)
throw new IllegalArgumentException(
"Unwrap DEK response from the Tenant Security Proxy was not valid base64.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ironcorelabs.tenantsecurity.kms.v1;

class VoidSecurityEventResponse extends NullParsingValidator {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should comment this class I think. At first I wasn't sure why we'd want this, so I definitely won't remember in the future.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up going a different way with it. I made an overload of makeRequestAndParseFailure for Void that doesn't do the parsing (and doesn't have the NullParsingValidator requirement). This let me delete the VoidSecurityEventResponse class altogether

@Override
void ensureNoNullsOrThrow() throws IllegalArgumentException {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* A new DEK wrapped by the tenant's KMS and its encrypted form (EDEK), both in Base64 format.
*/
public class WrappedDocumentKey {
public class WrappedDocumentKey extends NullParsingValidator {
@Key
private String dek;

Expand All @@ -17,7 +17,7 @@ public class WrappedDocumentKey {
public byte[] getDekBytes() {
try {
return Base64.getDecoder().decode(this.dek);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
Comment thread
giarc3 marked this conversation as resolved.
Outdated
throw new IllegalArgumentException(
"Wrapped document key response from the Tenant Security Proxy was not valid base64.");
}
Expand All @@ -26,4 +26,11 @@ public byte[] getDekBytes() {
public String getEdek() {
return this.edek;
}

@Override
void ensureNoNullsOrThrow() throws IllegalArgumentException {
if (edek == null || dek == null)
throw new IllegalArgumentException(
"Wrapped document key response from the Tenant Security Proxy was not valid base64.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.ironcorelabs.tenantsecurity.kms.v1;

import java.io.StringReader;
import org.testng.annotations.Test;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.gson.GsonFactory;

@Test(groups = {"unit"})
public class JsonParsingTest {
static JsonObjectParser parser = new JsonObjectParser(new GsonFactory());

@Test(expectedExceptions = IllegalArgumentException.class)
void batchKeysAreEmpty() throws Exception {
String json = "{}";
BatchWrappedDocumentKeys type = parser.<BatchWrappedDocumentKeys>parseAndClose(
new StringReader(json), BatchWrappedDocumentKeys.class);
type.ensureNoNullsOrThrow();
}

@Test(expectedExceptions = IllegalArgumentException.class)
void derivedKeyResponseIsEmpty() throws Exception {
String json = "{}";
DeriveKeyResponse type =
parser.<DeriveKeyResponse>parseAndClose(new StringReader(json), DeriveKeyResponse.class);
type.ensureNoNullsOrThrow();

}

@Test(expectedExceptions = IllegalArgumentException.class)
void unwrappedDocumentKeyErrors() throws Exception {
String json = "{}";
UnwrappedDocumentKey type = parser.<UnwrappedDocumentKey>parseAndClose(new StringReader(json),
UnwrappedDocumentKey.class);
type.ensureNoNullsOrThrow();
}

@Test(expectedExceptions = IllegalArgumentException.class)
void wrappedDocumentKeyErrors() throws Exception {
String json = "{}";
WrappedDocumentKey type =
parser.<WrappedDocumentKey>parseAndClose(new StringReader(json), WrappedDocumentKey.class);
type.ensureNoNullsOrThrow();
}

@Test(expectedExceptions = IllegalArgumentException.class)
void rekeyedDocumentKeyIsNull() throws Exception {
String json = "{}";
RekeyedDocumentKey type =
parser.<RekeyedDocumentKey>parseAndClose(new StringReader(json), RekeyedDocumentKey.class);
type.ensureNoNullsOrThrow();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void batchRoundtrip() throws Exception {

TenantSecurityClient client =
new TenantSecurityClient.Builder(TestSettings.TSP_ADDRESS + TestSettings.TSP_PORT,
this.API_KEY).build();
this.API_KEY).allowInsecureHttp(true).build();

int batchSize = 25;
int batchRepetitions = 50;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ private void assertEqualBytes(byte[] one, byte[] two) throws Exception {
}

private DeterministicPlaintextField getRoundtripDataToEncrypt() {
return new DeterministicPlaintextField("Encrypt these bytes!".getBytes(), "deriv_path",
"secret_path");
return new DeterministicPlaintextField("Encrypt these bytes!".getBytes(), "secret_path",
"deriv_path");
}

private CompletableFuture<DeterministicTenantSecurityClient> createClient() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException;
import com.ironcorelabs.tenantsecurity.logdriver.v1.EventMetadata;
import com.ironcorelabs.tenantsecurity.logdriver.v1.UserEvent;
import com.ironcorelabs.tenantsecurity.utils.CompletableFutures;
import org.testng.annotations.Test;

@Test(groups = {"local-integration"})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import java.util.concurrent.ExecutionException;
import com.ironcorelabs.tenantsecurity.TestUtils;
import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException;
import com.ironcorelabs.tenantsecurity.utils.CompletableFutures;
import org.testng.annotations.Test;

@Test(groups = {"dev-integration"})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import com.ironcorelabs.tenantsecurity.kms.v1.exception.TenantSecurityException;
import org.testng.annotations.Test;
Expand Down Expand Up @@ -50,24 +49,21 @@ public void roundtripTest() throws Exception {
new DocumentMetadata(tenant_id, "integrationTest", "sample", customFields, "customRayID");
Map<String, byte[]> documentMap = getRoundtripDataToEncrypt();

CompletableFuture<PlaintextDocument> roundtrip =
TenantSecurityClient.create(tsp_address + tsp_port, api_key).thenCompose(client -> {
try {
return client.encrypt(documentMap, metadata).thenCompose(encryptedDocument -> {
return client.rekeyEdek(encryptedDocument.getEdek(), metadata, new_tenant_id)
.thenCompose(rekeyedEdek -> {
DocumentMetadata newMetadata = new DocumentMetadata(new_tenant_id,
"integrationTest", "sample", customFields, "customRayID");
EncryptedDocument newDocument =
new EncryptedDocument(encryptedDocument.getEncryptedFields(), rekeyedEdek);
return client.decrypt(newDocument, newMetadata);
});
});
} catch (Exception e) {
throw new CompletionException(e);
}
});
try {
TenantSecurityClient client =
new TenantSecurityClient.Builder(tsp_address + tsp_port, api_key).allowInsecureHttp(true)
.build();
CompletableFuture<PlaintextDocument> roundtrip =
client.encrypt(documentMap, metadata).thenCompose(encryptedDocument -> {
return client.rekeyEdek(encryptedDocument.getEdek(), metadata, new_tenant_id)
.thenCompose(rekeyedEdek -> {
DocumentMetadata newMetadata = new DocumentMetadata(new_tenant_id,
"integrationTest", "sample", customFields, "customRayID");
EncryptedDocument newDocument =
new EncryptedDocument(encryptedDocument.getEncryptedFields(), rekeyedEdek);
return client.decrypt(newDocument, newMetadata);
});
});
Map<String, byte[]> decryptedValuesMap = roundtrip.get().getDecryptedFields();
assertEqualBytes(decryptedValuesMap.get("doc1"), documentMap.get("doc1"));
assertEqualBytes(decryptedValuesMap.get("doc2"), documentMap.get("doc2"));
Expand Down
4 changes: 4 additions & 0 deletions test-suites/allTest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Be sure to set API_KEY, TENANT_ID, and NEW_TENANT_ID env vars
cd "${0%/*/*}" # set the current directory to the one above this script
mvn -Dsuite=test-suites/test-all test
18 changes: 18 additions & 0 deletions test-suites/test-all.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">

<suite name="allTests">
<test name="allTests">
<groups>
<run>
<include name="unit" />
<include name="local-integration" />
<include name="dev-integration" />
<include name="local-batch-integration" />
<include name="local-deterministic" />
</run>
</groups>
<packages>
<package name=".*" />
</packages>
</test>
</suite>
Loading