Clean up some usage of SHA1 and SHA256 in the code base (#24696)

* AntiForgery: Use SHA256 one-shot

* AuthN: Use SHA256 one-shot APIs

* MVC/Razor: Cleanup SHA256 references

* WebSockets: Use SHA1 one-shots

* dotnet-openapi: Use preferred SHA256 factory

* SignalR: Prefer RNG.Fill over RNG.GetBytes
This commit is contained in:
Levi Broderick 2020-08-10 14:26:41 -07:00 committed by GitHub
parent adea6638cc
commit bbf7c8780c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 35 additions and 125 deletions

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.WebUtilities;
@ -35,12 +35,8 @@ namespace Microsoft.AspNetCore.Antiforgery
private static string ComputeCookieName(string applicationId)
{
using (var sha256 = CryptographyAlgorithms.CreateSHA256())
{
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(applicationId));
var subHash = hash.Take(8).ToArray();
return WebEncoders.Base64UrlEncode(subHash);
}
byte[] fullHash = SHA256.HashData(Encoding.UTF8.GetBytes(applicationId));
return WebEncoders.Base64UrlEncode(fullHash, 0, 8);
}
}
}

View File

@ -27,7 +27,6 @@ namespace Microsoft.AspNetCore.Antiforgery
private MemoryStream? _stream;
private BinaryReader? _reader;
private BinaryWriter? _writer;
private SHA256? _sha256;
public MemoryStream Stream
{
@ -82,23 +81,6 @@ namespace Microsoft.AspNetCore.Antiforgery
}
}
public SHA256 Sha256
{
get
{
if (_sha256 == null)
{
_sha256 = CryptographyAlgorithms.CreateSHA256();
}
return _sha256;
}
private set
{
_sha256 = value;
}
}
public char[] GetChars(int count)
{
if (_chars == null || _chars.Length < count)

View File

@ -1,25 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Cryptography;
namespace Microsoft.AspNetCore.Antiforgery
{
internal static class CryptographyAlgorithms
{
public static SHA256 CreateSHA256()
{
try
{
return SHA256.Create();
}
// SHA256.Create is documented to throw this exception on FIPS compliant machines.
// See: https://msdn.microsoft.com/en-us/library/z08hz7ad%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
catch (System.Reflection.TargetInvocationException)
{
// Fallback to a FIPS compliant SHA256 algorithm.
return new SHA256CryptoServiceProvider();
}
}
}
}

View File

@ -5,6 +5,7 @@ using System;
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 @@ namespace Microsoft.AspNetCore.Antiforgery
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);
return bytes;
}

View File

@ -110,23 +110,20 @@ namespace Microsoft.AspNetCore.WebSockets
// 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 written = SHA1.HashData(mergedBytes, hashedBytes);
if (written != 20)
{
// 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.");
}
return Convert.ToBase64String(hashedBytes);
throw new InvalidOperationException("Could not compute the hash for the 'Sec-WebSocket-Accept' header.");
}
return Convert.ToBase64String(hashedBytes);
}
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Cryptography;
namespace Microsoft.AspNetCore.Mvc.Razor.Infrastructure
{
internal static class CryptographyAlgorithms
{
public static SHA256 CreateSHA256()
{
try
{
return SHA256.Create();
}
// SHA256.Create is documented to throw this exception on FIPS compliant machines.
// See: https://msdn.microsoft.com/en-us/library/z08hz7ad%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
catch (System.Reflection.TargetInvocationException)
{
// Fallback to a FIPS compliant SHA256 algorithm.
return new SHA256CryptoServiceProvider();
}
}
}
}

View File

@ -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 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Infrastructure
private static string GetHashForFile(IFileInfo fileInfo)
{
using (var sha256 = CryptographyAlgorithms.CreateSHA256())
using (var sha256 = SHA256.Create())
{
using (var readStream = fileInfo.CreateReadStream())
{
@ -107,4 +108,4 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Infrastructure
}
}
}
}
}

