-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Background and motivation
The AesGcm class was introduced in to .NET in .NET Core 3.1. AES-GCM is an AEAD that is used in many protocols and specifications. Its broad use in in those specs makes it desirable or mandatory to support them.
For .NET Framework, there is no AES-GCM implementation that is readily available. Developers that want AES-GCM either have to p/invoke to Windows or find some other alternative implementation.
This is a proposal to add AesGcm to Microsoft.Bcl.Cryptography.
- When the package is build for .NET (Core), it will type-forward in to the shared runtime implementation.
- When the package is build for .NET Framework, it will carry the required API surface and p/invokes similar to
SP800108HmacCounterKdf - When the package is build for .NET Standard, it will behave similar to .NET Framework, however will perform a runtime check that the platform is Windows.
This means that the package will only provide an implementation on Windows. This is largely okay - developers on non-Windows platforms already have this in .NET itself and will benefit from the type forwards.
API Proposal
To provide the best experience, I think the ref needs to be split between .NET, .NET Framework, and .NET Standard.
This is because of the constructors that were replaced in .NET 8. For .NET Framework and .NET Standard, it would be ideal that we provide them with the same improved constructors since the new constructors are meant to mitigate misuse.
.NET Framework and .NET Standard
namespace System.Security.Cryptography;
public sealed partial class AesGcm : System.IDisposable
{
// NOT included because they are obsolete
// public AesGcm(byte[] key) { }
// public AesGcm(System.ReadOnlySpan<byte> key) { }
public AesGcm(byte[] key, int tagSizeInBytes) { }
public AesGcm(System.ReadOnlySpan<byte> key, int tagSizeInBytes) { }
public static bool IsSupported { get { throw null; } }
public static System.Security.Cryptography.KeySizes NonceByteSizes { get { throw null; } }
public static System.Security.Cryptography.KeySizes TagByteSizes { get { throw null; } }
public int? TagSizeInBytes { get { throw null; } }
public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[]? associatedData = null) { }
public void Decrypt(System.ReadOnlySpan<byte> nonce, System.ReadOnlySpan<byte> ciphertext, System.ReadOnlySpan<byte> tag, System.Span<byte> plaintext, System.ReadOnlySpan<byte> associatedData = default(System.ReadOnlySpan<byte>)) { }
public void Dispose() { }
public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[]? associatedData = null) { }
public void Encrypt(System.ReadOnlySpan<byte> nonce, System.ReadOnlySpan<byte> plaintext, System.Span<byte> ciphertext, System.Span<byte> tag, System.ReadOnlySpan<byte> associatedData = default(System.ReadOnlySpan<byte>)) { }
}
public sealed partial class AuthenticationTagMismatchException : CryptographicException
{
public AuthenticationTagMismatchException();
public AuthenticationTagMismatchException(string message);
public AuthenticationTagMismatchException(string message, System.Exception inner);
}.NET 8+ type forward
namespace System.Security.Cryptography;
// Matches ref for .NET 8.
public sealed partial class AesGcm : System.IDisposable
{
[System.ObsoleteAttribute("AesGcm should indicate the required tag size for encryption and decryption. Use a constructor that accepts the tag size.", DiagnosticId="SYSLIB0053", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
public AesGcm(byte[] key) { }
public AesGcm(byte[] key, int tagSizeInBytes) { }
[System.ObsoleteAttribute("AesGcm should indicate the required tag size for encryption and decryption. Use a constructor that accepts the tag size.", DiagnosticId="SYSLIB0053", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
public AesGcm(System.ReadOnlySpan<byte> key) { }
public AesGcm(System.ReadOnlySpan<byte> key, int tagSizeInBytes) { }
public static bool IsSupported { get { throw null; } }
public static System.Security.Cryptography.KeySizes NonceByteSizes { get { throw null; } }
public static System.Security.Cryptography.KeySizes TagByteSizes { get { throw null; } }
public int? TagSizeInBytes { get { throw null; } }
public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[]? associatedData = null) { }
public void Decrypt(System.ReadOnlySpan<byte> nonce, System.ReadOnlySpan<byte> ciphertext, System.ReadOnlySpan<byte> tag, System.Span<byte> plaintext, System.ReadOnlySpan<byte> associatedData = default(System.ReadOnlySpan<byte>)) { }
public void Dispose() { }
public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[]? associatedData = null) { }
public void Encrypt(System.ReadOnlySpan<byte> nonce, System.ReadOnlySpan<byte> plaintext, System.Span<byte> ciphertext, System.Span<byte> tag, System.ReadOnlySpan<byte> associatedData = default(System.ReadOnlySpan<byte>)) { }
}
public sealed partial class AuthenticationTagMismatchException : CryptographicException
{
public AuthenticationTagMismatchException();
public AuthenticationTagMismatchException(string message);
public AuthenticationTagMismatchException(string message, System.Exception inner);
}API Usage
Same as .NET.
Alternative Designs
This proposal leaves out AesCcm. This omission is purely due to no known demand for AES-CCM in downlevel platforms, whereas AES-GCM has demonstrated considerable demand. We can include AesCcm for completeness if desired.
Risks
This carries the similar risks of providing any API as a package. It carries maintenance burden.