Skip to content
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

Regression tests: Algorithm #2934

Merged
merged 4 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,29 @@ private static ValidationResult<SecurityKey> ValidateSignatureWithKey(
callContext);

if (!result.IsValid)
return new ValidationError(
new MessageDetail(
TokenLogMessages.IDX10518,
result.UnwrapError().MessageDetail.Message),
ValidationFailureType.SignatureAlgorithmValidationFailed,
typeof(SecurityTokenInvalidAlgorithmException),
new StackFrame(true));
{
if (result.UnwrapError() is AlgorithmValidationError algorithmValidationError)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious as to why we need to recreate the AlgorithmValidationError?

{
return new AlgorithmValidationError(
new MessageDetail(
TokenLogMessages.IDX10518,
algorithmValidationError.MessageDetail.Message),
typeof(SecurityTokenInvalidAlgorithmException),
new StackFrame(true),
algorithmValidationError.InvalidAlgorithm);
}
else
{
// overridden delegate did not return an AlgorithmValidationError
kllysng marked this conversation as resolved.
Show resolved Hide resolved
return new ValidationError(
new MessageDetail(
TokenLogMessages.IDX10518,
result.UnwrapError().MessageDetail.Message),
ValidationFailureType.SignatureAlgorithmValidationFailed,
typeof(SecurityTokenInvalidAlgorithmException),
new StackFrame(true));
}
}

SignatureProvider signatureProvider = cryptoProviderFactory.CreateForVerifying(key, jsonWebToken.Alg);
try
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10002 = "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10268 = "IDX10268: Unable to validate audience, validationParameters.ValidAudiences.Count == 0." -> string
Microsoft.IdentityModel.Tokens.AlgorithmValidationError
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.AlgorithmValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidAlgorithm) -> void
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.InvalidAlgorithm.get -> string
Microsoft.IdentityModel.Tokens.AlgorithmValidationError._invalidAlgorithm -> string
Microsoft.IdentityModel.Tokens.AudienceValidationError.AudienceValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Collections.Generic.IList<string> tokenAudiences, System.Collections.Generic.IList<string> validAudiences) -> void
Microsoft.IdentityModel.Tokens.AudienceValidationError.TokenAudiences.get -> System.Collections.Generic.IList<string>
Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedConfiguration = 1 -> Microsoft.IdentityModel.Tokens.IssuerValidationSource
Expand All @@ -12,6 +16,7 @@ Microsoft.IdentityModel.Tokens.ValidationError.GetException(System.Type exceptio
Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.Error.get -> Microsoft.IdentityModel.Tokens.ValidationError
Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.IsValid.get -> bool
Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.Result.get -> TResult
override Microsoft.IdentityModel.Tokens.AlgorithmValidationError.GetException() -> System.Exception
static Microsoft.IdentityModel.Tokens.AudienceValidationError.AudiencesCountZero -> System.Diagnostics.StackFrame
static Microsoft.IdentityModel.Tokens.AudienceValidationError.AudiencesNull -> System.Diagnostics.StackFrame
static Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidateAudienceFailed -> System.Diagnostics.StackFrame
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Diagnostics;

#nullable enable
namespace Microsoft.IdentityModel.Tokens
{
internal class AlgorithmValidationError : ValidationError
{
protected string? _invalidAlgorithm;

public AlgorithmValidationError(
MessageDetail messageDetail,
Type exceptionType,
StackFrame stackFrame,
string? invalidAlgorithm) :
base(messageDetail, ValidationFailureType.AlgorithmValidationFailed, exceptionType, stackFrame)
{
_invalidAlgorithm = invalidAlgorithm;
}

internal override Exception GetException()
{
if (ExceptionType == typeof(SecurityTokenInvalidAlgorithmException))
{
SecurityTokenInvalidAlgorithmException exception = new(MessageDetail.Message, InnerException)
{
InvalidAlgorithm = _invalidAlgorithm
};

return exception;
}

return base.GetException();
}

internal string? InvalidAlgorithm => _invalidAlgorithm;
}
}
#nullable restore
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ internal static ValidationResult<string> ValidateAlgorithm(
if (validationParameters.ValidAlgorithms != null &&
validationParameters.ValidAlgorithms.Count > 0 &&
!validationParameters.ValidAlgorithms.Contains(algorithm, StringComparer.Ordinal))
return new ValidationError(
return new AlgorithmValidationError(
new MessageDetail(
LogMessages.IDX10696,
LogHelper.MarkAsNonPII(algorithm)),
ValidationFailureType.AlgorithmValidationFailed,
typeof(SecurityTokenInvalidAlgorithmException),
new StackFrame(true));
new StackFrame(true),
algorithm);

return algorithm;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#nullable enable
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.IdentityModel.TestUtils;
using Microsoft.IdentityModel.Tokens;
using Xunit;

namespace Microsoft.IdentityModel.JsonWebTokens.Tests
{
public partial class JsonWebTokenHandlerValidateTokenAsyncTests
{
[Theory, MemberData(nameof(ValidateTokenAsync_AlgorithmTestCases), DisableDiscoveryEnumeration = true)]
public async Task ValidateTokenAsync_Algorithm(ValidateTokenAsyncAlgorithmTheoryData theoryData)
{
var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_Algorithm", theoryData);

string jwtString = CreateTokenWithSigningCredentials(theoryData.SigningCredentials);

await ValidateAndCompareResults(jwtString, theoryData, context);

TestUtilities.AssertFailIfErrors(context);
}

public static TheoryData<ValidateTokenAsyncAlgorithmTheoryData> ValidateTokenAsync_AlgorithmTestCases
{
get
{
var theoryData = new TheoryData<ValidateTokenAsyncAlgorithmTheoryData>();

theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Valid_AlgorithmIsValid")
{
SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2,
TokenValidationParameters = CreateTokenValidationParameters(
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key,
validAlgorithms: [SecurityAlgorithms.RsaSha256Signature]),
ValidationParameters = CreateValidationParameters(
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key,
validAlgorithms: [SecurityAlgorithms.RsaSha256Signature]),
});

theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Valid_ValidAlgorithmsIsNull")
{
SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2,
TokenValidationParameters = CreateTokenValidationParameters(
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key,
validAlgorithms: null),
ValidationParameters = CreateValidationParameters(
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key,
validAlgorithms: null),
});

theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Valid_ValidAlgorithmsIsEmptyList")
{
SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2,
TokenValidationParameters = CreateTokenValidationParameters(
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, validAlgorithms: []),
ValidationParameters = CreateValidationParameters(
KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key, validAlgorithms: []),
});

theoryData.Add(new ValidateTokenAsyncAlgorithmTheoryData("Invalid_TokenIsSignedWithAnInvalidAlgorithm")
iNinja marked this conversation as resolved.
Show resolved Hide resolved
{
// Token is signed with HmacSha256 but only sha256 is considered valid for this test's purposes
SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2,
TokenValidationParameters = CreateTokenValidationParameters(
KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
validAlgorithms: [SecurityAlgorithms.Sha256]),
ValidationParameters = CreateValidationParameters(
KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
validAlgorithms: [SecurityAlgorithms.Sha256]),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10511:"),
ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidAlgorithmException(
"IDX10518:",
propertiesExpected: new() { { "InvalidAlgorithm", SecurityAlgorithms.HmacSha256Signature } }),
});

return theoryData;

static TokenValidationParameters CreateTokenValidationParameters(
SecurityKey? signingKey = null, List<string>? validAlgorithms = null)
{
// only validate the signature and algorithm
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateLifetime = false,
ValidateTokenReplay = false,
ValidateIssuerSigningKey = false,
RequireSignedTokens = true,
IssuerSigningKey = signingKey,
};

tokenValidationParameters.ValidAlgorithms = validAlgorithms;

return tokenValidationParameters;
}

static ValidationParameters CreateValidationParameters(
SecurityKey? signingKey = null, List<string>? validAlgorithms = null)
{
ValidationParameters validationParameters = new ValidationParameters();

if (signingKey is not null)
validationParameters.IssuerSigningKeys.Add(signingKey);

validationParameters.ValidAlgorithms = validAlgorithms;

// Skip all validations except signature and algorithm
validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation;
validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation;
validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation;
validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation;
validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation;
validationParameters.TypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;

return validationParameters;
}
}
}

public class ValidateTokenAsyncAlgorithmTheoryData : ValidateTokenAsyncBaseTheoryData
{
public ValidateTokenAsyncAlgorithmTheoryData(string testId) : base(testId) { }

public SigningCredentials? SigningCredentials { get; set; }
}
}
}
#nullable restore
Loading