-
Notifications
You must be signed in to change notification settings - Fork 4.6k
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
[API Proposal]: Authenticated ciphers should distigush between mismatched tag and failure #67082
Comments
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones Issue DetailsBackground and motivationWhen decrypting authenticated data, API Proposalnamespace System.Cryptography
{
public partial class AesGcm
{
// existing: public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[] associatedData = null);
// existing: public void Decrypt(ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> tag, Span<byte> plaintext, ReadOnlySpan<byte> associatedData = default);
// Returns false only when tag is mismatched.
public bool TryDecrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[] associatedData = null);
public bool TryDecrypt(ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> tag, Span<byte> plaintext, ReadOnlySpan<byte> associatedData = default);
}
public partial class ChaCha20Poly1305
{
// existing: public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[]? associatedData = null);
// existing: public void Decrypt(ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> tag, Span<byte> plaintext, ReadOnlySpan<byte> associatedData = default);
// Returns false only when tag is mismatched.
public bool TryDecrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[]? associatedData = null);
public bool TryDecrypt(ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> tag, Span<byte> plaintext, ReadOnlySpan<byte> associatedData = default);
}
} API Usage// User has provided password, and PBKDF was used to derive key.
using (var aesGcm = new AesGcm(key))
{
try
{
if (!aesGcm.TryDecrypt(iv, ciphertext, tag, plaintext))
{
// Notify user that password was incorrect or data was corrupt.
// Prompt user for password again in case user typo'd password.
}
}
catch (CryptographicException)
{
// Something went wrong. Notify user that decryption will not be possible.
}
} Alternative DesignsNo response RisksNo response
|
Hello @carlreinke, the .NET team has decided not to give access to the decrypted plaintext in case of an authentication tag mismatch because processing this plaintext leads to security vulnerabilities and defeats the reason to use authenticated encryption in the first place. Apart from incorrect buffer sizes which are a user code bug that must be fixed, the only other reason the decryption operation would otherwise fail is if the underlying platform does not support this algorithm and you can check it by each class' Since the API you are proposing is very unlikely to be accepted, I am going to close this issue. Let me know if you have any further questions. |
The 99.9999% case is that tag verification failed (whether that be from tampered data or a mismatched ciphertext+tag+key). The 0.0001% case is that something weird went on during the decryption operation, such as the underlying library doing It's also not something we can necessarily guarantee that we can even detect a difference from across all of our underlying providers forever. (Apparently I didn't hit comment 2 hours ago) |
The proposed API does not assume or imply that decrypted plaintext would be made available in case of a tag mismatch. I would expect that the decrypted plaintext would be zeroed before returning false, just as it is zeroed before throwing today.
Okay, but it is still very much a distinct failure mode from a tag mismatch.
Is this the bar for new .NET APIs? I'm not sure how you can implement any new feature if that's the case. If there's a planned-to-be-supported platform that does not provide a means of implementing the proposed API, I would understand. |
@carlreinke to help me better understand:
Can you give a few examples here? How is distinguishing a bad authentication tag useful to you, as opposed to "any failure" ( My 2 cents: rather than introduce new APIs that indicate if the tag mismatched, instead I would change the existing APIs to throw an Proposalified: namespace System.Security.Cryptography {
public sealed class AuthenticationTagMismatchException : CryptographicException {
public AuthenticationTagMismatchException() { }
public AuthenticationTagMismatchException(string? message) : base(message) { }
public AuthenticationTagMismatchException(string? message, System.Exception? innerException) { }
}
} |
The only use case I have is basically the one described in the issue description.
I only went with a try-pattern in my proposal because in my use case the key is derived from user input, and "bad user input is not exceptional, it happens all the time." I don't feel particularly strongly about that for this API though. |
That particular concern is most apparent in cryptography, since (as a design goal / security principle) we use platform-provided cryptographic libraries rather than writing our own. I was most concerned about Android (a current platform) and our eventual move to Apple's CryptoKit library. I sensed a pretty high Spock-eyebrow through @vcsjones' message to me when suggested that we have the data available to us. |
I prefer the distinct exception type over the new method, for two reasons:
As for the perf impact of the exception (the generalized Try vs the Span-specific one), I don't think it really matters here. If the main source of failure is "the user typed some bad input" that means we're in an interactive session and the user probably won't notice the timing difference between Try->false and try->catch. There's not a big-data / fast-path / loop concern (at least, not one big enough to offset the above concerns (for me)). |
I updated the proposal to be what @vcsjones proposed. |
Just to enumerate all of the platforms:
|
We should consider unsealing the exception. It's not all that common for exception types to be sealed outside of a very small population which represent something very special to the runtime. |
Looks good as proposed namespace System.Security.Cryptography
{
// Note for discussion: Should this be sealed or unsealed?
public sealed class AuthenticationTagMismatchException : CryptographicException
{
public AuthenticationTagMismatchException();
public AuthenticationTagMismatchException(string? message);
public AuthenticationTagMismatchException(string? message, Exception? innerException);
}
} |
@bartonjs i have a branch for this, so removed [help wanted]. |
Background and motivation
When decrypting authenticated data,
AesCcm
,AesGcm
, andChaCha20Poly1305
throwCryptographicException
in the case when the tag does not match (indicating that either the key is wrong or the data is inauthentic) and also in the case when "the decryption operation otherwise failed". A program may want to take a different action in these two scenarios but currently cannot.API Proposal
API Usage
Alternative Designs
No response
Risks
No response
The text was updated successfully, but these errors were encountered: