From 70ae3f797c7ec1219636e1ad6795b403feb24841 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Sat, 13 May 2023 13:22:54 -0400 Subject: [PATCH 1/2] Implement API to require AES GCM tag sizes. --- .../ref/System.Security.Cryptography.cs | 3 + .../src/Resources/Strings.resx | 3 + .../System/Security/Cryptography/AesGcm.cs | 86 ++++++++- .../tests/AesGcmTests.cs | 180 ++++++++++++------ 4 files changed, 206 insertions(+), 66 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs index 5ca913712c4f85..9e4bcccc37fd4d 100644 --- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs +++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs @@ -124,10 +124,13 @@ public override void GenerateKey() { } public sealed partial class AesGcm : System.IDisposable { public AesGcm(byte[] key) { } + public AesGcm(byte[] key, int tagSizeInBytes) { } public AesGcm(System.ReadOnlySpan key) { } + public AesGcm(System.ReadOnlySpan 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 nonce, System.ReadOnlySpan ciphertext, System.ReadOnlySpan tag, System.Span plaintext, System.ReadOnlySpan associatedData = default(System.ReadOnlySpan)) { } public void Dispose() { } diff --git a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx index 0172bc6e6f4ea3..1fd0873fbfc4c1 100644 --- a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx @@ -348,6 +348,9 @@ Hash must be finalized before the hash value is retrieved. + + The size of the specified tag does not match the expected size of {0}. + Specified block size is not valid for this algorithm. diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs index 104d280452482c..a209d42cadd3de 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs @@ -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; @@ -23,15 +24,80 @@ public AesGcm(ReadOnlySpan key) } public AesGcm(byte[] key) + : this(new ReadOnlySpan(key ?? throw new ArgumentNullException(nameof(key)))) { - ThrowIfNotSupported(); + } - ArgumentNullException.ThrowIfNull(key); + /// + /// Initializes a new instance of the class with a provided key and required tag size. + /// + /// The secret key to use for this instance. + /// The size of the tag, in bytes, that encryption and decryption must use. + /// + /// The parameter length is other than 16, 24, or 32 bytes (128, 192, or 256 bits). + /// + /// + /// The parameter is an unsupported tag size indicated by + /// . + /// + /// + /// The current platform does not support AES-GCM. + /// + /// + /// The parameter is used to indicate that the tag parameter in Encrypt + /// or Decrypt must be exactly this size. Indicating the required tag size prevents issues where callers + /// of Decrypt may supply a tag as input and that input is truncated to an unexpected size. + /// + public AesGcm(ReadOnlySpan key, int tagSizeInBytes) + { + ThrowIfNotSupported(); AesAEAD.CheckKeySize(key.Length); + + if (!tagSizeInBytes.IsLegalSize(TagByteSizes)) + { + throw new ArgumentException(SR.Cryptography_InvalidTagLength, nameof(tagSizeInBytes)); + } + + TagSizeInBytes = tagSizeInBytes; ImportKey(key); } + /// + /// Initializes a new instance of the class with a provided key and required tag size. + /// + /// The secret key to use for this instance. + /// The size of the tag, in bytes, that encryption and decryption must use. + /// The parameter is null. + /// + /// The parameter length is other than 16, 24, or 32 bytes (128, 192, or 256 bits). + /// + /// + /// The parameter is an unsupported tag size indicated by + /// . + /// + /// + /// The current platform does not support AES-GCM. + /// + /// + /// The parameter is used to indicate that the tag parameter in Encrypt + /// or Decrypt must be exactly this size. Indicating the required tag size prevents issues where callers + /// of Decrypt may supply a tag as input and that input is truncated to an unexpected size. + /// + public AesGcm(byte[] key, int tagSizeInBytes) + : this(new ReadOnlySpan(key ?? throw new ArgumentNullException(nameof(key))), tagSizeInBytes) + { + } + + /// + /// Gets the size of the tag, in bytes. + /// + /// + /// The size of the tag that must be used for encryption or decryption, or if the + /// tag size is unspecified. + /// + public int? TagSizeInBytes { get; } + public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[]? associatedData = null) { ArgumentNullException.ThrowIfNull(nonce); @@ -74,7 +140,7 @@ public void Decrypt( DecryptCore(nonce, ciphertext, tag, plaintext, associatedData); } - private static void CheckParameters( + private void CheckParameters( ReadOnlySpan plaintext, ReadOnlySpan ciphertext, ReadOnlySpan nonce, @@ -86,8 +152,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() diff --git a/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs b/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs index 741b28020281f9..b417136c0053ab 100644 --- a/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs @@ -68,7 +68,7 @@ public static void InvalidNonceSize(int nonceSize) RandomNumberGenerator.Fill(key); RandomNumberGenerator.Fill(nonce); - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, AesGcm.TagByteSizes.MinSize)) { Assert.Throws("nonce", () => aesGcm.Encrypt(nonce, plaintext, ciphertext, tag)); } @@ -88,7 +88,7 @@ public static void ValidNonceSize(int nonceSize) RandomNumberGenerator.Fill(key); RandomNumberGenerator.Fill(nonce); - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, AesGcm.TagByteSizes.MinSize)) { aesGcm.Encrypt(nonce, plaintext, ciphertext, tag); @@ -101,7 +101,7 @@ public static void ValidNonceSize(int nonceSize) [Theory] [MemberData(nameof(GetInvalidTagSizes))] [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - public static void InvalidTagSize(int tagSize) + public static void InvalidTagSizeForUnspecifiedRequiredTag(int tagSize) { int dataLength = 30; byte[] plaintext = Enumerable.Range(1, dataLength).Select((x) => (byte)x).ToArray(); @@ -115,9 +115,20 @@ public static void InvalidTagSize(int tagSize) using (var aesGcm = new AesGcm(key)) { Assert.Throws("tag", () => aesGcm.Encrypt(nonce, plaintext, ciphertext, tag)); + Assert.Throws("tag", () => aesGcm.Decrypt(nonce, ciphertext, tag, plaintext)); } } + [Theory] + [MemberData(nameof(GetInvalidTagSizes))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + public static void InvalidTagSizeForRequiredTag(int tagSize) + { + byte[] key = new byte[32]; + Assert.Throws("tagSizeInBytes", () => new AesGcm(key, tagSize)); + Assert.Throws("tagSizeInBytes", () => new AesGcm(key.AsSpan(), tagSize)); + } + [Theory] [MemberData(nameof(GetValidTagSizes))] [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] @@ -132,7 +143,7 @@ public static void ValidTagSize(int tagSize) RandomNumberGenerator.Fill(key); RandomNumberGenerator.Fill(nonce); - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tagSize)) { aesGcm.Encrypt(nonce, plaintext, ciphertext, tag); @@ -142,6 +153,37 @@ public static void ValidTagSize(int tagSize) } } + [Theory] + [InlineData(12)] + [InlineData(13)] + [InlineData(14)] + [InlineData(15)] + public static void TagSizeDoesNotMatchConstructorRequirement(int wrongTagSize) + { + byte[] key = new byte[16]; + byte[] nonce = new byte[12]; + byte[] plaintext = new byte[1]; + byte[] ciphertext = new byte[1]; + byte[] tag = new byte[wrongTagSize]; + int tagSize = 16; + + using (var aesGcm = new AesGcm(key, tagSize)) + { + ArgumentException ex; + ex = Assert.Throws("tag", () => aesGcm.Encrypt(nonce, plaintext, ciphertext, tag)); + Assert.Contains(tagSize.ToString(), ex.Message); + + ex = Assert.Throws("tag", () => aesGcm.Encrypt(nonce.AsSpan(), plaintext, ciphertext, tag)); + Assert.Contains(tagSize.ToString(), ex.Message); + + ex = Assert.Throws("tag", () => aesGcm.Decrypt(nonce, ciphertext, tag, plaintext)); + Assert.Contains(tagSize.ToString(), ex.Message); + + ex = Assert.Throws("tag", () => aesGcm.Decrypt(nonce.AsSpan(), ciphertext, tag, plaintext)); + Assert.Contains(tagSize.ToString(), ex.Message); + } + } + [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void TwoEncryptionsAndDecryptionsUsingOneInstance() @@ -165,7 +207,7 @@ public static void TwoEncryptionsAndDecryptionsUsingOneInstance() "da").HexToByteArray(); byte[] expectedTag2 = "9c75d006640ff4fb68c60c9548a45cf8".HexToByteArray(); - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, expectedTag1.Length)) { byte[] ciphertext1 = new byte[originalData1.Length]; byte[] tag1 = new byte[expectedTag1.Length]; @@ -173,16 +215,19 @@ public static void TwoEncryptionsAndDecryptionsUsingOneInstance() Assert.Equal(expectedCiphertext1, ciphertext1); Assert.Equal(expectedTag1, tag1); + byte[] plaintext1 = new byte[originalData1.Length]; + aesGcm.Decrypt(nonce1, ciphertext1, tag1, plaintext1); + Assert.Equal(originalData1, plaintext1); + } + + using (var aesGcm = new AesGcm(key, expectedTag2.Length)) + { byte[] ciphertext2 = new byte[originalData2.Length]; byte[] tag2 = new byte[expectedTag2.Length]; aesGcm.Encrypt(nonce2, originalData2, ciphertext2, tag2, associatedData2); Assert.Equal(expectedCiphertext2, ciphertext2); Assert.Equal(expectedTag2, tag2); - byte[] plaintext1 = new byte[originalData1.Length]; - aesGcm.Decrypt(nonce1, ciphertext1, tag1, plaintext1); - Assert.Equal(originalData1, plaintext1); - byte[] plaintext2 = new byte[originalData2.Length]; aesGcm.Decrypt(nonce2, ciphertext2, tag2, plaintext2, associatedData2); Assert.Equal(originalData2, plaintext2); @@ -200,7 +245,7 @@ public static void PlaintextAndCiphertextSizeDiffer(int ptLen, int ctLen) byte[] ciphertext = new byte[ctLen]; byte[] tag = new byte[16]; - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tag.Length)) { Assert.Throws(() => aesGcm.Encrypt(nonce, plaintext, ciphertext, tag)); Assert.Throws(() => aesGcm.Decrypt(nonce, ciphertext, tag, plaintext)); @@ -222,7 +267,7 @@ public static void EncryptDecryptNullNonce() byte[] ciphertext = new byte[0]; byte[] tag = new byte[16]; - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tag.Length)) { Assert.Throws(() => aesGcm.Encrypt((byte[])null, plaintext, ciphertext, tag)); Assert.Throws(() => aesGcm.Decrypt((byte[])null, ciphertext, tag, plaintext)); @@ -238,7 +283,7 @@ public static void EncryptDecryptNullPlaintext() byte[] ciphertext = new byte[0]; byte[] tag = new byte[16]; - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tag.Length)) { Assert.Throws(() => aesGcm.Encrypt(nonce, (byte[])null, ciphertext, tag)); Assert.Throws(() => aesGcm.Decrypt(nonce, ciphertext, tag, (byte[])null)); @@ -254,7 +299,7 @@ public static void EncryptDecryptNullCiphertext() byte[] plaintext = new byte[0]; byte[] tag = new byte[16]; - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tag.Length)) { Assert.Throws(() => aesGcm.Encrypt(nonce, plaintext, (byte[])null, tag)); Assert.Throws(() => aesGcm.Decrypt(nonce, (byte[])null, tag, plaintext)); @@ -270,7 +315,7 @@ public static void EncryptDecryptNullTag() byte[] plaintext = new byte[0]; byte[] ciphertext = new byte[0]; - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tagSizeInBytes: 16)) { Assert.Throws(() => aesGcm.Encrypt(nonce, plaintext, ciphertext, (byte[])null)); Assert.Throws(() => aesGcm.Decrypt(nonce, ciphertext, (byte[])null, plaintext)); @@ -288,7 +333,7 @@ public static void InplaceEncryptDecrypt() byte[] tag = new byte[16]; RandomNumberGenerator.Fill(nonce); - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tag.Length)) { aesGcm.Encrypt(nonce, data, data, tag); Assert.NotEqual(originalPlaintext, data); @@ -309,7 +354,7 @@ public static void InplaceEncryptTamperTagDecrypt() byte[] tag = new byte[16]; RandomNumberGenerator.Fill(nonce); - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tag.Length)) { aesGcm.Encrypt(nonce, data, data, tag); Assert.NotEqual(originalPlaintext, data); @@ -325,7 +370,7 @@ public static void InplaceEncryptTamperTagDecrypt() [Theory] [MemberData(nameof(GetNistGcmTestCases))] [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - public static void AesGcmNistTests(AEADTest testCase) + public static void AesGcmNistTestsUnspecifiedTagSize(AEADTest testCase) { using (var aesGcm = new AesGcm(testCase.Key)) { @@ -360,71 +405,82 @@ public static void AesGcmNistTests(AEADTest testCase) [Theory] [MemberData(nameof(GetNistGcmTestCases))] [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] - public static void AesGcmNistTestsTamperTag(AEADTest testCase) + public static void AesGcmNistTestsSpecifiedTagSize(AEADTest testCase) { - using (var aesGcm = new AesGcm(testCase.Key)) + if (PlatformDetection.IsOSX && testCase.Tag.Length != CryptoKitSupportedTagSizeInBytes) { - if (PlatformDetection.IsOSX && testCase.Tag.Length != CryptoKitSupportedTagSizeInBytes) - { - byte[] plaintext = new byte[testCase.Plaintext.Length]; - byte[] tamperedTag = testCase.Tag.AsSpan().ToArray(); - tamperedTag[0] ^= 1; + Assert.Throws("tagSizeInBytes", () => new AesGcm(testCase.Key, testCase.Tag.Length)); + } + else + { + byte[] ciphertext = new byte[testCase.Plaintext.Length]; + byte[] tag = new byte[testCase.Tag.Length]; - Assert.Throws("tag", () => - { - aesGcm.Decrypt(testCase.Nonce, testCase.Ciphertext, tamperedTag, plaintext, testCase.AssociatedData); - }); - } - else + using (var aesGcm = new AesGcm(testCase.Key, testCase.Tag.Length)) { - byte[] ciphertext = new byte[testCase.Plaintext.Length]; - byte[] tag = new byte[testCase.Tag.Length]; aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); Assert.Equal(testCase.Ciphertext, ciphertext); Assert.Equal(testCase.Tag, tag); - tag[0] ^= 1; - byte[] plaintext = new byte[testCase.Plaintext.Length]; - RandomNumberGenerator.Fill(plaintext); - Assert.Throws( - () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); - Assert.Equal(new byte[plaintext.Length], plaintext); + aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData); + Assert.Equal(testCase.Plaintext, plaintext); } } } + [Theory] + [MemberData(nameof(GetNistGcmTestCases))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + public static void AesGcmNistTestsTamperTag(AEADTest testCase) + { + if (PlatformDetection.IsOSX && testCase.Tag.Length != CryptoKitSupportedTagSizeInBytes) + { + return; + } + + using (var aesGcm = new AesGcm(testCase.Key, testCase.Tag.Length)) + { + byte[] ciphertext = new byte[testCase.Plaintext.Length]; + byte[] tag = new byte[testCase.Tag.Length]; + aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); + Assert.Equal(testCase.Ciphertext, ciphertext); + Assert.Equal(testCase.Tag, tag); + + tag[0] ^= 1; + + byte[] plaintext = new byte[testCase.Plaintext.Length]; + RandomNumberGenerator.Fill(plaintext); + Assert.Throws( + () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); + Assert.Equal(new byte[plaintext.Length], plaintext); + } + } + [Theory] [MemberData(nameof(GetNistGcmTestCasesWithNonEmptyPT))] [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void AesGcmNistTestsTamperCiphertext(AEADTest testCase) { - using (var aesGcm = new AesGcm(testCase.Key)) + if (PlatformDetection.IsOSX && testCase.Tag.Length != CryptoKitSupportedTagSizeInBytes) { - if (PlatformDetection.IsOSX && testCase.Tag.Length != CryptoKitSupportedTagSizeInBytes) - { - byte[] tamperedCiphertext = testCase.Ciphertext.AsSpan().ToArray(); - tamperedCiphertext[0] ^= 1; - Assert.Throws("tag", () => - { - aesGcm.Decrypt(testCase.Nonce, tamperedCiphertext, testCase.Tag, testCase.Plaintext, testCase.AssociatedData); - }); - } - else - { - byte[] ciphertext = new byte[testCase.Plaintext.Length]; - byte[] tag = new byte[testCase.Tag.Length]; - aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); - Assert.Equal(testCase.Ciphertext, ciphertext); - Assert.Equal(testCase.Tag, tag); + return; + } - ciphertext[0] ^= 1; + using (var aesGcm = new AesGcm(testCase.Key, testCase.Tag.Length)) + { + byte[] ciphertext = new byte[testCase.Plaintext.Length]; + byte[] tag = new byte[testCase.Tag.Length]; + aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); + Assert.Equal(testCase.Ciphertext, ciphertext); + Assert.Equal(testCase.Tag, tag); - byte[] plaintext = RandomNumberGenerator.GetBytes(testCase.Plaintext.Length); - Assert.Throws( - () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); - AssertExtensions.FilledWith(0, plaintext); - } + ciphertext[0] ^= 1; + + byte[] plaintext = RandomNumberGenerator.GetBytes(testCase.Plaintext.Length); + Assert.Throws( + () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); + AssertExtensions.FilledWith(0, plaintext); } } @@ -437,7 +493,7 @@ public static void UseAfterDispose() byte[] ciphertext = Array.Empty(); byte[] tag = "58e2fccefa7e3061367f1d57a4e7455a".HexToByteArray(); - AesGcm aesGcm = new AesGcm(key); + AesGcm aesGcm = new AesGcm(key, tag.Length); aesGcm.Dispose(); Assert.Throws(() => aesGcm.Encrypt(nonce, plaintext, ciphertext, new byte[tag.Length])); From dd6d9294b60ee88c2d64763febcedfed85e0e3d0 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Sat, 13 May 2023 14:20:32 -0400 Subject: [PATCH 2/2] Obsolete existing constructors and fix tests --- docs/project/list-of-diagnostics.md | 1 + .../Common/src/System/Obsoletions.cs | 3 +++ .../ref/System.Security.Cryptography.cs | 2 ++ .../System/Security/Cryptography/AesGcm.cs | 2 ++ .../tests/AesGcmTests.cs | 19 ++++++++++++++++++- 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 2dfe110075e86d..7c814cdf68ae87 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -107,6 +107,7 @@ The PR that reveals the implementation of the ` key) { } public AesGcm(System.ReadOnlySpan key, int tagSizeInBytes) { } public static bool IsSupported { get { throw null; } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs index a209d42cadd3de..476b9917e5557d 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs @@ -15,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 key) { ThrowIfNotSupported(); @@ -23,6 +24,7 @@ public AesGcm(ReadOnlySpan key) ImportKey(key); } + [Obsolete(Obsoletions.AesGcmTagConstructorMessage, DiagnosticId = Obsoletions.AesGcmTagConstructorDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public AesGcm(byte[] key) : this(new ReadOnlySpan(key ?? throw new ArgumentNullException(nameof(key)))) { diff --git a/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs b/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs index b417136c0053ab..46e70b2e993a33 100644 --- a/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs @@ -29,7 +29,7 @@ public static void EncryptTamperAADDecrypt(int dataLength, int additionalDataLen RandomNumberGenerator.Fill(key); RandomNumberGenerator.Fill(nonce); - using (var aesGcm = new AesGcm(key)) + using (var aesGcm = new AesGcm(key, tag.Length)) { aesGcm.Encrypt(nonce, plaintext, ciphertext, tag, additionalData); @@ -51,7 +51,12 @@ public static void EncryptTamperAADDecrypt(int dataLength, int additionalDataLen public static void InvalidKeyLength(int keyLength) { byte[] key = new byte[keyLength]; +#pragma warning disable SYSLIB0053 Assert.Throws(() => new AesGcm(key)); + Assert.Throws(() => new AesGcm(key.AsSpan())); +#pragma warning restore SYSLIB0053 + Assert.Throws(() => new AesGcm(key, AesGcm.TagByteSizes.MinSize)); + Assert.Throws(() => new AesGcm(key.AsSpan(), AesGcm.TagByteSizes.MinSize)); } [Theory] @@ -112,7 +117,9 @@ public static void InvalidTagSizeForUnspecifiedRequiredTag(int tagSize) RandomNumberGenerator.Fill(key); RandomNumberGenerator.Fill(nonce); +#pragma warning disable SYSLIB0053 using (var aesGcm = new AesGcm(key)) +#pragma warning restore SYSLIB0053 { Assert.Throws("tag", () => aesGcm.Encrypt(nonce, plaintext, ciphertext, tag)); Assert.Throws("tag", () => aesGcm.Decrypt(nonce, ciphertext, tag, plaintext)); @@ -255,7 +262,10 @@ public static void PlaintextAndCiphertextSizeDiffer(int ptLen, int ctLen) [Fact] public static void NullKey() { +#pragma warning disable SYSLIB0053 Assert.Throws(() => new AesGcm((byte[])null)); +#pragma warning restore SYSLIB0053 + Assert.Throws(() => new AesGcm((byte[])null, AesGcm.TagByteSizes.MinSize)); } [Fact] @@ -372,7 +382,9 @@ public static void InplaceEncryptTamperTagDecrypt() [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void AesGcmNistTestsUnspecifiedTagSize(AEADTest testCase) { +#pragma warning disable SYSLIB0053 using (var aesGcm = new AesGcm(testCase.Key)) +#pragma warning restore SYSLIB0053 { byte[] ciphertext = new byte[testCase.Plaintext.Length]; byte[] tag = new byte[testCase.Tag.Length]; @@ -979,8 +991,13 @@ public static void CtorThrowsPNSEIfNotSupported() { byte[] key = RandomNumberGenerator.GetBytes(256 / 8); +#pragma warning disable SYSLIB0053 Assert.Throws(() => new AesGcm(key)); Assert.Throws(() => new AesGcm(key.AsSpan())); +#pragma warning restore SYSLIB0053 + + Assert.Throws(() => new AesGcm(key, AesGcm.TagByteSizes.MinSize)); + Assert.Throws(() => new AesGcm(key.AsSpan(), AesGcm.TagByteSizes.MinSize)); } [Fact]