Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check output size in Expand and DeriveKey in HKDF and throw ArgumentOutOfRangeException when needed #52259

Closed
wants to merge 2 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -363,4 +363,7 @@
<data name="Cryptography_FeedbackSizeNotSupported" xml:space="preserve">
<value>The current platform does not support the specified feedback size.</value>
</data>
</root>
<data name="Cryptography_OutputLength_OutOfRange" xml:space="preserve">
<value>The current platform does not support the specified feedback size.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,17 @@ private static void Extract(HashAlgorithmName hashAlgorithmName, int hashLength,
/// </summary>
/// <param name="hashAlgorithmName">The hash algorithm used for HMAC operations.</param>
/// <param name="prk">The pseudorandom key of at least <see cref="HashLength"/> bytes (usually the output from Expand step).</param>
/// <param name="outputLength">The length of the output keying material.</param>
/// <param name="outputLength">The length of the output keying material that must be larger than 0.</param>
/// <param name="info">The optional context and application specific information.</param>
/// <returns>The output keying material.</returns>
public static byte[] Expand(HashAlgorithmName hashAlgorithmName, byte[] prk, int outputLength, byte[]? info = null)
{
if (prk == null)
throw new ArgumentNullException(nameof(prk));

if (outputLength <= 0)
throw new ArgumentOutOfRangeException(nameof(outputLength), SR.Cryptography_OutputLength_OutOfRange);

int hashLength = HashLength(hashAlgorithmName);

// Constant comes from section 2.3 (the constraint on L in the Inputs section)
Expand All @@ -108,12 +111,15 @@ public static byte[] Expand(HashAlgorithmName hashAlgorithmName, byte[] prk, int
/// </summary>
/// <param name="hashAlgorithmName">The hash algorithm used for HMAC operations.</param>
/// <param name="prk">The pseudorandom key of at least <see cref="HashLength"/> bytes (usually the output from Expand step).</param>
/// <param name="output">The destination buffer to receive the output keying material.</param>
/// <param name="output">The destination buffer to receive the output keying material with a size of as least 1.</param>
/// <param name="info">The context and application specific information (can be an empty span).</param>
public static void Expand(HashAlgorithmName hashAlgorithmName, ReadOnlySpan<byte> prk, Span<byte> output, ReadOnlySpan<byte> info)
{
int hashLength = HashLength(hashAlgorithmName);

if (output.Length == 0)
throw new ArgumentOutOfRangeException(nameof(output), SR.Cryptography_OutputLength_OutOfRange);

// Constant comes from section 2.3 (the constraint on L in the Inputs section)
int maxOkmLength = 255 * hashLength;
if (output.Length > maxOkmLength)
Expand Down Expand Up @@ -170,7 +176,7 @@ private static void Expand(HashAlgorithmName hashAlgorithmName, int hashLength,
/// </summary>
/// <param name="hashAlgorithmName">The hash algorithm used for HMAC operations.</param>
/// <param name="ikm">The input keying material.</param>
/// <param name="outputLength">The length of the output keying material.</param>
/// <param name="outputLength">The length of the output keying material that must be larger than 0.</param>
/// <param name="salt">The optional salt value (a non-secret random value). If not provided it defaults to a byte array of <see cref="HashLength"/> zeros.</param>
/// <param name="info">The optional context and application specific information.</param>
/// <returns>The output keying material.</returns>
Expand All @@ -179,6 +185,9 @@ public static byte[] DeriveKey(HashAlgorithmName hashAlgorithmName, byte[] ikm,
if (ikm == null)
throw new ArgumentNullException(nameof(ikm));

if (outputLength <= 0)
throw new ArgumentOutOfRangeException(nameof(outputLength), SR.Cryptography_OutputLength_OutOfRange);

int hashLength = HashLength(hashAlgorithmName);
Debug.Assert(hashLength <= 512 / 8, "hashLength is larger than expected, consider increasing this value or using regular allocation");

Expand All @@ -202,13 +211,16 @@ public static byte[] DeriveKey(HashAlgorithmName hashAlgorithmName, byte[] ikm,
/// </summary>
/// <param name="hashAlgorithmName">The hash algorithm used for HMAC operations.</param>
/// <param name="ikm">The input keying material.</param>
/// <param name="output">The output buffer representing output keying material.</param>
/// <param name="output">The output buffer representing output keying material with a size of as least 1.</param>
/// <param name="salt">The salt value (a non-secret random value).</param>
/// <param name="info">The context and application specific information (can be an empty span).</param>
public static void DeriveKey(HashAlgorithmName hashAlgorithmName, ReadOnlySpan<byte> ikm, Span<byte> output, ReadOnlySpan<byte> salt, ReadOnlySpan<byte> info)
{
int hashLength = HashLength(hashAlgorithmName);

if (output.Length == 0)
throw new ArgumentOutOfRangeException(nameof(output), SR.Cryptography_OutputLength_OutOfRange);

// Constant comes from section 2.3 (the constraint on L in the Inputs section)
int maxOkmLength = 255 * hashLength;
if (output.Length > maxOkmLength)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,24 @@ public void Rfc5869ExpandOkmPotentiallyOverflowingValue()
() => HKDF.Expand(HashAlgorithmName.SHA1, prk, 8421505, Array.Empty<byte>()));
}

[Fact]
public void Rfc5869ExpandOutputLengthZero()
{
byte[] prk = new byte[20];
AssertExtensions.Throws<ArgumentOutOfRangeException>(
"outputLength",
() => HKDF.Expand(HashAlgorithmName.SHA1, prk, 0, Array.Empty<byte>()));
}

[Fact]
public void Rfc5869ExpandOutputLengthLessThanZero()
{
byte[] prk = new byte[20];
AssertExtensions.Throws<ArgumentOutOfRangeException>(
"outputLength",
() => HKDF.Expand(HashAlgorithmName.SHA1, prk, -1, Array.Empty<byte>()));
}

[Fact]
public void Rfc5869DeriveKeyNullIkm()
{
Expand All @@ -463,6 +481,24 @@ public void Rfc5869DeriveKeyOkmPotentiallyOverflowingValue()
"outputLength",
() => HKDF.DeriveKey(HashAlgorithmName.SHA1, ikm, 8421505, Array.Empty<byte>(), Array.Empty<byte>()));
}

[Fact]
public void Rfc5869DeriveOutputLengthZero()
{
byte[] ikm = new byte[20];
AssertExtensions.Throws<ArgumentOutOfRangeException>(
"outputLength",
() => HKDF.DeriveKey(HashAlgorithmName.SHA1, ikm, 0, Array.Empty<byte>(), Array.Empty<byte>()));
}

[Fact]
public void Rfc5869DeriveOutputLengthLessThanZero()
{
byte[] ikm = new byte[20];
AssertExtensions.Throws<ArgumentOutOfRangeException>(
"outputLength",
() => HKDF.DeriveKey(HashAlgorithmName.SHA1, ikm, -1, Array.Empty<byte>(), Array.Empty<byte>()));
}
}

public class HkdfSpanTests : HKDFTests
Expand Down Expand Up @@ -530,6 +566,17 @@ public void Rfc5869OkmMaxSizePotentiallyOverflowingValue()
() => HKDF.Expand(HashAlgorithmName.SHA1, prk, okm, Array.Empty<byte>()));
}

[Fact]
public void Rfc5869ExpandOutputLengthZero()
{
byte[] prk = new byte[20];
byte[] okm = new byte[0];

AssertExtensions.Throws<ArgumentOutOfRangeException>(
"output",
() => HKDF.Expand(HashAlgorithmName.SHA1, prk, okm, Array.Empty<byte>()));
}

[Fact]
public void Rfc5869DeriveKeySpanOkmMaxSizePlusOne()
{
Expand All @@ -549,6 +596,17 @@ public void Rfc5869DeriveKeySpanOkmPotentiallyOverflowingValue()
"output",
() => HKDF.DeriveKey(HashAlgorithmName.SHA1, ikm, okm, Array.Empty<byte>(), Array.Empty<byte>()));
}

[Fact]
public void Rfc5869DeriveKeyOutputLengthZero()
{
byte[] ikm = new byte[20];
byte[] okm = new byte[0];

AssertExtensions.Throws<ArgumentOutOfRangeException>(
"output",
() => HKDF.DeriveKey(HashAlgorithmName.SHA1, ikm, okm, Array.Empty<byte>(), Array.Empty<byte>()));
}
}
}
}