-
Notifications
You must be signed in to change notification settings - Fork 401
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Verify authentication tag length (#2569)
* initial changes to verify auth tag length + app context switch + unit test * use original tag length * test passes * add app context switch test * update tag lengths to size in bytes, implement tests * replace previous auth tag length check * address comments * add gcm test case * update test case name * update log message w/ aka.ms link, add in line that was removed * move curly brace
- Loading branch information
1 parent
d353b5a
commit d51c2ad
Showing
3 changed files
with
169 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,6 @@ | |
using System.IdentityModel.Tokens.Jwt.Tests; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Runtime.InteropServices; | ||
using System.Security.Claims; | ||
using System.Security.Cryptography; | ||
|
@@ -86,9 +85,9 @@ public void Base64UrlEncodedUnsignedJwtHeader() | |
public void CreateTokenThrowsNullArgumentException() | ||
{ | ||
var handler = new JsonWebTokenHandler(); | ||
Assert.Throws<ArgumentNullException>(() => handler.CreateToken(null, Default.SymmetricEncryptingCredentials, new Dictionary<string, object> { {"key", "value" } })); | ||
Assert.Throws<ArgumentNullException>(() => handler.CreateToken("Payload", (EncryptingCredentials) null, new Dictionary<string, object> { { "key", "value" } })); | ||
Assert.Throws<ArgumentNullException>(() => handler.CreateToken("Payload", Default.SymmetricEncryptingCredentials, (Dictionary<string, object>) null)); | ||
Assert.Throws<ArgumentNullException>(() => handler.CreateToken(null, Default.SymmetricEncryptingCredentials, new Dictionary<string, object> { { "key", "value" } })); | ||
Assert.Throws<ArgumentNullException>(() => handler.CreateToken("Payload", (EncryptingCredentials)null, new Dictionary<string, object> { { "key", "value" } })); | ||
Assert.Throws<ArgumentNullException>(() => handler.CreateToken("Payload", Default.SymmetricEncryptingCredentials, (Dictionary<string, object>)null)); | ||
} | ||
|
||
[Theory, MemberData(nameof(TokenValidationClaimsTheoryData))] | ||
|
@@ -276,7 +275,7 @@ public static TheoryData<JwtTheoryData> SegmentTheoryData() | |
theoryData); | ||
|
||
|
||
JwtTestData.InvalidEncodedSegmentsData("", theoryData); | ||
JwtTestData.InvalidEncodedSegmentsData("", theoryData); | ||
JwtTestData.ValidEncodedSegmentsData(theoryData); | ||
|
||
return theoryData; | ||
|
@@ -476,7 +475,7 @@ public static TheoryData<CreateTokenTheoryData> CreateJWEWithAesGcmTheoryData | |
|
||
tokenHandler.InboundClaimTypeMap.Clear(); | ||
var encryptionCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm128; | ||
encryptionCredentials.CryptoProviderFactory = new CryptoProviderFactoryMock(); | ||
encryptionCredentials.CryptoProviderFactory = new CryptoProviderFactoryForGcm(); | ||
return new TheoryData<CreateTokenTheoryData> | ||
{ | ||
new CreateTokenTheoryData | ||
|
@@ -2824,7 +2823,7 @@ public static TheoryData<CreateTokenTheoryData> RoundTripJWEKeyWrapTestCases | |
|
||
// Test checks to make sure that default times are correctly added to the token | ||
// upon token creation. | ||
[Fact (Skip = "Rewrite test to use claims, string will not succeed")] | ||
[Fact(Skip = "Rewrite test to use claims, string will not succeed")] | ||
public void SetDefaultTimesOnTokenCreation() | ||
{ | ||
// when the payload is passed as a string to JsonWebTokenHandler.CreateToken, we no longer | ||
|
@@ -2980,9 +2979,9 @@ public async Task ValidateJsonWebTokenClaimMapping() | |
} | ||
}; | ||
|
||
if(jsonValidationResult.IsValid && jwtValidationResult.IsValid) | ||
if (jsonValidationResult.IsValid && jwtValidationResult.IsValid) | ||
{ | ||
if(!IdentityComparer.AreEqual(jsonValidationResult, jwtValidationResult, context)) | ||
if (!IdentityComparer.AreEqual(jsonValidationResult, jwtValidationResult, context)) | ||
{ | ||
context.AddDiff("jsonValidationResult.IsValid && jwtValidationResult.IsValid, Validation results are not equal"); | ||
} | ||
|
@@ -3216,7 +3215,7 @@ public void ValidateJWS(JwtTheoryData theoryData) | |
try | ||
{ | ||
var handler = new JsonWebTokenHandler(); | ||
var validationResult =handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; | ||
var validationResult = handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; | ||
if (validationResult.Exception != null) | ||
{ | ||
if (validationResult.IsValid) | ||
|
@@ -3568,7 +3567,7 @@ public void ValidateJWSWithLastKnownGood(JwtTheoryData theoryData) | |
var setupValidationResult = handler.ValidateTokenAsync(theoryData.Token, theoryData.ValidationParameters).Result; | ||
|
||
theoryData.ValidationParameters.ValidateWithLKG = previousValidateWithLKG; | ||
|
||
if (setupValidationResult.Exception != null) | ||
{ | ||
if (setupValidationResult.IsValid) | ||
|
@@ -4189,6 +4188,143 @@ public static TheoryData<CreateTokenTheoryData> IncludeSecurityTokenOnFailureTes | |
}, | ||
}; | ||
} | ||
|
||
[Theory, MemberData(nameof(ValidateAuthenticationTagLengthTheoryData))] | ||
public void ValidateTokenAsync_ModifiedAuthNTag(CreateTokenTheoryData theoryData) | ||
{ | ||
// arrange | ||
AppContext.SetSwitch(AuthenticatedEncryptionProvider._skipValidationOfAuthenticationTagLength, theoryData.EnableAppContextSwitch); | ||
var payload = new JObject() | ||
{ | ||
{ JwtRegisteredClaimNames.Email, "[email protected]" }, | ||
{ JwtRegisteredClaimNames.GivenName, "Bob" }, | ||
{ JwtRegisteredClaimNames.Iss, "http://Default.Issuer.com"}, | ||
{ JwtRegisteredClaimNames.Aud, "http://Default.Audience.com" }, | ||
{ JwtRegisteredClaimNames.Iat, EpochTime.GetIntDate(DateTime.Now).ToString() }, | ||
{ JwtRegisteredClaimNames.Nbf, EpochTime.GetIntDate(DateTime.Now).ToString() }, | ||
{ JwtRegisteredClaimNames.Exp, EpochTime.GetIntDate(DateTime.Now.AddDays(1)).ToString() }, | ||
}.ToString(); | ||
|
||
var jsonWebTokenHandler = new JsonWebTokenHandler(); | ||
var signingCredentials = Default.SymmetricSigningCredentials; | ||
|
||
if (SupportedAlgorithms.IsAesGcm(theoryData.Algorithm)) | ||
{ | ||
theoryData.EncryptingCredentials.CryptoProviderFactory = new CryptoProviderFactoryForGcm(); | ||
} | ||
|
||
var jwe = jsonWebTokenHandler.CreateToken(payload, signingCredentials, theoryData.EncryptingCredentials); | ||
var jweWithExtraCharacters = jwe + "_cannoli_hunts_truffles_"; | ||
|
||
// act | ||
// calling ValidateTokenAsync.Result to prevent tests from sharing app context switch property | ||
// normally, we would want to await ValidateTokenAsync().ConfigureAwait(false) | ||
var tokenValidationResult = jsonWebTokenHandler.ValidateTokenAsync(jweWithExtraCharacters, theoryData.ValidationParameters).Result; | ||
|
||
// assert | ||
Assert.Equal(theoryData.IsValid, tokenValidationResult.IsValid); | ||
} | ||
|
||
public static TheoryData<CreateTokenTheoryData> ValidateAuthenticationTagLengthTheoryData() | ||
{ | ||
var signingCredentials512 = new SigningCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Sha512); | ||
return new TheoryData<CreateTokenTheoryData>() | ||
{ | ||
new("Aes256Gcm_IsNotValidByDefault") | ||
{ | ||
Algorithm = SecurityAlgorithms.Aes256Gcm, | ||
EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm256, | ||
ValidationParameters = new TokenValidationParameters | ||
{ | ||
TokenDecryptionKey = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key, | ||
IssuerSigningKey = Default.SymmetricSigningKey256, | ||
ValidAudience = "http://Default.Audience.com", | ||
ValidIssuer = "http://Default.Issuer.com", | ||
}, | ||
IsValid = false | ||
}, | ||
new("A128CBC-HS256_IsNotValidByDefault") | ||
{ | ||
Algorithm = SecurityAlgorithms.Aes128CbcHmacSha256, | ||
EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), | ||
ValidationParameters = new TokenValidationParameters | ||
{ | ||
TokenDecryptionKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, | ||
IssuerSigningKey = Default.SymmetricSigningKey256, | ||
ValidAudience = "http://Default.Audience.com", | ||
ValidIssuer = "http://Default.Issuer.com", | ||
}, | ||
IsValid = false | ||
}, | ||
new("A192CBC-HS384_IsNotValidByDefault") | ||
{ | ||
Algorithm = SecurityAlgorithms.Aes192CbcHmacSha384, | ||
EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes192CbcHmacSha384), | ||
ValidationParameters = new TokenValidationParameters | ||
{ | ||
TokenDecryptionKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, | ||
IssuerSigningKey = Default.SymmetricSigningKey256, | ||
ValidAudience = "http://Default.Audience.com", | ||
ValidIssuer = "http://Default.Issuer.com", | ||
}, | ||
IsValid = false | ||
}, | ||
new("A256CBC-HS512_IsNotValidByDefault") | ||
{ | ||
Algorithm = SecurityAlgorithms.Aes256CbcHmacSha512, | ||
EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes256CbcHmacSha512), | ||
ValidationParameters = new TokenValidationParameters | ||
{ | ||
TokenDecryptionKey = signingCredentials512.Key, | ||
IssuerSigningKey = Default.SymmetricSigningKey256, | ||
ValidAudience = "http://Default.Audience.com", | ||
ValidIssuer = "http://Default.Issuer.com", | ||
}, | ||
IsValid = false | ||
}, | ||
new("A128CBC-HS256_SkipTagLengthValidationAppContextSwitchOn_IsValid") | ||
{ | ||
EnableAppContextSwitch = true, | ||
Algorithm = SecurityAlgorithms.Aes128CbcHmacSha256, | ||
EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), | ||
ValidationParameters = new TokenValidationParameters | ||
{ | ||
TokenDecryptionKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, | ||
IssuerSigningKey = Default.SymmetricSigningKey256, | ||
ValidAudience = "http://Default.Audience.com", | ||
ValidIssuer = "http://Default.Issuer.com", | ||
}, | ||
IsValid = true | ||
}, | ||
new("A192CBC-HS384_SkipTagLengthValidationAppContextSwitchOn_IsValid") | ||
{ | ||
EnableAppContextSwitch = true, | ||
Algorithm = SecurityAlgorithms.Aes192CbcHmacSha384, | ||
EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes192CbcHmacSha384), | ||
ValidationParameters = new TokenValidationParameters | ||
{ | ||
TokenDecryptionKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, | ||
IssuerSigningKey = Default.SymmetricSigningKey256, | ||
ValidAudience = "http://Default.Audience.com", | ||
ValidIssuer = "http://Default.Issuer.com", | ||
}, | ||
IsValid = true | ||
}, | ||
new("A256CBC-HS512_SkipTagLengthValidationAppContextSwitchOn_IsValid") | ||
{ | ||
EnableAppContextSwitch = true, | ||
EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes256CbcHmacSha512), | ||
ValidationParameters = new TokenValidationParameters | ||
{ | ||
TokenDecryptionKey = signingCredentials512.Key, | ||
IssuerSigningKey = Default.SymmetricSigningKey256, | ||
ValidAudience = "http://Default.Audience.com", | ||
ValidIssuer = "http://Default.Issuer.com", | ||
}, | ||
IsValid = true | ||
} | ||
}; | ||
} | ||
} | ||
|
||
public class CreateTokenTheoryData : TheoryDataBase | ||
|
@@ -4234,24 +4370,26 @@ public CreateTokenTheoryData(string testId) : base(testId) | |
public IEnumerable<SecurityKey> ExpectedDecryptionKeys { get; set; } | ||
|
||
public Dictionary<string, object> ExpectedClaims { get; set; } | ||
|
||
public bool EnableAppContextSwitch { get; set; } = false; | ||
} | ||
|
||
// Overrides CryptoProviderFactory.CreateAuthenticatedEncryptionProvider to create AuthenticatedEncryptionProviderMock that provides AesGcm encryption. | ||
public class CryptoProviderFactoryMock: CryptoProviderFactory | ||
public class CryptoProviderFactoryForGcm : CryptoProviderFactory | ||
{ | ||
public override AuthenticatedEncryptionProvider CreateAuthenticatedEncryptionProvider(SecurityKey key, string algorithm) | ||
{ | ||
if (SupportedAlgorithms.IsSupportedEncryptionAlgorithm(algorithm, key) && SupportedAlgorithms.IsAesGcm(algorithm)) | ||
return new AuthenticatedEncryptionProviderMock(key, algorithm); | ||
return new AuthenticatedEncryptionProviderForGcm(key, algorithm); | ||
|
||
return null; | ||
} | ||
} | ||
|
||
// Overrides AuthenticatedEncryptionProvider.Encrypt to offer AesGcm encryption for testing. | ||
public class AuthenticatedEncryptionProviderMock: AuthenticatedEncryptionProvider | ||
public class AuthenticatedEncryptionProviderForGcm : AuthenticatedEncryptionProvider | ||
{ | ||
public AuthenticatedEncryptionProviderMock(SecurityKey key, string algorithm): base(key, algorithm) | ||
public AuthenticatedEncryptionProviderForGcm(SecurityKey key, string algorithm) : base(key, algorithm) | ||
{ } | ||
|
||
public override AuthenticatedEncryptionResult Encrypt(byte[] plaintext, byte[] authenticatedData) | ||
|
@@ -4275,7 +4413,7 @@ public override AuthenticatedEncryptionResult Encrypt(byte[] plaintext, byte[] a | |
aes.Encrypt(iv, plaintext, ciphertext, authenticationTag, authenticatedData); | ||
} | ||
|
||
return new AuthenticatedEncryptionResult(Key, ciphertext, iv, authenticationTag); | ||
return new AuthenticatedEncryptionResult(Key, ciphertext, iv, authenticationTag); | ||
} | ||
} | ||
|
||
|