Skip to content

Commit 26f91f1

Browse files
committed
Use TryComputeHash to reduce allocations.
1 parent 16fdbce commit 26f91f1

File tree

5 files changed

+27
-16
lines changed

5 files changed

+27
-16
lines changed

src/MySqlConnector/Core/ServerSession.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, IOBehavio
520520
Log.Debug("Session{0} sending change user request due to changed Database={1}", m_logArguments);
521521
DatabaseOverride = null;
522522
}
523-
var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData!, 0, cs.Password);
523+
var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData!, cs.Password);
524524
using (var changeUserPayload = ChangeUserPayload.Create(cs.UserID, hashedPassword, cs.Database, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
525525
await SendAsync(changeUserPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
526526
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -561,7 +561,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
561561
{
562562
case "mysql_native_password":
563563
AuthPluginData = switchRequest.Data;
564-
var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password);
564+
var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, cs.Password);
565565
payload = new PayloadData(hashedPassword);
566566
await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
567567
return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

src/MySqlConnector/Protocol/Payloads/ChangeUserPayload.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
using System;
12
using MySqlConnector.Protocol.Serialization;
23

34
namespace MySqlConnector.Protocol.Payloads
45
{
56
internal static class ChangeUserPayload
67
{
7-
public static PayloadData Create(string user, byte[] authResponse, string? schemaName, CharacterSet characterSet, byte[]? connectionAttributes)
8+
public static PayloadData Create(string user, ReadOnlySpan<byte> authResponse, string? schemaName, CharacterSet characterSet, byte[]? connectionAttributes)
89
{
910
var writer = new ByteBufferWriter();
1011

src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSe
5252
// TODO: verify server capabilities
5353
var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, useCompression, characterSet);
5454
writer.WriteNullTerminatedString(cs.UserID);
55-
var authenticationResponse = AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, 0, cs.Password);
55+
var authenticationResponse = AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, cs.Password);
5656
writer.Write((byte) authenticationResponse.Length);
5757
writer.Write(authenticationResponse);
5858

src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,33 @@ namespace MySqlConnector.Protocol.Serialization
77
{
88
internal static class AuthenticationUtility
99
{
10-
public static byte[] CreateAuthenticationResponse(byte[] challenge, int offset, string password) =>
11-
string.IsNullOrEmpty(password) ? Utility.EmptyByteArray : HashPassword(challenge, offset, password);
10+
public static byte[] CreateAuthenticationResponse(ReadOnlySpan<byte> challenge, string password) =>
11+
string.IsNullOrEmpty(password) ? Utility.EmptyByteArray : HashPassword(challenge, password);
1212

1313
/// <summary>
1414
/// Hashes a password with the "Secure Password Authentication" method.
1515
/// </summary>
1616
/// <param name="challenge">The 20-byte random challenge (from the "auth-plugin-data" in the initial handshake).</param>
17-
/// <param name="offset">The offset of the start of the challenge within <paramref name="challenge"/>.</param>
1817
/// <param name="password">The password to hash.</param>
1918
/// <returns>A 20-byte password hash.</returns>
2019
/// <remarks>See <a href="https://dev.mysql.com/doc/internals/en/secure-password-authentication.html">Secure Password Authentication</a>.</remarks>
21-
public static byte[] HashPassword(byte[] challenge, int offset, string password)
20+
public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password)
2221
{
2322
using var sha1 = SHA1.Create();
24-
var combined = new byte[40];
25-
Buffer.BlockCopy(challenge, offset, combined, 0, 20);
23+
Span<byte> combined = stackalloc byte[40];
24+
challenge.CopyTo(combined);
2625

2726
var passwordBytes = Encoding.UTF8.GetBytes(password);
28-
var hashedPassword = sha1.ComputeHash(passwordBytes);
27+
Span<byte> hashedPassword = stackalloc byte[20];
28+
sha1.TryComputeHash(passwordBytes, hashedPassword, out _);
29+
sha1.TryComputeHash(hashedPassword, combined.Slice(20), out _);
2930

30-
var doubleHashedPassword = sha1.ComputeHash(hashedPassword);
31-
Buffer.BlockCopy(doubleHashedPassword, 0, combined, 20, 20);
32-
33-
var xorBytes = sha1.ComputeHash(combined);
31+
Span<byte> xorBytes = stackalloc byte[20];
32+
sha1.TryComputeHash(combined, xorBytes, out _);
3433
for (int i = 0; i < hashedPassword.Length; i++)
3534
hashedPassword[i] ^= xorBytes[i];
3635

37-
return hashedPassword;
36+
return hashedPassword.ToArray();
3837
}
3938

4039
public static byte[] CreateScrambleResponse(byte[] nonce, string password)

src/MySqlConnector/Utilities/Utility.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,17 @@ public static Task<T> TaskFromException<T>(Exception exception)
367367
public static byte[] EmptyByteArray { get; } = Array.Empty<byte>();
368368
#endif
369369

370+
#if NET45 || NET461 || NET471 || NETSTANDARD1_3 || NETSTANDARD2_0
371+
public static bool TryComputeHash(this HashAlgorithm hashAlgorithm, ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
372+
{
373+
// assume caller supplies a large-enough buffer so we don't have to bounds-check it
374+
var output = hashAlgorithm.ComputeHash(source.ToArray());
375+
output.AsSpan().CopyTo(destination);
376+
bytesWritten = output.Length;
377+
return true;
378+
}
379+
#endif
380+
370381
#if !NETSTANDARD2_1 && !NETCOREAPP3_0
371382
public static Task CompletedValueTask => CompletedTask;
372383
#else

0 commit comments

Comments
 (0)