Skip to content

Commit

Permalink
Enforce key sizes when creating HMAC
Browse files Browse the repository at this point in the history
  • Loading branch information
Brent Schmaltz authored and brentschmaltz committed May 8, 2023
1 parent 06165eb commit 008e2f7
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 30 deletions.
79 changes: 78 additions & 1 deletion src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -428,24 +428,101 @@ public virtual KeyedHashAlgorithm CreateKeyedHashAlgorithm(byte[] keyBytes, stri
if (CustomCryptoProvider != null && CustomCryptoProvider.IsSupportedAlgorithm(algorithm, keyBytes))
{
if (!(CustomCryptoProvider.Create(algorithm, keyBytes) is KeyedHashAlgorithm keyedHashAlgorithm))
throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX10647, LogHelper.MarkAsNonPII(algorithm), LogHelper.MarkAsNonPII(typeof(KeyedHashAlgorithm)))));
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(
LogMessages.IDX10647,
LogHelper.MarkAsNonPII(algorithm),
LogHelper.MarkAsNonPII(typeof(KeyedHashAlgorithm)))));

return keyedHashAlgorithm;
}

// In the case of Aes128CbcHmacSha256, Aes192CbcHmacSha384, Aes256CbcHmacSha512 which are Authenticated Encryption algorithms
// SymmetricSignatureProvider will get passed a key with 1/2 the minimum keysize expected size for the HashAlgorithm. 16 bytes for SHA256, instead of 32 bytes.
// see: https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.2.1
switch (algorithm)
{
case SecurityAlgorithms.Aes128CbcHmacSha256:
{
if (keyBytes.Length < 16)
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(LogMessages.IDX10720,
LogHelper.MarkAsNonPII(algorithm),
LogHelper.MarkAsNonPII("128"),
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));

return new HMACSHA256(keyBytes);
}

case SecurityAlgorithms.Aes192CbcHmacSha384:
{
if (keyBytes.Length < 24)
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(LogMessages.IDX10720,
LogHelper.MarkAsNonPII(algorithm),
LogHelper.MarkAsNonPII("192"),
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));

return new HMACSHA384(keyBytes);
}

case SecurityAlgorithms.Aes256CbcHmacSha512:
{
if (keyBytes.Length < 32)
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(LogMessages.IDX10720,
LogHelper.MarkAsNonPII(algorithm),
LogHelper.MarkAsNonPII("256"),
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));

return new HMACSHA512(keyBytes);
}

case SecurityAlgorithms.HmacSha256Signature:
case SecurityAlgorithms.HmacSha256:
{
if (keyBytes.Length < 32)
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(LogMessages.IDX10720,
LogHelper.MarkAsNonPII(algorithm),
LogHelper.MarkAsNonPII("256"),
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));

return new HMACSHA256(keyBytes);
}

case SecurityAlgorithms.HmacSha384Signature:
case SecurityAlgorithms.HmacSha384:
{
if (keyBytes.Length < 48)
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(LogMessages.IDX10720,
LogHelper.MarkAsNonPII(algorithm),
LogHelper.MarkAsNonPII("384"),
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));

return new HMACSHA384(keyBytes);
}

case SecurityAlgorithms.HmacSha512Signature:
case SecurityAlgorithms.HmacSha512:
{
if (keyBytes.Length < 64)
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(LogMessages.IDX10720,
LogHelper.MarkAsNonPII(algorithm),
LogHelper.MarkAsNonPII("512"),
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));

return new HMACSHA512(keyBytes);
}

