Skip to content
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

Fix input wiping on decryption failure #337

Closed
wants to merge 1 commit into from

Conversation

SalusaSecondus
Copy link
Contributor

PR #298 appears to have introduced a bug wherein when there is decryption failure can cause the input buffer to be wiped. Specifically, this happens for AES-GCM in-place decryption. (Ideally, the output buffer would remain unchanged in all failure cases, but that change is more understandable.)

This change:

  • Returns to using a staging buffer to hold the unauthenticated plaintext
  • Uses the pre-existing input buffer to avoid memory allocation. (This wasn’t possible prior to PR # 298 because the input buffer wasn’t always used.)
  • Copies the plaintext over to the output buffer only upon successful decryption
  • Introduces a unit tests to avoid regression

So, this new change does incur a new memory copy relative to the current situation (but the same as before PR # 298) but does not incur any new memory allocation (so we keep that win from # 298).

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@SalusaSecondus SalusaSecondus requested a review from a team as a code owner September 27, 2023 22:01
@SalusaSecondus
Copy link
Contributor Author

Closing and re-opening to poke the checks.

@geedo0
Copy link
Contributor

geedo0 commented Sep 29, 2023

Nice catch! I don't mind the additional I/O here. I do worry about the precedent set by taking this stance of considering what happens to the output buffer (and what impact that has if it also happens to be the input) on failure cases. This can get into really murky territory. The OpenSSL docs state that the contents of the output are "corrupt" and don't consider any of these factors. That flexibility is pretty useful in these circumstances.

Copy link
Contributor

@geedo0 geedo0 left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -738,12 +738,9 @@ private int engineDecryptFinal(
}
}
// Decryption completed successfully.
// Copy from working buffer into actual output
System.arraycopy(workingInputArray, workingInputOffset, output, outputOffset, outLen);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can avoid this copy in case the output buffer doesn't overlap input. Still in the case of failure the output buffer would potentially be modified, but it's output buffer after all.

@@ -697,8 +697,8 @@ private int engineDecryptFinal(
workingInputArray,
workingInputOffset,
workingInputLength,
output,
outputOffset,
workingInputArray, // Output
Copy link
Contributor

Choose a reason for hiding this comment

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

In cases where the output buffer is distinct from the input, like here the copying back can be avoided by directly using the output buffer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am willing to change this, however:

  1. The current design never leaks unauthenticated plaintext outside the ACCP boundary.
  2. The proposed change is more complicated and we're already seeing bugs creep in which suggest that we're nearing maximum complexity to safely work here.

So, if left to me, I would leave this as written, but I am willing to change it. (Or, to read it another way. I have one approval and one request for changes. Mind reaching consensus?)

Copy link
Contributor

Choose a reason for hiding this comment

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

We spoke offline and I'm more hesitant now. My interpretation of the contract is that if the caller gives us an output buffer and a length we have no obligation to preserve the contents of that buffer even if the tag check fails. If we can preserve it for free, awesome, but if it adds O(n) memory I/O to all AES-GCM operations I'm less inclined. Even if we add the risky logic for overlap detection, what we'll find is that the perfect overlap case is the common case and it won't buy us much.

The remaining point of clarity for us is to look at how other libraries handle this case to determine if this is considered a bug as that's still a bit subjective to me. AFAICT, OpenSSL/AWS-LC will scribble over the output buffer and simply return an error code if the tag fails. I'd be hesitant to add new semantics at the ACCP layer which don't line up with the native library.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My concern is us overwriting the input buffer (which happens to be the same).

I have personally seen decryption logic which attempts repeated in-place decryption using multiple keys. Unfortunately, I no longer have access to said code. However, wiping the in-place buffer on failure would absolutely break that code.

More interesting than how OpenSSL/AWS-LC behave is how other JCA providers behave as that's what ACCP is replacing.

@geedo0
Copy link
Contributor

geedo0 commented Feb 13, 2024

Alright, after many months, we discussed this as a team and decided to close this PR. Absent any specification that mandates a certain behavior we decided to keep the implementation choice which improves performance. This change in behavior has been deployed for several months and based on the evidence so far we are willing to tolerate the amount of breakage this may invite.

@geedo0 geedo0 closed this Feb 13, 2024
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