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

View File

@ -27,7 +27,6 @@ namespace Microsoft.AspNetCore.Antiforgery
private MemoryStream? _stream; private MemoryStream? _stream;
private BinaryReader? _reader; private BinaryReader? _reader;
private BinaryWriter? _writer; private BinaryWriter? _writer;
private SHA256? _sha256;
public MemoryStream Stream 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) public char[] GetChars(int count)
{ {
if (_chars == null || _chars.Length < 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.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Security.Claims; using System.Security.Claims;
using System.Security.Cryptography;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
namespace Microsoft.AspNetCore.Antiforgery namespace Microsoft.AspNetCore.Antiforgery
@ -134,9 +135,13 @@ namespace Microsoft.AspNetCore.Antiforgery
writer.Flush(); writer.Flush();
var sha256 = serializationContext.Sha256; bool success = serializationContext.Stream.TryGetBuffer(out ArraySegment<byte> buffer);
var stream = serializationContext.Stream; if (!success)
var bytes = sha256.ComputeHash(stream.ToArray(), 0, checked((int)stream.Length)); {
throw new InvalidOperationException();
}
var bytes = SHA256.HashData(buffer);
return bytes; return bytes;
} }

View File

@ -110,23 +110,20 @@ namespace Microsoft.AspNetCore.WebSockets
// this concatenated value to obtain a 20-byte value and base64-encoding" // this concatenated value to obtain a 20-byte value and base64-encoding"
// https://tools.ietf.org/html/rfc6455#section-4.2.2 // 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 throw new InvalidOperationException("Could not compute the hash for the 'Sec-WebSocket-Accept' header.");
// 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);
} }
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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
@ -97,7 +98,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Infrastructure
private static string GetHashForFile(IFileInfo fileInfo) private static string GetHashForFile(IFileInfo fileInfo)
{ {
using (var sha256 = CryptographyAlgorithms.CreateSHA256()) using (var sha256 = SHA256.Create())
{ {
using (var readStream = fileInfo.CreateReadStream()) using (var readStream = fileInfo.CreateReadStream())
{ {

View File

@ -4,12 +4,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Razor.Infrastructure;
using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache 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 // 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 // as the actual key (better randomization and key distribution, so small vary
// values will generate dramatically different keys). // values will generate dramatically different keys).
using (var sha256 = CryptographyAlgorithms.CreateSHA256()) var contentBytes = Encoding.UTF8.GetBytes(key);
{ var hashedBytes = SHA256.HashData(contentBytes);
var contentBytes = Encoding.UTF8.GetBytes(key); return Convert.ToBase64String(hashedBytes);
var hashedBytes = sha256.ComputeHash(contentBytes);
return Convert.ToBase64String(hashedBytes);
}
} }
/// <inheritdoc /> /// <inheritdoc />

View File

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

View File

@ -280,8 +280,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
// Store this for use during the code redemption. // Store this for use during the code redemption.
properties.Items.Add(OAuthConstants.CodeVerifierKey, codeVerifier); properties.Items.Add(OAuthConstants.CodeVerifierKey, codeVerifier);
using var sha256 = SHA256.Create(); var challengeBytes = SHA256.HashData(Encoding.UTF8.GetBytes(codeVerifier));
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
var codeChallenge = WebEncoders.Base64UrlEncode(challengeBytes); var codeChallenge = WebEncoders.Base64UrlEncode(challengeBytes);
parameters[OAuthConstants.CodeChallengeKey] = codeChallenge; 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. // Store this for use during the code redemption. See RunAuthorizationCodeReceivedEventAsync.
properties.Items.Add(OAuthConstants.CodeVerifierKey, codeVerifier); properties.Items.Add(OAuthConstants.CodeVerifierKey, codeVerifier);
using var sha256 = SHA256.Create(); var challengeBytes = SHA256.HashData(Encoding.UTF8.GetBytes(codeVerifier));
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
var codeChallenge = WebEncoders.Base64UrlEncode(challengeBytes); var codeChallenge = WebEncoders.Base64UrlEncode(challengeBytes);
message.Parameters.Add(OAuthConstants.CodeChallengeKey, codeChallenge); 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? // TODO: Consider making this configurable? At least for testing?
private static readonly TimeSpan _heartbeatTickRate = TimeSpan.FromSeconds(1); private static readonly TimeSpan _heartbeatTickRate = TimeSpan.FromSeconds(1);
private static readonly RNGCryptoServiceProvider _keyGenerator = new RNGCryptoServiceProvider();
private readonly ConcurrentDictionary<string, (HttpConnectionContext Connection, ValueStopwatch Timer)> _connections = private readonly ConcurrentDictionary<string, (HttpConnectionContext Connection, ValueStopwatch Timer)> _connections =
new ConcurrentDictionary<string, (HttpConnectionContext Connection, ValueStopwatch Timer)>(StringComparer.Ordinal); new ConcurrentDictionary<string, (HttpConnectionContext Connection, ValueStopwatch Timer)>(StringComparer.Ordinal);
private readonly TimerAwaitable _nextHeartbeat; private readonly TimerAwaitable _nextHeartbeat;
@ -113,7 +111,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// 128 bit buffer / 8 bits per byte = 16 bytes // 128 bit buffer / 8 bits per byte = 16 bytes
Span<byte> buffer = stackalloc byte[16]; Span<byte> buffer = stackalloc byte[16];
// Generate the id with RNGCrypto because we want a cryptographically random id, which GUID is not // 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); return WebEncoders.Base64UrlEncode(buffer);
} }

View File

@ -513,19 +513,7 @@ namespace Microsoft.DotNet.OpenApi.Commands
private static byte[] GetHash(Stream stream) private static byte[] GetHash(Stream stream)
{ {
SHA256 algorithm; using (var algorithm = SHA256.Create())
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)
{ {
return algorithm.ComputeHash(stream); return algorithm.ComputeHash(stream);
} }