default:
throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10666, LogHelper.MarkAsNonPII(algorithm))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ private struct AuthenticatedKeys
private CryptoProviderFactory _cryptoProviderFactory;
private bool _disposed;
private Lazy<bool> _keySizeIsValid;
private string _hmacAlgorithm;
private Lazy<SymmetricSignatureProvider> _symmetricSignatureProvider;
private DecryptionDelegate DecryptFunction;
private EncryptionDelegate EncryptFunction;
Expand Down Expand Up @@ -86,7 +85,6 @@ private void InitializeUsingAesGcm()
private void InitializeUsingAesCbc()
{
_authenticatedkeys = new Lazy<AuthenticatedKeys>(CreateAuthenticatedKeys);
_hmacAlgorithm = GetHmacAlgorithm(Algorithm);
_symmetricSignatureProvider = new Lazy<SymmetricSignatureProvider>(CreateSymmetricSignatureProvider);
EncryptFunction = EncryptWithAesCbc;
DecryptFunction = DecryptWithAesCbc;
Expand Down Expand Up @@ -208,9 +206,9 @@ internal SymmetricSignatureProvider CreateSymmetricSignatureProvider()
SymmetricSignatureProvider symmetricSignatureProvider;

if (Key.CryptoProviderFactory.GetType() == typeof(CryptoProviderFactory))
symmetricSignatureProvider = Key.CryptoProviderFactory.CreateForSigning(_authenticatedkeys.Value.HmacKey, _hmacAlgorithm, false) as SymmetricSignatureProvider;
symmetricSignatureProvider = Key.CryptoProviderFactory.CreateForSigning(_authenticatedkeys.Value.HmacKey, Algorithm, false) as SymmetricSignatureProvider;
else
symmetricSignatureProvider = Key.CryptoProviderFactory.CreateForSigning(_authenticatedkeys.Value.HmacKey, _hmacAlgorithm) as SymmetricSignatureProvider;
symmetricSignatureProvider = Key.CryptoProviderFactory.CreateForSigning(_authenticatedkeys.Value.HmacKey, Algorithm) as SymmetricSignatureProvider;

if (symmetricSignatureProvider == null)
throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10649, LogHelper.MarkAsNonPII(Algorithm))));
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.IdentityModel.Tokens/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ internal static class LogMessages
public const string IDX10717 = "IDX10717: '{0} + {1}' must not be greater than {2}, '{3} + {4} > {5}'.";
public const string IDX10718 = "IDX10718: AlgorithmToValidate is not supported: '{0}'. Algorithm '{1}'.";
public const string IDX10719 = "IDX10719: SignatureSize (in bytes) was expected to be '{0}', was '{1}'.";
public const string IDX10720 = "IDX10720: Unable to create KeyedHashAlgorithm for algorithm '{0}', the key size must be greater than: '{1}' bits, key has '{2}' bits.";

// Json specific errors
//public const string IDX10801 = "IDX10801:"
Expand Down
26 changes: 14 additions & 12 deletions test/Microsoft.IdentityModel.Tokens.Tests/ReferenceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.TestUtils;
using Microsoft.IdentityModel.Tokens;
using Xunit;
Expand Down Expand Up @@ -72,7 +73,8 @@ public void AesGcmReferenceTest()
[Theory, MemberData(nameof(AuthenticatedEncryptionTheoryData))]
public void AuthenticatedEncryptionReferenceTest(AuthenticationEncryptionTestParams testParams)
{
var context = new CompareContext();
var context = TestUtilities.WriteHeader("AuthenticatedEncryptionReferenceTest", testParams);

var providerForEncryption = CryptoProviderFactory.Default.CreateAuthenticatedEncryptionProvider(testParams.EncryptionKey, testParams.Algorithm);
var providerForDecryption = CryptoProviderFactory.Default.CreateAuthenticatedEncryptionProvider(testParams.DecryptionKey, testParams.Algorithm);
var plaintext = providerForDecryption.Decrypt(testParams.Ciphertext, testParams.AuthenticationData, testParams.IV, testParams.AuthenticationTag);
Expand All @@ -99,7 +101,7 @@ public static TheoryData<AuthenticationEncryptionTestParams> AuthenticatedEncryp
{
var theoryData = new TheoryData<AuthenticationEncryptionTestParams>();

theoryData.Add(new AuthenticationEncryptionTestParams
theoryData.Add(new AuthenticationEncryptionTestParams("AES_128_CBC_HMAC_SHA_256")
{
Algorithm = AES_128_CBC_HMAC_SHA_256.Algorithm,
AuthenticationData = AES_128_CBC_HMAC_SHA_256.A,
Expand All @@ -108,11 +110,10 @@ public static TheoryData<AuthenticationEncryptionTestParams> AuthenticatedEncryp
DecryptionKey = new SymmetricSecurityKey(AES_128_CBC_HMAC_SHA_256.K) { KeyId = "DecryptionKey.AES_128_CBC_HMAC_SHA_256.K" },
EncryptionKey = new SymmetricSecurityKey(AES_128_CBC_HMAC_SHA_256.K) { KeyId = "EncryptionKey.AES_128_CBC_HMAC_SHA_256.K" },
IV = AES_128_CBC_HMAC_SHA_256.IV,
Plaintext = AES_128_CBC_HMAC_SHA_256.P,
TestId = "AES_128_CBC_HMAC_SHA_256"
Plaintext = AES_128_CBC_HMAC_SHA_256.P
});

theoryData.Add(new AuthenticationEncryptionTestParams
theoryData.Add(new AuthenticationEncryptionTestParams("AES_192_CBC_HMAC_SHA_384")
{
Algorithm = AES_192_CBC_HMAC_SHA_384.Algorithm,
AuthenticationData = AES_192_CBC_HMAC_SHA_384.A,
Expand All @@ -121,11 +122,10 @@ public static TheoryData<AuthenticationEncryptionTestParams> AuthenticatedEncryp
DecryptionKey = new SymmetricSecurityKey(AES_192_CBC_HMAC_SHA_384.K) { KeyId = "DecryptionKey.AES_192_CBC_HMAC_SHA_384.K" },
EncryptionKey = new SymmetricSecurityKey(AES_192_CBC_HMAC_SHA_384.K) { KeyId = "EncryptionKey.AES_192_CBC_HMAC_SHA_384.K" },
IV = AES_192_CBC_HMAC_SHA_384.IV,
Plaintext = AES_192_CBC_HMAC_SHA_384.P,
TestId = "AES_192_CBC_HMAC_SHA_384"
Plaintext = AES_192_CBC_HMAC_SHA_384.P
});

theoryData.Add(new AuthenticationEncryptionTestParams
theoryData.Add(new AuthenticationEncryptionTestParams("AES_256_CBC_HMAC_SHA_512")
{
Algorithm = AES_256_CBC_HMAC_SHA_512.Algorithm,
AuthenticationData = AES_256_CBC_HMAC_SHA_512.A,
Expand All @@ -134,16 +134,19 @@ public static TheoryData<AuthenticationEncryptionTestParams> AuthenticatedEncryp
DecryptionKey = new SymmetricSecurityKey(AES_256_CBC_HMAC_SHA_512.K) { KeyId = "DecryptionKey.AES_256_CBC_HMAC_SHA_512.K" },
EncryptionKey = new SymmetricSecurityKey(AES_256_CBC_HMAC_SHA_512.K) { KeyId = "EncryptionKey.AES_256_CBC_HMAC_SHA_512.K" },
IV = AES_256_CBC_HMAC_SHA_512.IV,
Plaintext = AES_256_CBC_HMAC_SHA_512.P,
TestId = "AES_256_CBC_HMAC_SHA_512"
Plaintext = AES_256_CBC_HMAC_SHA_512.P
});

return theoryData;
}
}

public class AuthenticationEncryptionTestParams
public class AuthenticationEncryptionTestParams : TheoryDataBase
{
public AuthenticationEncryptionTestParams() { }

public AuthenticationEncryptionTestParams(string testId) : base(testId) { }

public string Algorithm { get; set; }
public byte[] AuthenticationData { get; set; }
public byte[] AuthenticationTag { get; set; }
Expand All @@ -152,7 +155,6 @@ public class AuthenticationEncryptionTestParams
public SecurityKey EncryptionKey { get; set; }
public byte[] IV { get; set; }
public byte[] Plaintext { get; set; }
public string TestId { get; set; }

public override string ToString()
{
Expand Down
Loading

0 comments on commit 008e2f7

Please sign in to comment.