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

[API Proposal]: KMAC #93494

Closed
vcsjones opened this issue Oct 13, 2023 · 6 comments · Fixed by #96025
Closed

[API Proposal]: KMAC #93494

vcsjones opened this issue Oct 13, 2023 · 6 comments · Fixed by #96025
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.Security
Milestone

Comments

@vcsjones
Copy link
Member

vcsjones commented Oct 13, 2023

Background and motivation

This is the continuation of SHA-3 that was introduced in .NET 8, which is to bring in KMAC-128 and KMAC-256 as defined in SP800-185.

KMAC is a keyed-hash function based on Keccak with variable length MACs. As such, it takes a key, a message, an optional customization string, and produces a variable length MAC.

This API surface is very similar to SHAKE in terms of naming, and supporting both instance based appending and static one-shots.

A note on the name "customization string": this is in according with the name of SP800-185. Its name is understood in the context of KMAC.

API Proposal

Note: The API shape is identical between the classes, the only variation is the name of the class.

namespace System.Security.Cryptography;

public sealed class Kmac128 : IDisposable {
    public Kmac128(byte[] key, byte[]? customizationString = null);
    public Kmac128(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    // One shots
    public static byte[] HashData(
        byte[] key,
        byte[] source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        ReadOnlySpan<byte> source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        ReadOnlySpan<byte> source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static byte[] HashData(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null,
        CancellationToken cancellationToken = default);

    public static ValueTask<byte[]> HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        int outputLength,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);

    public static ValueTask HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        Memory<byte> desintation,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);
}


public sealed class Kmac256 : IDisposable {
    public Kmac256(byte[] key, byte[]? customizationString = null);
    public Kmac256(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    // One shots
    public static byte[] HashData(
        byte[] key,
        byte[] source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        ReadOnlySpan<byte> source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        ReadOnlySpan<byte> source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static byte[] HashData(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null,
        CancellationToken cancellationToken = default);

    public static ValueTask<byte[]> HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        int outputLength,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);

    public static ValueTask HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        Memory<byte> desintation,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);
}


public sealed class KmacXof128 : IDisposable {
    public KmacXof128(byte[] key, byte[]? customizationString = null);
    public KmacXof128(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    // One shots
    public static byte[] HashData(
        byte[] key,
        byte[] source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        ReadOnlySpan<byte> source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        ReadOnlySpan<byte> source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static byte[] HashData(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null,
        CancellationToken cancellationToken = default);

    public static ValueTask<byte[]> HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        int outputLength,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);

    public static ValueTask HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        Memory<byte> desintation,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);
}


public sealed class KmacXof256 : IDisposable {
    public KmacXof256(byte[] key, byte[]? customizationString = null);
    public KmacXof256(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    // One shots
    public static byte[] HashData(
        byte[] key,
        byte[] source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        ReadOnlySpan<byte> source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        ReadOnlySpan<byte> source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static byte[] HashData(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null,
        CancellationToken cancellationToken = default);

    public static ValueTask<byte[]> HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        int outputLength,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);

    public static ValueTask HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        Memory<byte> desintation,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);
}

API Usage

The usage is similar for all of KMAC depending on XOF and the security strength.

byte[] key = GetKmacKey();
ReadOnlySpan<byte> customizationString = "unrelated function"u8;

using (KMAC128 kmac = new KMAC128(key, customizationString))
{
    kmac.AppendData("Hello .NET"u8);
    byte[] mac = kmac.GetHashAndReset(outputLength: 32);
}

Alternative Designs

I had originally considered XOF being a parameter of KMAC instead of separate types. I however landed on separate types, despite them having identical API surfaces between the XOF and non-XOF KMACs.

The reason for this is because there is a possibility to further expand the XOF API surface independently of the non-XOF API surface.

An API may exist in the future like SqueezeHash that allows repeated calls to derive the MAC. This API would make sense for the XOF KMACs, but would not make sense for the non-XOF. That API would have to throw.


So why don't we have a SqueezeHash or similar API? Today, we do not have that capability because OpenSSL does not let XOF "stream" back results. OpenSSL does not let you do successive "squeeze" calls. They may address this in the future. Windows gives us this capability, however it is close enough to our "2+ primitives rule" that I chose not to include it.

