Skip to content

Commit

Permalink
Reduce allocations and transformations when creating a token.
Browse files Browse the repository at this point in the history
  • Loading branch information
Brent Schmaltz authored and brentschmaltz committed Dec 5, 2023
1 parent f214b6d commit f40dd90
Show file tree
Hide file tree
Showing 19 changed files with 2,794 additions and 1,066 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using BenchmarkDotNet.Attributes;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

namespace Microsoft.IdentityModel.Benchmarks
{
[HideColumns("Type", "Job", "WarmupCount", "LaunchCount")]
[MemoryDiagnoser]
public class CreateTokenTests
{
Expand All @@ -17,6 +17,7 @@ public class CreateTokenTests
[GlobalSetup]
public void Setup()
{
DateTime now = DateTime.UtcNow;
_jsonWebTokenHandler = new JsonWebTokenHandler();
_tokenDescriptor = new SecurityTokenDescriptor
{
Expand Down
1,383 changes: 1,383 additions & 0 deletions src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs

Large diffs are not rendered by default.

958 changes: 2 additions & 956 deletions src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs

Large diffs are not rendered by default.

82 changes: 78 additions & 4 deletions src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.IdentityModel.Abstractions;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Tokens.Json;

using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages;

Expand Down Expand Up @@ -47,7 +45,7 @@ public partial class JwtTokenUtilities
private static Regex CreateJweRegex() => new Regex(JwtConstants.JweCompactSerializationRegex, RegexOptions.Compiled | RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(_regexMatchTimeoutMilliseconds));
#endif

internal static IList<string> DefaultHeaderParameters = new List<string>()
internal static List<string> DefaultHeaderParameters = new List<string>()
{
JwtHeaderParameterNames.Alg,
JwtHeaderParameterNames.Kid,
Expand Down Expand Up @@ -121,6 +119,82 @@ public static string CreateEncodedSignature(string input, SigningCredentials sig
}
}

internal static byte[] CreateEncodedSignature(
byte[] input,
int offset,
int count,
SigningCredentials signingCredentials)
{
if (input == null)
throw LogHelper.LogArgumentNullException(nameof(input));

if (signingCredentials == null)
return null;

var cryptoProviderFactory = signingCredentials.CryptoProviderFactory ?? signingCredentials.Key.CryptoProviderFactory;
var signatureProvider = cryptoProviderFactory.CreateForSigning(signingCredentials.Key, signingCredentials.Algorithm) ??
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(
TokenLogMessages.IDX10637,
signingCredentials.Key == null ? "Null" : signingCredentials.Key.ToString(),
LogHelper.MarkAsNonPII(signingCredentials.Algorithm))));

try
{
if (LogHelper.IsEnabled(EventLogLevel.Verbose))
LogHelper.LogVerbose(LogMessages.IDX14200);

return signatureProvider.Sign(input, offset, count);
}
finally
{
cryptoProviderFactory.ReleaseSignatureProvider(signatureProvider);
}
}

#if NET6_0_OR_GREATER
/// <summary>
/// Produces a signature over the <paramref name="data"/>.
/// </summary>
/// <param name="data">Span containing bytes to be signed.</param>
/// <param name="destination">destination for signature.</param>
/// <param name="signingCredentials">The <see cref="SigningCredentials"/> that contain crypto specs used to sign the token.</param>
/// <param name="bytesWritten"></param>
/// <returns>The size of the signature.</returns>
/// <exception cref="ArgumentNullException">'input' or 'signingCredentials' is null.</exception>
internal static bool CreateSignature(
ReadOnlySpan<byte> data,
Span<byte> destination,
SigningCredentials signingCredentials,
out int bytesWritten)
{
bytesWritten = 0;
if (signingCredentials == null)
return false;

var cryptoProviderFactory = signingCredentials.CryptoProviderFactory ?? signingCredentials.Key.CryptoProviderFactory;
var signatureProvider = cryptoProviderFactory.CreateForSigning(signingCredentials.Key, signingCredentials.Algorithm) ??
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(
TokenLogMessages.IDX10637, signingCredentials.Key == null ? "Null" : signingCredentials.Key.ToString(),
LogHelper.MarkAsNonPII(signingCredentials.Algorithm))));

try
{
if (LogHelper.IsEnabled(EventLogLevel.Verbose))
LogHelper.LogVerbose(LogMessages.IDX14200);

return signatureProvider.Sign(data, destination, out bytesWritten);
}
finally
{
cryptoProviderFactory.ReleaseSignatureProvider(signatureProvider);
}
}
#endif

/// <summary>
/// Decompress JWT token bytes.
/// </summary>
Expand Down Expand Up @@ -419,7 +493,7 @@ internal static string SafeLogJwtToken(object obj)
// not a string, we do not know how to sanitize so we return a String which represents the object instance
if (!(obj is string token))
return obj.GetType().ToString();

int lastDot = token.LastIndexOf(".");

// no dots, not a JWT, we do not know how to sanitize so we return UnrecognizedEncodedToken
Expand Down
Loading

0 comments on commit f40dd90

Please sign in to comment.