View File

@ -4,12 +4,11 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Razor.Infrastructure;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
@ -180,12 +179,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
// The key is typically too long to be useful, so we use a cryptographic hash
// as the actual key (better randomization and key distribution, so small vary
// values will generate dramatically different keys).
using (var sha256 = CryptographyAlgorithms.CreateSHA256())
{
var contentBytes = Encoding.UTF8.GetBytes(key);
var hashedBytes = sha256.ComputeHash(contentBytes);
return Convert.ToBase64String(hashedBytes);
}
var contentBytes = Encoding.UTF8.GetBytes(key);
var hashedBytes = SHA256.HashData(contentBytes);
return Convert.ToBase64String(hashedBytes);
}
/// <inheritdoc />

View File

@ -68,8 +68,7 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
// Store this for use during the code redemption.
properties.Items.Add(OAuthConstants.CodeVerifierKey, codeVerifier);
using var sha256 = SHA256.Create();
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
var challengeBytes = SHA256.HashData(Encoding.UTF8.GetBytes(codeVerifier));
var codeChallenge = WebEncoders.Base64UrlEncode(challengeBytes);
queryStrings[OAuthConstants.CodeChallengeKey] = codeChallenge;

View File

@ -280,8 +280,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
// Store this for use during the code redemption.
properties.Items.Add(OAuthConstants.CodeVerifierKey, codeVerifier);
using var sha256 = SHA256.Create();
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
var challengeBytes = SHA256.HashData(Encoding.UTF8.GetBytes(codeVerifier));
var codeChallenge = WebEncoders.Base64UrlEncode(challengeBytes);
parameters[OAuthConstants.CodeChallengeKey] = codeChallenge;

View File

@ -375,8 +375,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
// Store this for use during the code redemption. See RunAuthorizationCodeReceivedEventAsync.
properties.Items.Add(OAuthConstants.CodeVerifierKey, codeVerifier);
using var sha256 = SHA256.Create();
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
var challengeBytes = SHA256.HashData(Encoding.UTF8.GetBytes(codeVerifier));
var codeChallenge = WebEncoders.Base64UrlEncode(challengeBytes);
message.Parameters.Add(OAuthConstants.CodeChallengeKey, codeChallenge);

View File

@ -23,8 +23,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// TODO: Consider making this configurable? At least for testing?
private static readonly TimeSpan _heartbeatTickRate = TimeSpan.FromSeconds(1);
private static readonly RNGCryptoServiceProvider _keyGenerator = new RNGCryptoServiceProvider();
private readonly ConcurrentDictionary<string, (HttpConnectionContext Connection, ValueStopwatch Timer)> _connections =
new ConcurrentDictionary<string, (HttpConnectionContext Connection, ValueStopwatch Timer)>(StringComparer.Ordinal);
private readonly TimerAwaitable _nextHeartbeat;
@ -113,7 +111,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// 128 bit buffer / 8 bits per byte = 16 bytes
Span<byte> buffer = stackalloc byte[16];
// Generate the id with RNGCrypto because we want a cryptographically random id, which GUID is not
_keyGenerator.GetBytes(buffer);
RandomNumberGenerator.Fill(buffer);
return WebEncoders.Base64UrlEncode(buffer);
}

View File

@ -513,19 +513,7 @@ namespace Microsoft.DotNet.OpenApi.Commands
private static byte[] GetHash(Stream stream)
{
SHA256 algorithm;
try
{
algorithm = SHA256.Create();
}
catch (TargetInvocationException)
{
// SHA256.Create is documented to throw this exception on FIPS-compliant machines. See
// https://msdn.microsoft.com/en-us/library/z08hz7ad Fall back to a FIPS-compliant SHA256 algorithm.
algorithm = new SHA256CryptoServiceProvider();
}
using (algorithm)
using (var algorithm = SHA256.Create())
{
return algorithm.ComputeHash(stream);
}