There is some argument for not including KMACXOF at all, however it is in a complete enough state between two platforms that it seems worthwhile to support it without making any tradeoffs.

Risks

No response

@vcsjones vcsjones added api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Security labels Oct 13, 2023
@vcsjones vcsjones added this to the 9.0.0 milestone Oct 13, 2023
@vcsjones vcsjones self-assigned this Oct 13, 2023
@ghost
Copy link

ghost commented Oct 13, 2023

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

This is the continuation of SHA-3 that was introduced in .NET 8, which is to bring in KMAC-128 and KMAC-256 as defined in SP800-185.

KMAC is a keyed-hash function based on Keccak with variable length MACs. As such, it takes a key, a message, an optional customization string, and produces a variable length MAC.

This API surface is very similar to SHAKE in terms of naming, and supporting both instance based appending and static one-shots.

A note on the name "customization string": this is in according with the name of SP800-185. Its name is understood in the context of KMAC.

API Proposal

Note: The API shape is identical between the classes, the only variation is the name of the class.

namespace System.Security.Cryptography;

public sealed class KMAC128 : IDisposable {
    public KMAC128(byte[] key, byte[]? customizationString = null);
    public KMAC128(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    // One shots
    public static byte[] HashData(
        byte[] key,
        byte[] source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpana<byte> key,
        ReadOnlySpan<byte> source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpana<byte> key,
        ReadOnlySpan<byte> source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static byte[] HashData(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null,
        CancellationToken cancellationToken = default);

    public static ValueTask<byte[]> HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        int outputLength,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);

    public static ValueTask HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        Memory<byte> desintation,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);
}


public sealed class KMAC256 : IDisposable {
    public KMAC256(byte[] key, byte[]? customizationString = null);
    public KMAC256(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    // One shots
    public static byte[] HashData(
        byte[] key,
        byte[] source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpana<byte> key,
        ReadOnlySpan<byte> source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpana<byte> key,
        ReadOnlySpan<byte> source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static byte[] HashData(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null,
        CancellationToken cancellationToken = default);

    public static ValueTask<byte[]> HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        int outputLength,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);

    public static ValueTask HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        Memory<byte> desintation,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);
}


public sealed class KMACXOF128 : IDisposable {
    public KMACXOF128(byte[] key, byte[]? customizationString = null);
    public KMACXOF128(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    // One shots
    public static byte[] HashData(
        byte[] key,
        byte[] source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpana<byte> key,
        ReadOnlySpan<byte> source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpana<byte> key,
        ReadOnlySpan<byte> source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static byte[] HashData(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null,
        CancellationToken cancellationToken = default);

    public static ValueTask<byte[]> HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        int outputLength,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);

    public static ValueTask HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        Memory<byte> desintation,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);
}


