Skip to content

Conversation

@coltfred
Copy link
Member

This is a strawman implementation of the idea of caching a DEK for reuse for a short period of time.

TODO:

  • Add the encryptor counterpart.
  • Decide if we should zero the DEK the caller sends in to the decryptor to ensure proper disposal
  • Add reporting of DEK usage on close

Comment on lines +190 to +200
private CompletableFuture<PlaintextDocument> decryptFields(Map<String, byte[]> document,
String documentEdek) {
// Check closed/expired state again before starting decryption
if (closed.get()) {
return CompletableFuture.failedFuture(new TscException(
TenantSecurityErrorCodes.DOCUMENT_DECRYPT_FAILED, "CachedKeyDecryptor has been closed"));
}
if (isExpired()) {
return CompletableFuture.failedFuture(new TscException(
TenantSecurityErrorCodes.DOCUMENT_DECRYPT_FAILED, "CachedKeyDecryptor has expired"));
}
Copy link
Member

Choose a reason for hiding this comment

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

Re-checking these feels really unnecessary to me. If you're following the calls from decrypt, there's not really any code between the check and the re-check, right? Or we could remove the checks from decrypt and only have them here

Comment on lines +202 to +215
// Parallel decrypt each field
Map<String, CompletableFuture<byte[]>> decryptOps = document.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
entry -> CompletableFuture.supplyAsync(
() -> CryptoUtils.decryptDocument(entry.getValue(), dek).join(),
encryptionExecutor)));

// Join all futures and build result
return CompletableFutures.tryCatchNonFatal(() -> {
Map<String, byte[]> decryptedBytes = decryptOps.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().join()));
return new PlaintextDocument(decryptedBytes, documentEdek);
});
}
Copy link
Member

Choose a reason for hiding this comment

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

It's a bummer that this has to just be duplicated code

Comment on lines +165 to +180
if (closed.get()) {
return CompletableFuture.failedFuture(new TscException(
TenantSecurityErrorCodes.DOCUMENT_DECRYPT_FAILED, "CachedKeyDecryptor has been closed"));
}
if (isExpired()) {
return CompletableFuture.failedFuture(new TscException(
TenantSecurityErrorCodes.DOCUMENT_DECRYPT_FAILED, "CachedKeyDecryptor has expired"));
}

// Validate EDEK matches
if (!this.edek.equals(edek)) {
return CompletableFuture
.failedFuture(new TscException(TenantSecurityErrorCodes.DOCUMENT_DECRYPT_FAILED,
"Provided EDEK does not match the cached EDEK. "
+ "This decryptor can only decrypt documents with matching EDEKs."));
}
Copy link
Member

Choose a reason for hiding this comment

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

These checks should probably move to a function

* This is the recommended pattern for using cached decryptors with CompletableFuture composition:
*
* <pre>
* client.withCachedDecryptor(edek, metadata, decryptor -&gt;
Copy link
Member

Choose a reason for hiding this comment

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

why is the > escaped here and below?

Copy link
Member Author

Choose a reason for hiding this comment

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

When you put a > inside a javadoc comment you have use the html escape for it. We've been really bad at putting examples on our other functions so you haven't seen it much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants