diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLKem.cs b/src/libraries/Common/src/System/Security/Cryptography/MLKem.cs index 98d67d158c2496..efc74d3c0b338e 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/MLKem.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/MLKem.cs @@ -136,113 +136,29 @@ public void Encapsulate(Span ciphertext, Span sharedSecret) } /// - /// Creates an encapsulation ciphertext and shared secret, writing them into the provided buffers. + /// Creates an encapsulation ciphertext and shared secret. /// /// - /// The buffer to receive the ciphertext. - /// - /// - /// The buffer to receive the shared secret. - /// - /// - /// When this method returns, the total number of bytes written into . + /// When this method returns, the ciphertext. /// - /// - /// When this method returns, the total number of bytes written into . - /// - /// - /// An error occurred during encapsulation. - /// -or - - /// overlaps with . - /// - /// - /// is too small to hold the ciphertext. - /// -or- - /// is too small to hold the shared secret. - /// - /// The object has already been disposed. - public void Encapsulate( - Span ciphertext, - Span sharedSecret, - out int ciphertextBytesWritten, - out int sharedSecretBytesWritten) - { - if (ciphertext.Length < Algorithm.CiphertextSizeInBytes) - throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(ciphertext)); - - if (sharedSecret.Length < Algorithm.SharedSecretSizeInBytes) - throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(sharedSecret)); - - Span ciphertextExact = ciphertext.Slice(0, Algorithm.CiphertextSizeInBytes); - Span sharedSecretExact = sharedSecret.Slice(0, Algorithm.SharedSecretSizeInBytes); - - if (ciphertextExact.Overlaps(sharedSecretExact)) - { - throw new CryptographicException(SR.Cryptography_OverlappingBuffers); - } - - ThrowIfDisposed(); - EncapsulateCore(ciphertextExact, sharedSecretExact); - ciphertextBytesWritten = ciphertextExact.Length; - sharedSecretBytesWritten = sharedSecretExact.Length; - } - - /// - /// Creates an encapsulation ciphertext and shared secret. - /// /// /// When this method returns, the shared secret. /// - /// - /// The ciphertext. - /// /// /// An error occurred during encapsulation. /// /// The object has already been disposed. - public byte[] Encapsulate(out byte[] sharedSecret) + public void Encapsulate(out byte[] ciphertext, out byte[] sharedSecret) { ThrowIfDisposed(); - byte[] ciphertext = new byte[Algorithm.CiphertextSizeInBytes]; + byte[] localCiphertext = new byte[Algorithm.CiphertextSizeInBytes]; byte[] localSharedSecret = new byte[Algorithm.SharedSecretSizeInBytes]; - EncapsulateCore(ciphertext, localSharedSecret); + EncapsulateCore(localCiphertext, localSharedSecret); sharedSecret = localSharedSecret; - return ciphertext; - } - - /// - /// Creates an encapsulation ciphertext and shared secret, writing the shared secret into a buffer. - /// - /// - /// When this method returns, the shared secret. - /// - /// - /// The ciphertext. - /// - /// - /// is not the correct size. - /// - /// - /// An error occurred during encapsulation. - /// - /// The object has already been disposed. - public byte[] Encapsulate(Span sharedSecret) - { - ThrowIfDisposed(); - - if (sharedSecret.Length != Algorithm.SharedSecretSizeInBytes) - { - throw new ArgumentException( - SR.Format(SR.Argument_DestinationImprecise, Algorithm.SharedSecretSizeInBytes), - nameof(sharedSecret)); - } - - byte[] ciphertext = new byte[Algorithm.CiphertextSizeInBytes]; - EncapsulateCore(ciphertext, sharedSecret); - return ciphertext; + ciphertext = localCiphertext; } /// @@ -296,42 +212,6 @@ public void Decapsulate(ReadOnlySpan ciphertext, Span sharedSecret) DecapsulateCore(ciphertext, sharedSecret); } - /// - /// Decapsulates a shared secret from a provided ciphertext. - /// - /// - /// The ciphertext. - /// - /// - /// The buffer to receive the shared secret. - /// - /// - /// When this method returns, the total number of bytes written into . - /// - /// - /// An error occurred during decapsulation. - /// - /// - /// is not the correct size. - /// -or- - /// is too small to hold the shared secret. - /// - /// The object has already been disposed. - public void Decapsulate(ReadOnlySpan ciphertext, Span sharedSecret, out int sharedSecretBytesWritten) - { - if (ciphertext.Length != Algorithm.CiphertextSizeInBytes) - throw new ArgumentException(SR.Argument_KemInvalidCiphertextLength, nameof(ciphertext)); - - if (sharedSecret.Length < Algorithm.SharedSecretSizeInBytes) - throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(sharedSecret)); - - ThrowIfDisposed(); - - Span sharedSecretExact = sharedSecret.Slice(0, Algorithm.SharedSecretSizeInBytes); - DecapsulateCore(ciphertext, sharedSecretExact); - sharedSecretBytesWritten = sharedSecretExact.Length; - } - /// /// Decapsulates a shared secret from a provided ciphertext. /// @@ -379,21 +259,6 @@ public byte[] Decapsulate(byte[] ciphertext) /// protected abstract void DecapsulateCore(ReadOnlySpan ciphertext, Span sharedSecret); - /// - /// Throws if the current instance is disposed. - /// - protected void ThrowIfDisposed() - { -#if NET - ObjectDisposedException.ThrowIf(_disposed, typeof(MLKem)); -#else - if (_disposed) - { - throw new ObjectDisposedException(typeof(MLKem).FullName); - } -#endif - } - /// /// Exports the private seed into the provided buffer. /// @@ -1899,6 +1764,18 @@ private static void ThrowIfNull( #endif } + private protected void ThrowIfDisposed() + { +#if NET + ObjectDisposedException.ThrowIf(_disposed, typeof(MLKem)); +#else + if (_disposed) + { + throw new ObjectDisposedException(typeof(MLKem).FullName); + } +#endif + } + private AsnWriter ExportEncryptedPkcs8PrivateKeyCore( ReadOnlySpan password, PbeParameters pbeParameters, diff --git a/src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs index 1e740458201401..ed4e5be32dcfdf 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs @@ -160,12 +160,6 @@ public void DecapsulateVectors() kem.Decapsulate(ciphertextBytes, sharedSecretBuffer); AssertExtensions.SequenceEqual(expectedSharedSecret, sharedSecretBuffer); - // Large enough buffers - sharedSecretBuffer.AsSpan().Clear(); - kem.Decapsulate(ciphertextBytes, sharedSecretBuffer, out int sharedSecretWritten); - AssertExtensions.SequenceEqual(expectedSharedSecret, sharedSecretBuffer); - Assert.Equal(vector.Algorithm.SharedSecretSizeInBytes, sharedSecretWritten); - // Allocating return byte[] sharedSecret = kem.Decapsulate(ciphertextBytes); AssertExtensions.SequenceEqual(expectedSharedSecret, sharedSecret); @@ -194,27 +188,6 @@ public void DecapsulateVectors_OverlappingBuffers_ExactBuffers(bool partial) } } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void DecapsulateVectors_OverlappingBuffers_LargeEnoughBuffers(bool partial) - { - foreach (MLKemTestDecapsulationVector vector in MLKemDecapsulationTestVectors) - { - byte[] decapsulationKeyBytes = vector.DecapsulationKey.HexToByteArray(); - byte[] ciphertextBytes = vector.Ciphertext.HexToByteArray(); - using MLKem kem = ImportDecapsulationKey(vector.Algorithm, decapsulationKeyBytes); - - Span sharedSecretBuffer = ciphertextBytes.AsSpan(partial ? 1 : 0); - - ReadOnlySpan expectedSharedSecret = vector.SharedSecret.HexToByteArray(); - - kem.Decapsulate(ciphertextBytes, sharedSecretBuffer, out int sharedSecretWritten); - Assert.Equal(vector.Algorithm.SharedSecretSizeInBytes, sharedSecretWritten); - AssertExtensions.SequenceEqual(expectedSharedSecret, sharedSecretBuffer.Slice(0, sharedSecretWritten)); - } - } - [Fact] public void Decapsulate_OnlyEncapsulationKey() { @@ -229,12 +202,6 @@ public void Decapsulate_OnlyEncapsulationKey() vector.Ciphertext.HexToByteArray(), sharedSecretBuffer)); - // Large enough buffer - Assert.ThrowsAny(() => kem.Decapsulate( - vector.Ciphertext.HexToByteArray(), - sharedSecretBuffer, - out _)); - // Allocating Assert.ThrowsAny(() => kem.Decapsulate(vector.Ciphertext.HexToByteArray())); } @@ -259,12 +226,6 @@ public void DecapsulateVectors_ModifiedCiphertext() kem.Decapsulate(ciphertext, sharedSecretExact); AssertExtensions.SequenceNotEqual(expectedSharedSecret, sharedSecretExact); - // Large enough buffer - sharedSecretBuffer.AsSpan().Clear(); - kem.Decapsulate(ciphertext, sharedSecretBuffer, out int sharedSecretWritten); - Assert.Equal(vector.Algorithm.SharedSecretSizeInBytes, sharedSecretWritten); - AssertExtensions.SequenceNotEqual(expectedSharedSecret, sharedSecretBuffer.AsSpan(0, sharedSecretWritten)); - // Allocating byte[] sharedSecret = kem.Decapsulate(ciphertext); AssertExtensions.SequenceNotEqual(expectedSharedSecret, sharedSecret); @@ -287,50 +248,14 @@ public void Encapsulate_NonDeterministic_Exact(MLKemAlgorithm algorithm) AssertExtensions.SequenceNotEqual(sharedSecret1, sharedSecret2); } - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public void Encapsulate_NonDeterministic_LargeEnough(MLKemAlgorithm algorithm) - { - using MLKem kem = GenerateKey(algorithm); - - byte[] ciphertext1 = new byte[algorithm.CiphertextSizeInBytes + 10]; - byte[] sharedSecret1 = new byte[algorithm.SharedSecretSizeInBytes + 10]; - byte[] ciphertext2 = new byte[algorithm.CiphertextSizeInBytes + 10]; - byte[] sharedSecret2 = new byte[algorithm.SharedSecretSizeInBytes + 10]; - kem.Encapsulate(ciphertext1, sharedSecret1, out int ciphertext1Written, out int sharedSecret1Written); - kem.Encapsulate(ciphertext2, sharedSecret2, out int ciphertext2Written, out int sharedSecret2Written); - - AssertExtensions.SequenceNotEqual( - ciphertext1.AsSpan(0, ciphertext1Written), - ciphertext2.AsSpan(0, ciphertext2Written)); - - AssertExtensions.SequenceNotEqual( - sharedSecret1.AsSpan(0, sharedSecret1Written), - sharedSecret2.AsSpan(0, sharedSecret2Written)); - } - [Theory] [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] public void Encapsulate_NonDeterministic_Allocating(MLKemAlgorithm algorithm) { using MLKem kem = GenerateKey(algorithm); - byte[] ciphertext1 = kem.Encapsulate(out byte[] sharedSecret1); - byte[] ciphertext2 = kem.Encapsulate(out byte[] sharedSecret2); - AssertExtensions.SequenceNotEqual(ciphertext1, ciphertext2); - AssertExtensions.SequenceNotEqual(sharedSecret1, sharedSecret2); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public void Encapsulate_NonDeterministic_WriteSharedSecretReturnCiphertext(MLKemAlgorithm algorithm) - { - using MLKem kem = GenerateKey(algorithm); - Span sharedSecret1 = new byte[algorithm.SharedSecretSizeInBytes]; - Span sharedSecret2 = new byte[algorithm.SharedSecretSizeInBytes]; - - byte[] ciphertext1 = kem.Encapsulate(sharedSecret1); - byte[] ciphertext2 = kem.Encapsulate(sharedSecret2); + kem.Encapsulate(out byte[] ciphertext1, out byte[] sharedSecret1); + kem.Encapsulate(out byte[] ciphertext2, out byte[] sharedSecret2); AssertExtensions.SequenceNotEqual(ciphertext1, ciphertext2); AssertExtensions.SequenceNotEqual(sharedSecret1, sharedSecret2); } @@ -351,29 +276,6 @@ public void Encapsulate_Roundtrip_ExactBuffers(MLKemAlgorithm algorithm) AssertExtensions.SequenceEqual(sharedSecret, decapsulatedSharedSecret); } - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public void Encapsulate_Roundtrip_LargeBuffers(MLKemAlgorithm algorithm) - { - using MLKem kem1 = GenerateKey(algorithm); - using MLKem kem2 = ImportEncapsulationKey(algorithm, kem1.ExportEncapsulationKey()); - - Span ciphertext = new byte[algorithm.CiphertextSizeInBytes + 10]; - Span sharedSecret = new byte[algorithm.SharedSecretSizeInBytes + 10]; - kem2.Encapsulate(ciphertext, sharedSecret, out int ciphertextWritten, out int sharedSecretWritten); - - Span decapsulatedSharedSecret = new byte[algorithm.SharedSecretSizeInBytes + 10]; - - kem1.Decapsulate( - ciphertext.Slice(0, ciphertextWritten), - decapsulatedSharedSecret, - out int decapsulatedSharedSecretWritten); - - AssertExtensions.SequenceEqual( - sharedSecret.Slice(0, sharedSecretWritten), - decapsulatedSharedSecret.Slice(0, decapsulatedSharedSecretWritten)); - } - [Theory] [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] public void Encapsulate_Roundtrip_Allocating(MLKemAlgorithm algorithm) @@ -381,26 +283,12 @@ public void Encapsulate_Roundtrip_Allocating(MLKemAlgorithm algorithm) using MLKem kem1 = GenerateKey(algorithm); using MLKem kem2 = ImportEncapsulationKey(algorithm, kem1.ExportEncapsulationKey()); - byte[] ciphertext = kem2.Encapsulate(out byte[] sharedSecret); + kem2.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret); byte[] decapsulatedSharedSecret = kem1.Decapsulate(ciphertext); AssertExtensions.SequenceEqual(sharedSecret, decapsulatedSharedSecret); } - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public void Encapsulate_Roundtrip_WriteSharedSecretReturnCiphertext(MLKemAlgorithm algorithm) - { - using MLKem kem1 = GenerateKey(algorithm); - using MLKem kem2 = ImportEncapsulationKey(algorithm, kem1.ExportEncapsulationKey()); - - Span sharedSecret = new byte[algorithm.SharedSecretSizeInBytes]; - byte[] ciphertext = kem2.Encapsulate(sharedSecret); - byte[] decapsulatedSharedSecret = kem1.Decapsulate(ciphertext); - - AssertExtensions.SequenceEqual(sharedSecret, new ReadOnlySpan(decapsulatedSharedSecret)); - } - [Theory] [InlineData(false)] [InlineData(true)] @@ -415,13 +303,6 @@ public void Encapsulate_Overlaps_Fail(bool partial) Span ciphertext = buffer.AsSpan(0, MLKemAlgorithm.MLKem512.CiphertextSizeInBytes); kem.Encapsulate(ciphertext, sharedSecret); }); - - Assert.Throws(() => - { - Span sharedSecret = buffer.AsSpan(partial ? 1 : 0, MLKemAlgorithm.MLKem512.SharedSecretSizeInBytes); - Span ciphertext = buffer.AsSpan(0, MLKemAlgorithm.MLKem512.CiphertextSizeInBytes); - kem.Encapsulate(ciphertext, sharedSecret, out _, out _); - }); } [Fact] @@ -433,38 +314,13 @@ public void Encapsulate_Overlaps_SameBuffer_Works() Span sharedSecret = buffer.AsSpan(0, algorithm.SharedSecretSizeInBytes); Span ciphertext = buffer.AsSpan(algorithm.SharedSecretSizeInBytes); - kem.Encapsulate(ciphertext, sharedSecret, out int ciphertextWritten, out int sharedSecretWritten); - Assert.Equal(algorithm.CiphertextSizeInBytes, ciphertextWritten); - Assert.Equal(algorithm.SharedSecretSizeInBytes, sharedSecretWritten); + kem.Encapsulate(ciphertext, sharedSecret); Span decapsulated = new byte[algorithm.SharedSecretSizeInBytes]; kem.Decapsulate(ciphertext, decapsulated); AssertExtensions.SequenceEqual(sharedSecret, decapsulated); } - [Fact] - public void Encapsulate_Overlaps_WhenTrimmed_Works() - { - MLKemAlgorithm algorithm = MLKemAlgorithm.MLKem512; - using MLKem kem = GenerateKey(algorithm); - - // sharedSecret does overlap ciphertext in this test. However, the part that overlaps will never - // be written to because it is trimmed to the exact size, which ends up with the buffers beside each - // other, which should work. - byte[] buffer = new byte[algorithm.SharedSecretSizeInBytes + algorithm.CiphertextSizeInBytes + 10]; - Span sharedSecret = buffer.AsSpan(0, algorithm.SharedSecretSizeInBytes + 10); - Span ciphertext = buffer.AsSpan(algorithm.SharedSecretSizeInBytes); - Assert.True(sharedSecret.Overlaps(ciphertext), "sharedSecret.Overlaps(ciphertext)"); - - kem.Encapsulate(ciphertext, sharedSecret, out int ciphertextWritten, out int sharedSecretWritten); - Assert.Equal(algorithm.CiphertextSizeInBytes, ciphertextWritten); - Assert.Equal(algorithm.SharedSecretSizeInBytes, sharedSecretWritten); - - Span decapsulated = new byte[algorithm.SharedSecretSizeInBytes]; - kem.Decapsulate(ciphertext.Slice(0, ciphertextWritten), decapsulated); - AssertExtensions.SequenceEqual(sharedSecret.Slice(0, sharedSecretWritten), decapsulated); - } - [Fact] public void TryExportPkcs8PrivateKey_Seed_Roundtrip() { @@ -696,7 +552,7 @@ private static void AssertSubjectPublicKeyInfo(MLKem kem, bool useTryExport, Rea AssertExtensions.SequenceEqual(expectedSpki, encodedSpki); using MLKem encapsulator = MLKem.ImportSubjectPublicKeyInfo(encodedSpki); - byte[] ciphertext = encapsulator.Encapsulate(out byte[] encapsulatorSharedSecret); + encapsulator.Encapsulate(out byte[] ciphertext, out byte[] encapsulatorSharedSecret); byte[] decapsulatedSharedSecret = kem.Decapsulate(ciphertext); AssertExtensions.SequenceEqual(encapsulatorSharedSecret, decapsulatedSharedSecret); } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/MLKemContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/MLKemContractTests.cs index c0c5ffee55b915..1867f2fa5227ae 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/MLKemContractTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/MLKemContractTests.cs @@ -146,130 +146,6 @@ public static void Encapsulate_Exact_SameNonOverlappingBuffer_Works(MLKemAlgorit kem.Encapsulate(ciphertextBuffer.Span, sharedSecretBuffer.Span); } - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Encapsulate_Written_DistinctBufferSameLength_Works(MLKemAlgorithm algorithm) - { - byte[] ciphertextBuffer = new byte[algorithm.CiphertextSizeInBytes]; - byte[] sharedSecretBuffer = new byte[algorithm.SharedSecretSizeInBytes]; - using MLKemContract kem = new(algorithm) - { - OnEncapsulateCore = (Span ciphertext, Span sharedSecret) => - { - AssertSameBuffer(ciphertext, ciphertextBuffer); - AssertSameBuffer(sharedSecret, sharedSecretBuffer); - } - }; - - kem.Encapsulate( - ciphertextBuffer, - sharedSecretBuffer, - out int ciphertextBytesWritten, - out int sharedSecretBytesWritten); - - Assert.Equal(algorithm.CiphertextSizeInBytes, ciphertextBytesWritten); - Assert.Equal(algorithm.SharedSecretSizeInBytes, sharedSecretBytesWritten); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Encapsulate_Written_DistinctBufferOversized_Works(MLKemAlgorithm algorithm) - { - byte[] ciphertextBuffer = new byte[algorithm.CiphertextSizeInBytes + 42]; - byte[] sharedSecretBuffer = new byte[algorithm.SharedSecretSizeInBytes + 42]; - using MLKemContract kem = new(algorithm) - { - OnEncapsulateCore = (Span ciphertext, Span sharedSecret) => - { - AssertSameBuffer(ciphertext, ciphertextBuffer.AsSpan(0, algorithm.CiphertextSizeInBytes)); - AssertSameBuffer(sharedSecret, sharedSecretBuffer.AsSpan(0, algorithm.SharedSecretSizeInBytes)); - } - }; - - kem.Encapsulate( - ciphertextBuffer, - sharedSecretBuffer, - out int ciphertextBytesWritten, - out int sharedSecretBytesWritten); - - Assert.Equal(algorithm.CiphertextSizeInBytes, ciphertextBytesWritten); - Assert.Equal(algorithm.SharedSecretSizeInBytes, sharedSecretBytesWritten); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Encapsulate_Written_UndersizedCiphertextBuffer(MLKemAlgorithm algorithm) - { - byte[] ciphertextBuffer = new byte[algorithm.CiphertextSizeInBytes - 1]; - byte[] sharedSecretBuffer = new byte[algorithm.SharedSecretSizeInBytes]; - using MLKemContract kem = new(algorithm); - - Assert.Throws("ciphertext", () => kem.Encapsulate( - ciphertextBuffer, - sharedSecretBuffer, - out _, - out _)); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Encapsulate_Written_UndersizedSharedSecretBuffer(MLKemAlgorithm algorithm) - { - byte[] ciphertextBuffer = new byte[algorithm.CiphertextSizeInBytes]; - byte[] sharedSecretBuffer = new byte[algorithm.SharedSecretSizeInBytes - 1]; - using MLKemContract kem = new(algorithm); - - Assert.Throws("sharedSecret", () => kem.Encapsulate( - ciphertextBuffer, - sharedSecretBuffer, - out _, - out _)); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Encapsulate_Written_Overlaps_WhenTrimmed_Works(MLKemAlgorithm algorithm) - { - // sharedSecret does overlap ciphertext in this test. However, the part that overlaps will never - // be written to because it is trimmed to the exact size, which ends up with the buffers beside each - // other, which should work. - byte[] buffer = new byte[algorithm.SharedSecretSizeInBytes + algorithm.CiphertextSizeInBytes + 10]; - Memory sharedSecretBuffer = buffer.AsMemory(0, algorithm.SharedSecretSizeInBytes + 10); - Memory ciphertextBuffer = buffer.AsMemory(algorithm.SharedSecretSizeInBytes); - AssertExtensions.TrueExpression(sharedSecretBuffer.Span.Overlaps(ciphertextBuffer.Span)); - - using MLKemContract kem = new(algorithm) - { - OnEncapsulateCore = (Span ciphertext, Span sharedSecret) => - { - AssertSameBuffer(ciphertext, ciphertextBuffer.Span.Slice(0, algorithm.CiphertextSizeInBytes)); - AssertSameBuffer(sharedSecret, sharedSecretBuffer.Span.Slice(0, algorithm.SharedSecretSizeInBytes)); - AssertExtensions.FalseExpression(ciphertext.Overlaps(sharedSecret)); - } - }; - - kem.Encapsulate( - ciphertextBuffer.Span, - sharedSecretBuffer.Span, - out int ciphertextWritten, - out int sharedSecretWritten); - - Assert.Equal(algorithm.CiphertextSizeInBytes, ciphertextWritten); - Assert.Equal(algorithm.SharedSecretSizeInBytes, sharedSecretWritten); - } - - [Fact] - public static void Encapsulate_Written_Disposed() - { - MLKemContract kem = new(MLKemAlgorithm.MLKem512); - kem.Dispose(); - Assert.Throws(() => kem.Encapsulate( - new byte[kem.Algorithm.CiphertextSizeInBytes], - new byte[kem.Algorithm.SharedSecretSizeInBytes], - out _, - out _)); - } - [Theory] [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] public static void Encapsulate_Allocated(MLKemAlgorithm algorithm) @@ -283,7 +159,7 @@ public static void Encapsulate_Allocated(MLKemAlgorithm algorithm) } }; - byte[] ciphertext = kem.Encapsulate(out byte[] sharedSecret); + kem.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret); Assert.Equal(algorithm.CiphertextSizeInBytes, ciphertext.Length); Assert.Equal(algorithm.SharedSecretSizeInBytes, sharedSecret.Length); @@ -291,42 +167,12 @@ public static void Encapsulate_Allocated(MLKemAlgorithm algorithm) AssertExtensions.FilledWith(0xFE, sharedSecret); } - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Encapsulate_Allocated_DestinationSecret(MLKemAlgorithm algorithm) - { - using MLKemContract kem = new(algorithm) - { - OnEncapsulateCore = (Span ciphertext, Span sharedSecret) => - { - ciphertext.Fill(0xCA); - sharedSecret.Fill(0xFE); - } - }; - - byte[] sharedSecret = new byte[algorithm.SharedSecretSizeInBytes]; - byte[] ciphertext = kem.Encapsulate(sharedSecret); - - Assert.Equal(algorithm.CiphertextSizeInBytes, ciphertext.Length); - AssertExtensions.FilledWith(0xCA, ciphertext); - AssertExtensions.FilledWith(0xFE, sharedSecret); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Encapsulate_Allocated_DestinationSecret_WrongSecretBufferSize(MLKemAlgorithm algorithm) - { - using MLKemContract kem = new(algorithm); - byte[] sharedSecret = new byte[algorithm.SharedSecretSizeInBytes + 1]; - AssertExtensions.Throws("sharedSecret", () => kem.Encapsulate(sharedSecret)); - } - [Fact] public static void Encapsulate_Allocated_Disposed() { MLKemContract kem = new(MLKemAlgorithm.MLKem512); kem.Dispose(); - Assert.Throws(() => kem.Encapsulate(out _)); + Assert.Throws(() => kem.Encapsulate(out _, out _)); } [Theory] @@ -412,86 +258,6 @@ public static void Decapsulate_Exact_OverlappingBuffers_Works(MLKemAlgorithm alg kem.Decapsulate(ciphertextBuffer.Span, sharedSecretBuffer.Span); } - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Decapsulate_Written_WrongCiphertextLength(MLKemAlgorithm algorithm) - { - using MLKemContract kem = new(algorithm); - AssertExtensions.Throws("ciphertext", () => kem.Decapsulate( - new byte[algorithm.CiphertextSizeInBytes - 1], - new byte[algorithm.SharedSecretSizeInBytes], - out _)); - - AssertExtensions.Throws("ciphertext", () => kem.Decapsulate( - new byte[algorithm.CiphertextSizeInBytes + 1], - new byte[algorithm.SharedSecretSizeInBytes], - out _)); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Decapsulate_Written_SharedSecretTooShort(MLKemAlgorithm algorithm) - { - using MLKemContract kem = new(algorithm); - AssertExtensions.Throws("sharedSecret", () => kem.Decapsulate( - new byte[algorithm.CiphertextSizeInBytes], - new byte[algorithm.SharedSecretSizeInBytes - 1], - out _)); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Decapsulate_Written_Diposed(MLKemAlgorithm algorithm) - { - MLKemContract kem = new(algorithm); - kem.Dispose(); - - Assert.Throws(() => kem.Decapsulate( - new byte[algorithm.CiphertextSizeInBytes], - new byte[algorithm.SharedSecretSizeInBytes], - out _)); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Decapsulate_Written_Oversized_Works(MLKemAlgorithm algorithm) - { - byte[] ciphertextBuffer = new byte[algorithm.CiphertextSizeInBytes]; - byte[] sharedSecretBuffer = new byte[algorithm.SharedSecretSizeInBytes + 42]; - - using MLKemContract kem = new(algorithm) - { - OnDecapsulateCore = (ReadOnlySpan ciphertext, Span sharedSecret) => - { - AssertSameBuffer(ciphertextBuffer, ciphertext); - AssertSameBuffer(sharedSecretBuffer.AsSpan(0, algorithm.SharedSecretSizeInBytes), sharedSecret); - } - }; - - kem.Decapsulate(ciphertextBuffer, sharedSecretBuffer, out int sharedSecretBytesWritten); - Assert.Equal(algorithm.SharedSecretSizeInBytes, sharedSecretBytesWritten); - } - - [Theory] - [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] - public static void Decapsulate_Written_Exact_Works(MLKemAlgorithm algorithm) - { - byte[] ciphertextBuffer = new byte[algorithm.CiphertextSizeInBytes]; - byte[] sharedSecretBuffer = new byte[algorithm.SharedSecretSizeInBytes]; - - using MLKemContract kem = new(algorithm) - { - OnDecapsulateCore = (ReadOnlySpan ciphertext, Span sharedSecret) => - { - AssertSameBuffer(ciphertextBuffer, ciphertext); - AssertSameBuffer(sharedSecretBuffer, sharedSecret); - } - }; - - kem.Decapsulate(ciphertextBuffer, sharedSecretBuffer, out int sharedSecretBytesWritten); - Assert.Equal(algorithm.SharedSecretSizeInBytes, sharedSecretBytesWritten); - } - [Theory] [MemberData(nameof(MLKemTestData.MLKemAlgorithms), MemberType = typeof(MLKemTestData))] public static void Decapsulate_Allocated_NullCiphertext(MLKemAlgorithm algorithm) 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 6f8b14f2412e39..4b85a927b186b1 100644 --- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs +++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs @@ -1877,14 +1877,11 @@ protected MLKem(System.Security.Cryptography.MLKemAlgorithm algorithm) { } public static bool IsSupported { get { throw null; } } public byte[] Decapsulate(byte[] ciphertext) { throw null; } public void Decapsulate(System.ReadOnlySpan ciphertext, System.Span sharedSecret) { } - public void Decapsulate(System.ReadOnlySpan ciphertext, System.Span sharedSecret, out int sharedSecretBytesWritten) { throw null; } protected abstract void DecapsulateCore(System.ReadOnlySpan ciphertext, System.Span sharedSecret); public void Dispose() { } protected virtual void Dispose(bool disposing) { } - public byte[] Encapsulate(out byte[] sharedSecret) { throw null; } - public byte[] Encapsulate(System.Span sharedSecret) { throw null; } + public void Encapsulate(out byte[] ciphertext, out byte[] sharedSecret) { throw null; } public void Encapsulate(System.Span ciphertext, System.Span sharedSecret) { } - public void Encapsulate(System.Span ciphertext, System.Span sharedSecret, out int ciphertextBytesWritten, out int sharedSecretBytesWritten) { throw null; } protected abstract void EncapsulateCore(System.Span ciphertext, System.Span sharedSecret); public byte[] ExportDecapsulationKey() { throw null; } public void ExportDecapsulationKey(System.Span destination) { } @@ -1925,7 +1922,6 @@ public void ExportPrivateSeed(System.Span destination) { } public static System.Security.Cryptography.MLKem ImportPrivateSeed(System.Security.Cryptography.MLKemAlgorithm algorithm, System.ReadOnlySpan source) { throw null; } public static System.Security.Cryptography.MLKem ImportSubjectPublicKeyInfo(byte[] source) { throw null; } public static System.Security.Cryptography.MLKem ImportSubjectPublicKeyInfo(System.ReadOnlySpan source) { throw null; } - protected void ThrowIfDisposed() { } public bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } public bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } public bool TryExportEncryptedPkcs8PrivateKey(string password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; }