public sealed class KMACXOF256 : IDisposable {
    public KMACXOF256(byte[] key, byte[]? customizationString = null);
    public KMACXOF256(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    // One shots
    public static byte[] HashData(
        byte[] key,
        byte[] source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpana<byte> key,
        ReadOnlySpan<byte> source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpana<byte> key,
        ReadOnlySpan<byte> source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static byte[] HashData(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null);

    public static byte[] HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        int outputLength,
        ReadOnlySpan<byte> customizationString = default);

    public static void HashData(
        ReadOnlySpan<byte> key,
        Stream source,
        Span<byte> destination,
        ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(
        byte[] key,
        Stream source,
        int outputLength,
        byte[]? customizationString = null,
        CancellationToken cancellationToken = default);

    public static ValueTask<byte[]> HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        int outputLength,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);

    public static ValueTask HashDataAsync(
        ReadOnlyMemory<byte> key,
        Stream source,
        Memory<byte> desintation,
        ReadOnlyMemory<byte> customizationString = default,
        CancellationToken cancellationToken = default);
}

API Usage

The usage is similar for all of KMAC depending on XOF and the security strength.

byte[] key = GetKmacKey();
ReadOnlySpan<byte> customizationString = "unrelated function"u8;

using (KMAC128 kmac = new KMAC128(key, customizationString))
{
    kmac.AppendData("Hello .NET"u8);
    byte[] mac = kmac.GetHashAndReset(outputLength: 32);
}

Alternative Designs

I had originally considered XOF being a parameter of KMAC instead of separate types. I however landed on separate types, despite them having identical API surfaces between the XOF and non-XOF KMACs.

The reason for this is because there is a possibility to further expand the XOF API surface independently of the non-XOF API surface.

An API may exist in the future like SqueezeHash that allows repeated calls to derive the MAC. This API would make sense for the XOF KMACs, but would not make sense for the non-XOF. That API would have to throw.


So why don't we have a SqueezeHash or similar API? Today, we do not have that capability because OpenSSL does not let XOF "stream" back results. OpenSSL does not let you do successive "squeeze" calls. They may address this in the future. Windows gives us this capability, however it is close enough to our "2+ primitives rule" that I chose not to include it.

There is some argument for not including KMACXOF at all, however it is in a complete enough state between two platforms that it seems worthwhile to support it without making any tradeoffs.

Risks

No response

Author: vcsjones
Assignees: vcsjones
Labels:

api-suggestion, area-System.Security

Milestone: 9.0.0

@bartonjs
Copy link
Member

I'm not a super-fan of the 7 successive caps of KMACXOF. KmacXof would be the next-most-reasonable, but would be asserting that we think "Kmac128" is a reasonable name... which I'm not sure it is.

Otherwise, it seems reasonable to me. Especially the differentiation for if we can ever add the streaming semantics of "squeeze" to the XOFs.

@vcsjones
Copy link
Member Author

Given that we called SHAKE in .NET 8 Shake128 and not SHAKE128, and KMACXOF is a bit alphabet soup, I am leaning toward Kmac128 and KmacXof128.

@bartonjs bartonjs added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Oct 17, 2023
@terrajobst
Copy link
Member

terrajobst commented Nov 7, 2023

Video

  • Looks good as proposed
  • There is a spelling error in desintation, should be destination
namespace System.Security.Cryptography;

public sealed class Kmac128 : IDisposable
{
    public Kmac128(byte[] key, byte[]? customizationString = null);
    public Kmac128(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    public static byte[] HashData(byte[] key, byte[] source, int outputLength, byte[]? customizationString = null);
    public static byte[] HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, int outputLength, ReadOnlySpan<byte> customizationString = default);
    public static void HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, Span<byte> destination, ReadOnlySpan<byte> customizationString = default);
    public static byte[] HashData(byte[] key, Stream source, int outputLength, byte[]? customizationString = null);
    public static byte[] HashData(ReadOnlySpan<byte> key, Stream source, int outputLength, ReadOnlySpan<byte> customizationString = default);
    public static void HashData(ReadOnlySpan<byte> key, Stream source, Span<byte> destination, ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(byte[] key, Stream source, int outputLength, byte[]? customizationString = null, CancellationToken cancellationToken = default);
    public static ValueTask<byte[]> HashDataAsync(ReadOnlyMemory<byte> key, Stream source, int outputLength, ReadOnlyMemory<byte> customizationString = default, CancellationToken cancellationToken = default);
    public static ValueTask HashDataAsync(ReadOnlyMemory<byte> key, Stream source, Memory<byte> destination, ReadOnlyMemory<byte> customizationString = default, CancellationToken cancellationToken = default);
}

public sealed class Kmac256 : IDisposable
{
    public Kmac256(byte[] key, byte[]? customizationString = null);
    public Kmac256(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    public static byte[] HashData(byte[] key, byte[] source, int outputLength, byte[]? customizationString = null);
    public static byte[] HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, int outputLength, ReadOnlySpan<byte> customizationString = default);
    public static void HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, Span<byte> destination, ReadOnlySpan<byte> customizationString = default);
    public static byte[] HashData(byte[] key, Stream source, int outputLength, byte[]? customizationString = null);
    public static byte[] HashData(ReadOnlySpan<byte> key, Stream source, int outputLength, ReadOnlySpan<byte> customizationString = default);
    public static void HashData(ReadOnlySpan<byte> key, Stream source, Span<byte> destination, ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(byte[] key, Stream source, int outputLength, byte[]? customizationString = null, CancellationToken cancellationToken = default);
    public static ValueTask<byte[]> HashDataAsync(ReadOnlyMemory<byte> key, Stream source, int outputLength, ReadOnlyMemory<byte> customizationString = default, CancellationToken cancellationToken = default);
    public static ValueTask HashDataAsync(ReadOnlyMemory<byte> key, Stream source, Memory<byte> destination, ReadOnlyMemory<byte> customizationString = default, CancellationToken cancellationToken = default);
}

public sealed class KmacXof128 : IDisposable
{
    public KmacXof128(byte[] key, byte[]? customizationString = null);
    public KmacXof128(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    public static byte[] HashData(byte[] key, byte[] source, int outputLength, byte[]? customizationString = null);
    public static byte[] HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, int outputLength, ReadOnlySpan<byte> customizationString = default);
    public static void HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, Span<byte> destination, ReadOnlySpan<byte> customizationString = default);
    public static byte[] HashData(byte[] key, Stream source, int outputLength, byte[]? customizationString = null);
    public static byte[] HashData(ReadOnlySpan<byte> key, Stream source, int outputLength, ReadOnlySpan<byte> customizationString = default);
    public static void HashData(ReadOnlySpan<byte> key, Stream source, Span<byte> destination, ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(byte[] key, Stream source, int outputLength, byte[]? customizationString = null, CancellationToken cancellationToken = default);
    public static ValueTask<byte[]> HashDataAsync(ReadOnlyMemory<byte> key, Stream source, int outputLength, ReadOnlyMemory<byte> customizationString = default, CancellationToken cancellationToken = default);
    public static ValueTask HashDataAsync(ReadOnlyMemory<byte> key, Stream source, Memory<byte> destination, ReadOnlyMemory<byte> customizationString = default, CancellationToken cancellationToken = default);
}

public sealed class KmacXof256 : IDisposable
{
    public KmacXof256(byte[] key, byte[]? customizationString = null);
    public KmacXof256(ReadOnlySpan<byte> key, ReadOnlySpan<byte> customizationString = default);

    public static bool IsSupported { get; }

    public void AppendData(byte[] data);
    public void AppendData(ReadOnlySpan<byte> data);

    public byte[] GetHashAndReset(int outputLength);
    public void GetHashAndReset(Span<byte> destination);

    public byte[] GetCurrentHash(int outputLength);
    public void GetCurrentHash(Span<byte> destination);

    public void Dispose();

    public static byte[] HashData(byte[] key, byte[] source, int outputLength, byte[]? customizationString = null);
    public static byte[] HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, int outputLength, ReadOnlySpan<byte> customizationString = default);
    public static void HashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, Span<byte> destination, ReadOnlySpan<byte> customizationString = default);
    public static byte[] HashData(byte[] key, Stream source, int outputLength, byte[]? customizationString = null);
    public static byte[] HashData(ReadOnlySpan<byte> key, Stream source, int outputLength, ReadOnlySpan<byte> customizationString = default);
    public static void HashData(ReadOnlySpan<byte> key, Stream source, Span<byte> destination, ReadOnlySpan<byte> customizationString = default);

    public static ValueTask<byte[]> HashDataAsync(byte[] key, Stream source, int outputLength, byte[]? customizationString = null, CancellationToken cancellationToken = default);
    public static ValueTask<byte[]> HashDataAsync(ReadOnlyMemory<byte> key, Stream source, int outputLength, ReadOnlyMemory<byte> customizationString = default, CancellationToken cancellationToken = default);
    public static ValueTask HashDataAsync(ReadOnlyMemory<byte> key, Stream source, Memory<byte> destination, ReadOnlyMemory<byte> customizationString = default, CancellationToken cancellationToken = default);
}

@terrajobst terrajobst added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Nov 7, 2023
@vcsjones vcsjones modified the milestones: 9.0.0, Future Nov 29, 2023
@vcsjones
Copy link
Member Author

This is on hold pending some input from the Windows team about the CNG implementation.

@vcsjones vcsjones modified the milestones: Future, 9.0.0 Dec 14, 2023
@vcsjones
Copy link
Member Author

We have a path forward on Windows now, so going back to 9.0.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Dec 14, 2023
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jan 11, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Feb 11, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Security
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants