Repository: dotnet/aspnetcore
File: src/DataProtection/DataProtection/src/SP800_108/ManagedSP800_108_CTR_HMACSHA512.cs (C#)

Code before:
        Span<byte> validationSubkey)
    {
        // netFX and netStandard dont have API to NOT use HashAlgorithm
        using HashAlgorithm prf = new HMACSHA512(kdk);

        // kdk is passed just to have a shared implementation for different framework versions
        DeriveKeys(kdk, label, contextHeader, contextData, operationSubkey, validationSubkey, prf);
    }
#endif

#if NET10_0_OR_GREATER
    /// <summary>
    /// For FIPS enabled scenarios we have no other choice but to enforce a minimum key length of 14 bytes.
    /// It should not be on the hot-path, but only needed for one-time derivation of keys when e.g. creating context header.
    /// </summary>
    /// <remarks>
    /// NIST SP 800-131A Rev. 2:
    /// Keys less than 112 bits in length are disallowed for HMAC generation.
    /// The use of key lengths ≥ 112 bits is acceptable for HMAC generation.
    /// <see href="https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf"/>
    /// </remarks>
    private const int FipsMinimumKeyLengthInBytes = 14;

    public static void DeriveKeys(ReadOnlySpan<byte> kdk, ReadOnlySpan<byte> label, ReadOnlySpan<byte> contextHeader, ReadOnlySpan<byte> contextData, Span<byte> operationSubkey, Span<byte> validationSubkey)
    {
        if (kdk.Length < FipsMinimumKeyLengthInBytes)
        {
            // The HMACSHA512 constructor internally pads short keys to the block size,
            // which satisfies the FIPS minimum key length requirement.
            using var prf = new HMACSHA512(kdk.ToArray());
            DeriveKeys(kdk, label, contextHeader, contextData, operationSubkey, validationSubkey, prf);
        }
        else
        {
            DeriveKeys(kdk, label, contextHeader, contextData, operationSubkey, validationSubkey, prf: null);
        }
    }
#endif

    /// <remarks>
    /// note: kdk will be used only if prf is null and only in later framework versions (10+)
    /// where static method on `HMACSHA512` exists which avoids allocations
    /// </remarks>
    private static void DeriveKeys(
        ReadOnlySpan<byte> kdk,
        ReadOnlySpan<byte> label,
        ReadOnlySpan<byte> contextHeader,
        ReadOnlySpan<byte> contextData,
        Span<byte> operationSubkey,
        Span<byte> validationSubkey,
        HashAlgorithm? prf = null)

Review comment (suggestion):
```C#
if (kdk.Length < FipsMinimumKeyLengthInBytes)
{
    Span<byte> zeroExtended = stackalloc byte[FipsMinimumKeyLengthInBytes];
    zeroExtend.Clear();
    kdk.CopyTo(zeroExtend);
    return DeriveKeys(zeroExtend, ...);
}
```

HMAC keys shorter than the algorithm's (internal) block size are zero-appended, so just doing that up front will give the same answer.

We clearly do that for the instance method.  I don't remember if we explicitly decided not to do it for the statics, or if it's just that it used to not be enforced by OSSL in their "FIPS mode" and now it is.

Code after:
        Span<byte> validationSubkey)
    {
        // netFX and netStandard dont have API to NOT use HashAlgorithm
        using HashAlgorithm prf = new HMACSHA512(kdk);

        // kdk is passed just to have a shared implementation for different framework versions
        DeriveKeys(kdk, label, contextHeader, contextData, operationSubkey, validationSubkey, prf);
    }
#endif

#if NET10_0_OR_GREATER
    /// <summary>
    /// For FIPS enabled scenarios we have no other choice but to enforce a minimum key length of 14 bytes.
    /// It should not be on the hot-path, but only needed for one-time derivation of keys when e.g. creating context header.
    /// </summary>
    /// <remarks>
    /// NIST SP 800-131A Rev. 2:
    /// Keys less than 112 bits in length are disallowed for HMAC generation.
    /// The use of key lengths ≥ 112 bits is acceptable for HMAC generation.
    /// <see href="https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf"/>
    /// </remarks>
    private const int FipsMinimumKeyLengthInBytes = 14;

    public static void DeriveKeys(ReadOnlySpan<byte> kdk, ReadOnlySpan<byte> label, ReadOnlySpan<byte> contextHeader, ReadOnlySpan<byte> contextData, Span<byte> operationSubkey, Span<byte> validationSubkey)
    {
        if (kdk.Length < FipsMinimumKeyLengthInBytes)
        {
            // When you provide a key to HMAC that is shorter than the algorithm's internal block size,
            // the HMAC algorithm internally pads the key with zeros to reach the block size.
            Span<byte> zeroExtended = stackalloc byte[FipsMinimumKeyLengthInBytes];
            zeroExtended.Clear();
            kdk.CopyTo(zeroExtended);

            DeriveKeys(zeroExtended, label, contextHeader, contextData, operationSubkey, validationSubkey, prf: null);
        }
        else
        {
            DeriveKeys(kdk, label, contextHeader, contextData, operationSubkey, validationSubkey, prf: null);
        }
    }
#endif

    /// <remarks>
    /// note: kdk will be used only if prf is null and only in later framework versions (10+)
    /// where static method on `HMACSHA512` exists which avoids allocations
    /// </remarks>
    private static void DeriveKeys(
        ReadOnlySpan<byte> kdk,
        ReadOnlySpan<byte> label,
        ReadOnlySpan<byte> contextHeader,
        ReadOnlySpan<byte> contextData,