зеркало из https://github.com/dotnet/aspnetcore.git
Use HMACSHA1.HashData() for Identity TOTP values (#36724)
This commit is contained in:
Родитель
d847549c9b
Коммит
a7d3a230a7
|
@ -54,13 +54,24 @@ namespace Microsoft.AspNetCore.Identity
|
|||
return false;
|
||||
}
|
||||
|
||||
using var hash = new HMACSHA1(Base32.FromBase32(key));
|
||||
var keyBytes = Base32.FromBase32(key);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
var unixTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
#else
|
||||
using var hash = new HMACSHA1(keyBytes);
|
||||
var unixTimestamp = Convert.ToInt64(Math.Round((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds));
|
||||
#endif
|
||||
|
||||
var timestep = Convert.ToInt64(unixTimestamp / 30);
|
||||
// Allow codes from 90s in each direction (we could make this configurable?)
|
||||
for (int i = -2; i <= 2; i++)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
var expectedCode = Rfc6238AuthenticationService.ComputeTotp(keyBytes, (ulong)(timestep + i), modifier: null);
|
||||
#else
|
||||
var expectedCode = Rfc6238AuthenticationService.ComputeTotp(hash, (ulong)(timestep + i), modifier: null);
|
||||
#endif
|
||||
if (expectedCode == code)
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -30,7 +30,14 @@ namespace Microsoft.AspNetCore.Identity
|
|||
return bytes;
|
||||
}
|
||||
|
||||
internal static int ComputeTotp(HashAlgorithm hashAlgorithm, ulong timestepNumber, string modifier)
|
||||
internal static int ComputeTotp(
|
||||
#if NET6_0_OR_GREATER
|
||||
byte[] key,
|
||||
#else
|
||||
HashAlgorithm hashAlgorithm,
|
||||
#endif
|
||||
ulong timestepNumber,
|
||||
string modifier)
|
||||
{
|
||||
// # of 0's = length of pin
|
||||
const int Mod = 1000000;
|
||||
|
@ -38,7 +45,12 @@ namespace Microsoft.AspNetCore.Identity
|
|||
// See https://tools.ietf.org/html/rfc4226
|
||||
// We can add an optional modifier
|
||||
var timestepAsBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)timestepNumber));
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
var hash = HMACSHA1.HashData(key, ApplyModifier(timestepAsBytes, modifier));
|
||||
#else
|
||||
var hash = hashAlgorithm.ComputeHash(ApplyModifier(timestepAsBytes, modifier));
|
||||
#endif
|
||||
|
||||
// Generate DT string
|
||||
var offset = hash[hash.Length - 1] & 0xf;
|
||||
|
@ -53,7 +65,7 @@ namespace Microsoft.AspNetCore.Identity
|
|||
|
||||
private static byte[] ApplyModifier(byte[] input, string modifier)
|
||||
{
|
||||
if (String.IsNullOrEmpty(modifier))
|
||||
if (string.IsNullOrEmpty(modifier))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
@ -85,10 +97,15 @@ namespace Microsoft.AspNetCore.Identity
|
|||
|
||||
// Allow a variance of no greater than 9 minutes in either direction
|
||||
var currentTimeStep = GetCurrentTimeStepNumber();
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
return ComputeTotp(securityToken, currentTimeStep, modifier);
|
||||
#else
|
||||
using (var hashAlgorithm = new HMACSHA1(securityToken))
|
||||
{
|
||||
return ComputeTotp(hashAlgorithm, currentTimeStep, modifier);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool ValidateCode(byte[] securityToken, int code, string modifier = null)
|
||||
|
@ -100,11 +117,18 @@ namespace Microsoft.AspNetCore.Identity
|
|||
|
||||
// Allow a variance of no greater than 9 minutes in either direction
|
||||
var currentTimeStep = GetCurrentTimeStepNumber();
|
||||
|
||||
#if !NET6_0_OR_GREATER
|
||||
using (var hashAlgorithm = new HMACSHA1(securityToken))
|
||||
#endif
|
||||
{
|
||||
for (var i = -2; i <= 2; i++)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
var computedTotp = ComputeTotp(securityToken, (ulong)((long)currentTimeStep + i), modifier);
|
||||
#else
|
||||
var computedTotp = ComputeTotp(hashAlgorithm, (ulong)((long)currentTimeStep + i), modifier);
|
||||
#endif
|
||||
if (computedTotp == code)
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom.Html;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
||||
{
|
||||
|
@ -50,10 +45,10 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
|||
|
||||
public static string ComputeCode(string key)
|
||||
{
|
||||
var hash = new HMACSHA1(Base32.FromBase32(key));
|
||||
var unixTimestamp = Convert.ToInt64(Math.Round((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds));
|
||||
var keyBytes = Base32.FromBase32(key);
|
||||
var unixTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
var timestep = Convert.ToInt64(unixTimestamp / 30);
|
||||
var topt = Rfc6238AuthenticationService.ComputeTotp(hash, (ulong)timestep, modifier: null);
|
||||
var topt = Rfc6238AuthenticationService.ComputeTotp(keyBytes, (ulong)timestep, modifier: null);
|
||||
return topt.ToString("D6", CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче