Skip to content

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

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

Merged
merged 1 commit into from
Oct 6, 2021

Conversation

martincostello
Copy link
Member

PR Title

Use HMACSHA1.HashData() for Identity TOTP values in .net6.0 and later.

PR Description

Use the new static HMACSHA1.HashData() method when targeting .NET 6 and later to compute TOTP values for ASP.NET Core Identity.

I originally left this out of #36368 as Microsoft.Extensions.Identity.Core multi-targets which made it non-trivial to update, and also wanted to verify whether adding the hash defines to use the newer methods actually provided performance benefits.

I copied the relevant bits out of the solution into a throw-away console application using BenchmarkDotNet (source) and generated results for net461, netcoreapp2.0, net5.0 and net6.0 before and after the changes, the results of which are below.

The gains are relatively modest, but improve the happy path in all scenarios; the unhappy paths are slower as multiple TOTP codes are tested over the validity window.

I guess the consideration for the team to accept the PR or not is the balance of the additional code for multi-targeting and unhappy path time increase vs. the reduced allocations and time decrease to the happy paths.

Benchmarks Summary

  1. Ater the change, allocations are reduced for net6.0 in all benchmarked scenarios.
    • Rfc6238AuthenticationService_GenerateCode - 416 B vs. 80 B (0.19)
    • Rfc6238AuthenticationService_ValidateCode_Invalid - 928 B vs. 400 B (0.43)
    • Rfc6238AuthenticationService_ValidateCode_Valid - 672 B vs. 160 B (0.23)
    • AuthenticatorTokenProvider_ValidateAsync_Invalid - 1,064 B vs. 520 B (0.48)
    • AuthenticatorTokenProvider_ValidateAsync_Valid - 808 B vs. 280 B (0.34)
  2. After the change, performance is increased for token generation and validation of valid tokens.
    • Rfc6238AuthenticationService_GenerateCode - 1.141 μs vs. 0.820 μs (0.71)
    • Rfc6238AuthenticationService_ValidateCode_Valid - 2.120 μs vs. 1.633 μs (0.77)
    • AuthenticatorTokenProvider_ValidateAsync_Valid - 2.680 μs vs. 2.198 μs (0.82)
  3. After the change, performance is decreased for validation of invalid tokens.
    • Rfc6238AuthenticationService_ValidateCode_Invalid - 2.995 μs vs. 3.981 μs (1.33)
    • AuthenticatorTokenProvider_ValidateAsync_Invalid - 3.539 μs vs. 4.546 μs (1.28)
  4. Allocations and performance are effectively unchanged for other TFMs.

Before

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1237 (21H1/May2021Update)
Intel Core i9-9980HK CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.100-rc.1.21458.32
  [Host]               : .NET 6.0.0 (6.0.21.45113), X64 RyuJIT
  .NET 5.0             : .NET 5.0.10 (5.0.1021.41214), X64 RyuJIT
  .NET 6.0             : .NET 6.0.0 (6.0.21.45113), X64 RyuJIT
  .NET Core 2.0        : .NET Core 2.1.30 (CoreCLR 4.6.30411.01, CoreFX 4.6.30411.02), X64 RyuJIT
  .NET Framework 4.6.1 : .NET Framework 4.8 (4.8.4400.0), X64 RyuJIT

Method Job Runtime Mean Error StdDev Gen 0 Allocated
Rfc6238AuthenticationService_GenerateCode .NET 5.0 .NET 5.0 1.201 μs 0.0221 μs 0.0254 μs 0.0496 416 B
Rfc6238AuthenticationService_ValidateCode_Invalid .NET 5.0 .NET 5.0 2.930 μs 0.0431 μs 0.0382 μs 0.1106 928 B
Rfc6238AuthenticationService_ValidateCode_Valid .NET 5.0 .NET 5.0 2.073 μs 0.0356 μs 0.0316 μs 0.0801 672 B
AuthenticatorTokenProvider_ValidateAsync_Invalid .NET 5.0 .NET 5.0 3.629 μs 0.0713 μs 0.0666 μs 0.1297 1,096 B
AuthenticatorTokenProvider_ValidateAsync_Valid .NET 5.0 .NET 5.0 2.262 μs 0.0263 μs 0.0246 μs 0.0839 712 B
Rfc6238AuthenticationService_GenerateCode .NET 6.0 .NET 6.0 1.141 μs 0.0175 μs 0.0146 μs 0.0496 416 B
Rfc6238AuthenticationService_ValidateCode_Invalid .NET 6.0 .NET 6.0 2.995 μs 0.0587 μs 0.0763 μs 0.1106 928 B
Rfc6238AuthenticationService_ValidateCode_Valid .NET 6.0 .NET 6.0 2.120 μs 0.0395 μs 0.0732 μs 0.0801 672 B
AuthenticatorTokenProvider_ValidateAsync_Invalid .NET 6.0 .NET 6.0 3.539 μs 0.0686 μs 0.0984 μs 0.1259 1,064 B
AuthenticatorTokenProvider_ValidateAsync_Valid .NET 6.0 .NET 6.0 2.680 μs 0.0502 μs 0.0469 μs 0.0954 808 B
Rfc6238AuthenticationService_GenerateCode .NET Core 2.0 .NET Core 2.0 1.555 μs 0.0197 μs 0.0185 μs 0.0648 416 B
Rfc6238AuthenticationService_ValidateCode_Invalid .NET Core 2.0 .NET Core 2.0 3.670 μs 0.0733 μs 0.0979 μs 0.1450 928 B
Rfc6238AuthenticationService_ValidateCode_Valid .NET Core 2.0 .NET Core 2.0 2.124 μs 0.0410 μs 0.0856 μs 0.0839 544 B
AuthenticatorTokenProvider_ValidateAsync_Invalid .NET Core 2.0 .NET Core 2.0 4.550 μs 0.0644 μs 0.0603 μs 0.1678 1,096 B
AuthenticatorTokenProvider_ValidateAsync_Valid .NET Core 2.0 .NET Core 2.0 3.073 μs 0.0498 μs 0.0416 μs 0.1106 712 B
Rfc6238AuthenticationService_GenerateCode .NET Framework 4.6.1 .NET Framework 4.6.1 5.257 μs 0.0781 μs 0.0692 μs 0.1068 714 B
Rfc6238AuthenticationService_ValidateCode_Invalid .NET Framework 4.6.1 .NET Framework 4.6.1 18.235 μs 0.2762 μs 0.2583 μs 0.2441 1,677 B
Rfc6238AuthenticationService_ValidateCode_Valid .NET Framework 4.6.1 .NET Framework 4.6.1 11.702 μs 0.1713 μs 0.1431 μs 0.1831 1,196 B
AuthenticatorTokenProvider_ValidateAsync_Invalid .NET Framework 4.6.1 .NET Framework 4.6.1 19.840 μs 0.3898 μs 0.3647 μs 0.2747 1,942 B
AuthenticatorTokenProvider_ValidateAsync_Valid .NET Framework 4.6.1 .NET Framework 4.6.1 9.964 μs 0.1686 μs 0.2308 μs 0.1831 1,220 B

After

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1237 (21H1/May2021Update)
Intel Core i9-9980HK CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.100-rc.1.21458.32
  [Host]               : .NET 6.0.0 (6.0.21.45113), X64 RyuJIT
  .NET 5.0             : .NET 5.0.10 (5.0.1021.41214), X64 RyuJIT
  .NET 6.0             : .NET 6.0.0 (6.0.21.45113), X64 RyuJIT
  .NET Core 2.0        : .NET Core 2.1.30 (CoreCLR 4.6.30411.01, CoreFX 4.6.30411.02), X64 RyuJIT
  .NET Framework 4.6.1 : .NET Framework 4.8 (4.8.4400.0), X64 RyuJIT

Method Job Runtime Mean Error StdDev Gen 0 Allocated
Rfc6238AuthenticationService_GenerateCode .NET 5.0 .NET 5.0 1,198.7 ns 23.15 ns 30.10 ns 0.0496 416 B
Rfc6238AuthenticationService_ValidateCode_Invalid .NET 5.0 .NET 5.0 2,929.8 ns 31.74 ns 26.50 ns 0.1106 928 B
Rfc6238AuthenticationService_ValidateCode_Valid .NET 5.0 .NET 5.0 2,111.0 ns 40.97 ns 53.27 ns 0.0801 672 B
AuthenticatorTokenProvider_ValidateAsync_Invalid .NET 5.0 .NET 5.0 3,654.5 ns 71.57 ns 70.29 ns 0.1297 1,096 B
AuthenticatorTokenProvider_ValidateAsync_Valid .NET 5.0 .NET 5.0 2,341.9 ns 41.62 ns 36.90 ns 0.0839 712 B
Rfc6238AuthenticationService_GenerateCode .NET 6.0 .NET 6.0 819.9 ns 8.29 ns 7.76 ns 0.0095 80 B
Rfc6238AuthenticationService_ValidateCode_Invalid .NET 6.0 .NET 6.0 3,980.9 ns 68.15 ns 60.42 ns 0.0458 400 B
Rfc6238AuthenticationService_ValidateCode_Valid .NET 6.0 .NET 6.0 1,633.0 ns 28.60 ns 38.17 ns 0.0191 160 B
AuthenticatorTokenProvider_ValidateAsync_Invalid .NET 6.0 .NET 6.0 4,545.6 ns 45.67 ns 40.48 ns 0.0610 520 B
AuthenticatorTokenProvider_ValidateAsync_Valid .NET 6.0 .NET 6.0 2,198.1 ns 23.11 ns 18.04 ns 0.0305 280 B
Rfc6238AuthenticationService_GenerateCode .NET Core 2.0 .NET Core 2.0 1,524.3 ns 23.51 ns 23.09 ns 0.0648 416 B
Rfc6238AuthenticationService_ValidateCode_Invalid .NET Core 2.0 .NET Core 2.0 3,721.4 ns 68.79 ns 60.98 ns 0.1450 928 B
Rfc6238AuthenticationService_ValidateCode_Valid .NET Core 2.0 .NET Core 2.0 2,578.7 ns 39.96 ns 35.43 ns 0.1030 672 B
AuthenticatorTokenProvider_ValidateAsync_Invalid .NET Core 2.0 .NET Core 2.0 4,612.7 ns 33.87 ns 30.03 ns 0.1678 1,096 B
AuthenticatorTokenProvider_ValidateAsync_Valid .NET Core 2.0 .NET Core 2.0 2,913.8 ns 135.06 ns 389.68 ns 0.1717 1,096 B
Rfc6238AuthenticationService_GenerateCode .NET Framework 4.6.1 .NET Framework 4.6.1 5,261.9 ns 63.61 ns 56.39 ns 0.1068 714 B
Rfc6238AuthenticationService_ValidateCode_Invalid .NET Framework 4.6.1 .NET Framework 4.6.1 18,217.5 ns 208.25 ns 184.61 ns 0.2441 1,677 B
Rfc6238AuthenticationService_ValidateCode_Valid .NET Framework 4.6.1 .NET Framework 4.6.1 12,094.2 ns 237.48 ns 263.96 ns 0.1831 1,196 B
AuthenticatorTokenProvider_ValidateAsync_Invalid .NET Framework 4.6.1 .NET Framework 4.6.1 19,645.9 ns 186.44 ns 174.40 ns 0.2747 1,942 B
AuthenticatorTokenProvider_ValidateAsync_Valid .NET Framework 4.6.1 .NET Framework 4.6.1 9,944.8 ns 101.70 ns 79.40 ns 0.1831 1,220 B

Use HMACSHA1.HashData() when targeting .NET 6 and later to compute TOTP values for ASP.NET Core Identity.
@ghost ghost added area-identity Includes: Identity and providers community-contribution Indicates that the PR has been added by a community member labels Sep 19, 2021
@Tratcher Tratcher requested review from HaoK and Tratcher October 5, 2021 20:41
@Tratcher
Copy link
Member

Tratcher commented Oct 5, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@HaoK
Copy link
Member

HaoK commented Oct 5, 2021

Looks reasonable to me, @blowdart any concerns with this?

@blowdart
Copy link
Contributor

blowdart commented Oct 5, 2021

No, removing our implementation is a good thing :)

@HaoK
Copy link
Member

HaoK commented Oct 5, 2021

Thanks for the great PR with data @martincostello !

@HaoK HaoK merged commit a7d3a23 into dotnet:main Oct 6, 2021
@ghost ghost added this to the 7.0-preview1 milestone Oct 6, 2021
@martincostello martincostello deleted the Use-HMACSHA1-OneShot-TOTP branch October 6, 2021 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-identity Includes: Identity and providers community-contribution Indicates that the PR has been added by a community member Perf
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants