Skip to content

[API Proposal]: Provide an implementation AesGcm in Microsoft.Bcl.Cryptography #89718

@vcsjones

Description

@vcsjones

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.

  1. When the package is build for .NET (Core), it will type-forward in to the shared runtime implementation.
  2. When the package is build for .NET Framework, it will carry the required API surface and p/invokes similar to SP800108HmacCounterKdf
  3. 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.

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-System.Securityin-prThere is an active PR which will close this issue when it is merged

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions