-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Clean up some usage of SHA1 and SHA256 in the code base #24696
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
Changes from all commits
97ebdfe
cb97e54
fd1f41f
fac3aaf
b896a75
76d0095
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Security.Claims; | ||
using System.Security.Cryptography; | ||
using Microsoft.Extensions.ObjectPool; | ||
|
||
namespace Microsoft.AspNetCore.Antiforgery | ||
|
@@ -134,9 +135,13 @@ private byte[] ComputeSha256(IEnumerable<string> parameters) | |
|
||
writer.Flush(); | ||
|
||
var sha256 = serializationContext.Sha256; | ||
var stream = serializationContext.Stream; | ||
var bytes = sha256.ComputeHash(stream.ToArray(), 0, checked((int)stream.Length)); | ||
bool success = serializationContext.Stream.TryGetBuffer(out ArraySegment<byte> buffer); | ||
if (!success) | ||
{ | ||
throw new InvalidOperationException(); | ||
} | ||
|
||
var bytes = SHA256.HashData(buffer); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implicit span conversion? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. |
||
|
||
return bytes; | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -110,23 +110,20 @@ public static string CreateResponseKey(string requestKey) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
// this concatenated value to obtain a 20-byte value and base64-encoding" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// https://tools.ietf.org/html/rfc6455#section-4.2.2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
using (var algorithm = SHA1.Create()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// requestKey is already verified to be small (24 bytes) by 'IsRequestKeyValid()' and everything is 1:1 mapping to UTF8 bytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// so this can be hardcoded to 60 bytes for the requestKey + static websocket string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Span<byte> mergedBytes = stackalloc byte[60]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Encoding.UTF8.GetBytes(requestKey, mergedBytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
EncodedWebSocketKey.CopyTo(mergedBytes.Slice(24)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Span<byte> hashedBytes = stackalloc byte[20]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var success = algorithm.TryComputeHash(mergedBytes, hashedBytes, out var written); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!success || written != 20) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new InvalidOperationException("Could not compute the hash for the 'Sec-WebSocket-Accept' header."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// requestKey is already verified to be small (24 bytes) by 'IsRequestKeyValid()' and everything is 1:1 mapping to UTF8 bytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// so this can be hardcoded to 60 bytes for the requestKey + static websocket string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Span<byte> mergedBytes = stackalloc byte[60]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Encoding.UTF8.GetBytes(requestKey, mergedBytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
EncodedWebSocketKey.CopyTo(mergedBytes.Slice(24)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return Convert.ToBase64String(hashedBytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Span<byte> hashedBytes = stackalloc byte[20]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var written = SHA1.HashData(mergedBytes, hashedBytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it only allocates on Win7 & Win8. But it's still faster than alternatives: see dotnet/runtime#40510. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I care more about linux? Is it allocating? This change had a measurable impact on websocket upgrades when it was made. I want to make sure this isn't regressing that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @BrennanConroy did you commit those performance tests? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Last I checked Linux was neither Win7 nor Win8, so it should be non-allocating. :) Here's the Linux implementation, for reference: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have a suitable environment for running the perf tests, sorry. If you have a shared environment where I can do so, along with clear instructions for it (copy + paste these exact commands), I can give it a spin. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated to this PR, if IsRequestKeyValid is a hot method I think you can get significant perf gains in it from rolling your own logic. The framework method you're calling performs base64 decoding, but you don't actually need to decode it. You only care that it is valid base64 and that it decodes to 16 bytes. So a rough outline of the logic would be:
If any of these checks fail, you can fall back to the framework's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Linux Before:
After:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We had a try of this in #13196 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (written != 20) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new InvalidOperationException("Could not compute the hash for the 'Sec-WebSocket-Accept' header."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return Convert.ToBase64String(hashedBytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Security.Cryptography; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc.ViewFeatures; | ||
|
@@ -97,7 +98,7 @@ public string AddFileVersionToPath(PathString requestPathBase, string path) | |
|
||
private static string GetHashForFile(IFileInfo fileInfo) | ||
{ | ||
using (var sha256 = CryptographyAlgorithms.CreateSHA256()) | ||
using (var sha256 = SHA256.Create()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why isn't this one using the static method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's hashing a stream and the one-shot doesn't have a stream overload, only |
||
{ | ||
using (var readStream = fileInfo.CreateReadStream()) | ||
{ | ||
|
@@ -107,4 +108,4 @@ private static string GetHashForFile(IFileInfo fileInfo) | |
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're here, may as well improve the allocations here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is called exactly once at app start. Wasn't worth complicating the code for so little gain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's fair