Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/project/list-of-diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ The PR that reveals the implementation of the `<IncludeInternalObsoleteAttribute
| __`SYSLIB0050`__ | Formatter-based serialization is obsolete and should not be used. |
| __`SYSLIB0051`__ | This API supports obsolete formatter-based serialization. It should not be called or extended by application code. |
| __`SYSLIB0052`__ | This API supports obsolete mechanisms for Regex extensibility. It is not supported. |
| __`SYSLIB0053`__ | AesGcm should indicate the required tag size for encryption and decryption. Use a constructor that accepts the tag size. |

## Analyzer Warnings

Expand Down
3 changes: 3 additions & 0 deletions src/libraries/Common/src/System/Obsoletions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,8 @@ internal static class Obsoletions

internal const string RegexExtensibilityImplMessage = "This API supports obsolete mechanisms for Regex extensibility. It is not supported.";
internal const string RegexExtensibilityDiagId = "SYSLIB0052";

internal const string AesGcmTagConstructorMessage = "AesGcm should indicate the required tag size for encryption and decryption. Use a constructor that accepts the tag size.";
internal const string AesGcmTagConstructorDiagId = "SYSLIB0053";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,16 @@ public override void GenerateKey() { }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
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() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@
<data name="Cryptography_HashNotYetFinalized" xml:space="preserve">
<value>Hash must be finalized before the hash value is retrieved.</value>
</data>
<data name="Cryptography_IncorrectTagLength" xml:space="preserve">
<value>The size of the specified tag does not match the expected size of {0}.</value>
</data>
<data name="Cryptography_InvalidBlockSize" xml:space="preserve">
<value>Specified block size is not valid for this algorithm.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.Versioning;
using Internal.Cryptography;

Expand All @@ -14,6 +15,7 @@ public sealed partial class AesGcm : IDisposable
private const int NonceSize = 12;
public static KeySizes NonceByteSizes { get; } = new KeySizes(NonceSize, NonceSize, 1);

[Obsolete(Obsoletions.AesGcmTagConstructorMessage, DiagnosticId = Obsoletions.AesGcmTagConstructorDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public AesGcm(ReadOnlySpan<byte> key)
{
ThrowIfNotSupported();
Expand All @@ -22,16 +24,82 @@ public AesGcm(ReadOnlySpan<byte> key)
ImportKey(key);
}

[Obsolete(Obsoletions.AesGcmTagConstructorMessage, DiagnosticId = Obsoletions.AesGcmTagConstructorDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public AesGcm(byte[] key)
: this(new ReadOnlySpan<byte>(key ?? throw new ArgumentNullException(nameof(key))))
{
ThrowIfNotSupported();
}

ArgumentNullException.ThrowIfNull(key);
/// <summary>
Comment thread
vcsjones marked this conversation as resolved.
/// Initializes a new instance of the <see cref="AesGcm" /> class with a provided key and required tag size.
/// </summary>
/// <param name="key">The secret key to use for this instance.</param>
/// <param name="tagSizeInBytes">The size of the tag, in bytes, that encryption and decryption must use.</param>
/// <exception cref="CryptographicException">
/// The <paramref name="key" /> parameter length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
/// </exception>
/// <exception cref="ArgumentException">
/// The <paramref name="tagSizeInBytes" /> parameter is an unsupported tag size indicated by
/// <see cref="TagByteSizes" />.
/// </exception>
/// <exception cref="PlatformNotSupportedException">
/// The current platform does not support AES-GCM.
/// </exception>
/// <remarks>
/// The <paramref name="tagSizeInBytes" /> parameter is used to indicate that the tag parameter in <c>Encrypt</c>
Comment thread
vcsjones marked this conversation as resolved.
/// or <c>Decrypt</c> must be exactly this size. Indicating the required tag size prevents issues where callers
/// of <c>Decrypt</c> may supply a tag as input and that input is truncated to an unexpected size.
/// </remarks>
public AesGcm(ReadOnlySpan<byte> key, int tagSizeInBytes)
{
ThrowIfNotSupported();

AesAEAD.CheckKeySize(key.Length);

if (!tagSizeInBytes.IsLegalSize(TagByteSizes))
{
throw new ArgumentException(SR.Cryptography_InvalidTagLength, nameof(tagSizeInBytes));
}

TagSizeInBytes = tagSizeInBytes;
ImportKey(key);
}

/// <summary>
/// Initializes a new instance of the <see cref="AesGcm" /> class with a provided key and required tag size.
/// </summary>
/// <param name="key">The secret key to use for this instance.</param>
/// <param name="tagSizeInBytes">The size of the tag, in bytes, that encryption and decryption must use.</param>
/// <exception cref="ArgumentNullException">The <paramref name="key" /> parameter is null.</exception>
/// <exception cref="CryptographicException">
/// The <paramref name="key" /> parameter length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
/// </exception>
/// <exception cref="ArgumentException">
/// The <paramref name="tagSizeInBytes" /> parameter is an unsupported tag size indicated by
/// <see cref="TagByteSizes" />.
/// </exception>
/// <exception cref="PlatformNotSupportedException">
/// The current platform does not support AES-GCM.
/// </exception>
/// <remarks>
/// The <paramref name="tagSizeInBytes" /> parameter is used to indicate that the tag parameter in <c>Encrypt</c>
/// or <c>Decrypt</c> must be exactly this size. Indicating the required tag size prevents issues where callers
/// of <c>Decrypt</c> may supply a tag as input and that input is truncated to an unexpected size.
/// </remarks>
public AesGcm(byte[] key, int tagSizeInBytes)
: this(new ReadOnlySpan<byte>(key ?? throw new ArgumentNullException(nameof(key))), tagSizeInBytes)
{
}

/// <summary>
/// Gets the size of the tag, in bytes.
/// </summary>
/// <value>
/// The size of the tag that must be used for encryption or decryption, or <see langword="null" /> if the
/// tag size is unspecified.
/// </value>
public int? TagSizeInBytes { get; }

public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[]? associatedData = null)
{
ArgumentNullException.ThrowIfNull(nonce);
Expand Down Expand Up @@ -74,7 +142,7 @@ public void Decrypt(
DecryptCore(nonce, ciphertext, tag, plaintext, associatedData);
}

private static void CheckParameters(
private void CheckParameters(
ReadOnlySpan<byte> plaintext,
ReadOnlySpan<byte> ciphertext,
ReadOnlySpan<byte> nonce,
Expand All @@ -86,8 +154,20 @@ private static void CheckParameters(
if (!nonce.Length.IsLegalSize(NonceByteSizes))
throw new ArgumentException(SR.Cryptography_InvalidNonceLength, nameof(nonce));

if (!tag.Length.IsLegalSize(TagByteSizes))
if (TagSizeInBytes is int tagSizeInBytes)
{
// constructor promise
Debug.Assert(tagSizeInBytes.IsLegalSize(TagByteSizes));

if (tag.Length != tagSizeInBytes)
{
throw new ArgumentException(SR.Format(SR.Cryptography_IncorrectTagLength, tagSizeInBytes), nameof(tag));
}
}
else if (!tag.Length.IsLegalSize(TagByteSizes))
{
throw new ArgumentException(SR.Cryptography_InvalidTagLength, nameof(tag));
}
}

private static void ThrowIfNotSupported()
Expand Down
Loading