Use HMACSHA1.HashData() for Identity TOTP values (#36724)

This commit is contained in:
Martin Costello 2021-10-06 16:55:02 +01:00 коммит произвёл GitHub
Родитель d847549c9b
Коммит a7d3a230a7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 41 добавлений и 11 удалений

Просмотреть файл

@ -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);
}
}