Skip to content

Commit be62f48

Browse files
Create HMACUtilities.cs
1 parent b922f29 commit be62f48

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Buffers;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
8+
using System.Security.Cryptography;
9+
using System.Text;
10+
11+
namespace SixLabors.ImageSharp.Web
12+
{
13+
/// <summary>
14+
/// Provides methods to compute a Hash-based Message Authentication Code (HMAC).
15+
/// </summary>
16+
public static class HMACUtilities
17+
{
18+
/// <summary>
19+
/// Computes a Hash-based Message Authentication Code (HMAC) by using the SHA256 hash function.
20+
/// </summary>
21+
/// <param name="value">The value to hash</param>
22+
/// <param name="secret">
23+
/// The secret key for <see cref="HMACSHA256"/>encryption.
24+
/// The key can be any length. However, the recommended size is 64 bytes.
25+
/// </param>
26+
/// <returns>The hashed <see cref="string"/>.</returns>
27+
public static unsafe string CreateSHA256HashCode(string value, byte[] secret)
28+
{
29+
static void Action(Span<char> chars, (IntPtr Ptr, int Length, string Value, byte[] Secret) args)
30+
{
31+
var bytes = new Span<byte>((byte*)args.Ptr, args.Length);
32+
Encoding.ASCII.GetBytes(args.Value, bytes);
33+
using var hashAlgorithm = new HMACSHA256(args.Secret);
34+
hashAlgorithm.TryComputeHash(bytes, MemoryMarshal.Cast<char, byte>(chars), out int _);
35+
}
36+
37+
// Bits to chars - 256/8/2
38+
return CreateHMAC(value, secret, 16, Action);
39+
}
40+
41+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
42+
private static unsafe string CreateHMAC(string value, byte[] secret, int length, SpanAction<char, (IntPtr Ptr, int Length, string Value, byte[] Secret)> action)
43+
{
44+
int byteCount = Encoding.ASCII.GetByteCount(value);
45+
46+
// Allocating a buffer from the pool is ~27% slower than stackalloc so use that for short strings
47+
if (byteCount < 257)
48+
{
49+
fixed (byte* bytesPtr = stackalloc byte[byteCount])
50+
{
51+
return string.Create(length, ((IntPtr)bytesPtr, byteCount, value, secret), action);
52+
}
53+
}
54+
55+
byte[] buffer = null;
56+
try
57+
{
58+
buffer = ArrayPool<byte>.Shared.Rent(byteCount);
59+
fixed (byte* bytesPtr = buffer.AsSpan(0, byteCount))
60+
{
61+
return string.Create(length, ((IntPtr)bytesPtr, byteCount, value, secret), action);
62+
}
63+
}
64+
finally
65+
{
66+
if (buffer != null)
67+
{
68+
ArrayPool<byte>.Shared.Return(buffer);
69+
}
70+
}
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)